Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: added support for generics #5

Closed
wants to merge 61 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c01af26
initial implementation
i582 Mar 14, 2022
d373f2f
fixed situation when it was necessary to resolve a function during ty…
i582 Mar 14, 2022
1d35cff
added initial support for class generics
i582 Mar 15, 2022
df53c16
fixed type inference for shape with unresolved types
i582 Mar 15, 2022
19943d0
fixed type inference for non-template classes
i582 Mar 15, 2022
015d276
fixed NullPointerException when generic comment contains syntacticall…
i582 Mar 15, 2022
ebfa25c
added class TypeTestBase for type testing and added several tests for…
i582 Mar 15, 2022
3c0674a
fixed class-string comparison with other types
i582 Mar 15, 2022
6b749b7
added more tests
i582 Mar 15, 2022
f8220bc
wip
i582 Mar 27, 2022
26102d0
fixed tests
i582 Mar 27, 2022
87ce1f2
extended generic inspections for method and constructor calls and add…
i582 Mar 27, 2022
62b1369
added initial support for generic types bounds
i582 Mar 28, 2022
afa6442
removed old generic type provider
i582 Mar 28, 2022
9c274bc
added Map and Pair generic classes
i582 Mar 28, 2022
a248737
update
i582 Apr 22, 2022
cba4ab1
implementation of generics instantiation is redesigned through the in…
i582 May 23, 2022
48f300a
fixed tests
i582 May 23, 2022
afc47aa
update (wip)
i582 May 26, 2022
6887870
added initial support for default generic types
i582 May 26, 2022
c2b5be2
added AddExplicitInstantiationComment quick fix
i582 May 27, 2022
dec32ed
added support for static methods
i582 May 27, 2022
96c502f
reworked complex type resolution system, now all tests are green
i582 May 28, 2022
829cd14
added optimisation for non-generic symbols
i582 May 28, 2022
18b65d5
added more tests
i582 May 28, 2022
7c12652
added checks for generic type duplication with class types and added …
i582 May 28, 2022
b746f71
fixed bug with class-string
i582 May 28, 2022
8c9feab
added type inference for parameters when they have a generic type tha…
i582 May 28, 2022
c1140f7
wip
i582 May 29, 2022
707a475
update
i582 May 29, 2022
1dc21d7
added support for generic name in generic isntantiation list
i582 May 29, 2022
43a2bb5
refactor
i582 May 30, 2022
f78a639
refactor
i582 May 30, 2022
969e0f7
small refactor
i582 May 30, 2022
8511964
dropped kphp-template-class support
i582 May 30, 2022
b806f49
Merge branch 'master' into pmakhnev/generic
i582 May 30, 2022
2f846c8
added fields tests
i582 May 30, 2022
3d83f60
fixed @var tag
i582 May 30, 2022
96af2be
added tests for callback
i582 May 30, 2022
00d923a
now generic names in static fields are treated as a usual class inste…
i582 May 30, 2022
57f898a
small fix
i582 Jun 6, 2022
897f64d
update
i582 Jun 7, 2022
02dc6a7
update
i582 Jun 7, 2022
6fd074a
fixed stubs
i582 Jun 7, 2022
80500cf
small fixes
i582 Jun 7, 2022
77a4003
added type inference for a class methods parameters
i582 Jun 7, 2022
810956f
fixed type inference for tuples and shapes for simple cases
i582 Jun 8, 2022
3e2195e
fixed check for union extends class
i582 Jun 8, 2022
4e7bac8
added type inference from extends/default types for fields
i582 Jun 8, 2022
37bc52c
added check for generic instantiation count in PHPDoc
i582 Jun 9, 2022
2820bba
added highlight for @kphp-generic parts
i582 Jun 9, 2022
5f203d3
small rename
i582 Jun 9, 2022
7b6f053
added initial support for @kphp-inherit tag
i582 Jun 9, 2022
363a80e
small fixes
i582 Jun 9, 2022
25a39b8
added more checks and tests
i582 Jun 10, 2022
f7b998d
added initial support for class inherit
i582 Jun 10, 2022
cbc91b1
refactored
i582 Jun 12, 2022
37d5b3a
added generics T names settings for color scheme
i582 Jun 12, 2022
d967da2
fixed several KPHP tests
i582 Jun 12, 2022
80330d1
improved test bases
i582 Jun 12, 2022
9da0627
added support for depth inheritance
i582 Jun 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
i582 committed Mar 27, 2022
commit f8220bcb3b4e613daf94986f3373d28775c7b984
10 changes: 7 additions & 3 deletions src/main/kotlin/com/vk/kphpstorm/generics/GenericFunctionCall.kt
Original file line number Diff line number Diff line change
@@ -261,6 +261,9 @@ class ResolvingGenericFunctionCall(project: Project) : ResolvingGenericBase(proj

override fun unpackImpl(packedData: String): Boolean {
val firstSeparatorIndex = packedData.indexOf("@@")
if (firstSeparatorIndex == -1) {
return false
}
val functionName = packedData.substring(0, firstSeparatorIndex)

val remainingPackedData = packedData.substring(firstSeparatorIndex + "@@".length)
@@ -332,9 +335,9 @@ class ResolvingGenericMethodCall(project: Project) : ResolvingGenericBase(projec
// for IDE we return PhpType "A"|"A<T>", that's why
// A<A<T>> is resolved as "A"|"A<A/A<T>>", so if pipe — search for instantiation
val instantiation = when (parsed) {
is ExPhpTypePipe -> parsed.items.firstOrNull { it is ExPhpTypeTplInstantiation }
is ExPhpTypePipe -> parsed.items.firstOrNull { it is ExPhpTypeTplInstantiation }
is ExPhpTypeNullable -> parsed.inner
else -> parsed
else -> parsed
} as? ExPhpTypeTplInstantiation ?: return false

classGenericType = instantiation
@@ -399,7 +402,8 @@ class GenericFunctionCall(call: FunctionReference) {
val genericTs = mutableListOf<String>()
private val parameters = mutableListOf<Parameter>()
private val callArgs = call.parameters
private val argumentsTypes = callArgs.filterIsInstance<PhpTypedElement>().map { it.type.global(project).toExPhpType() }
private val argumentsTypes =
callArgs.filterIsInstance<PhpTypedElement>().map { it.type.global(project).toExPhpType() }

val explicitSpecsPsi = findInstantiationComment(call)

Original file line number Diff line number Diff line change
@@ -120,7 +120,14 @@ class TupleShapeTypeProvider : PhpTypeProvider4 {
* So, we decode indexKey, know a complete lhs type and call [ExPhpType.getSubkeyByIndex]
*/
override fun complete(incompleteTypeStr: String, project: Project): PhpType? {
if (!incompleteTypeStr.startsWith("#Й")) {
return null
}
val spacePos = incompleteTypeStr.indexOf(' ')
if (spacePos == -1) {
return null
}

val indexKey = incompleteTypeStr.substring(3, spacePos)
val lhsTypeStr = incompleteTypeStr.substring(spacePos + 1)
val wholeType = PhpType().add(lhsTypeStr).global(project)
2 changes: 0 additions & 2 deletions src/test/fixtures/generics/class-string.fixture.php
Original file line number Diff line number Diff line change
@@ -36,5 +36,3 @@ function get_children() {
$children2_array = filter_is_instance($base_array, Child2::class);
expr_type($children2_array, "\Classes\Child2[]");
}


4 changes: 2 additions & 2 deletions src/test/fixtures/generics/class_implicit.fixture.php
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ function nullable($arg) {

"Импортированный класс из пространства имен"; {
$a = mirror(nullable(new B()));
expr_type($a, "\Classes\B|null");
expr_type($a, "null|\Classes\B");
}

"Импортированный класс из пространства имен с алиасом"; {
@@ -139,7 +139,7 @@ function nullable($arg) {

"Импортированный класс из пространства имен с алиасом как у глобально класса + Глобальный класс с именем как у локального алиаса для другого класса"; {
$a = mirror(new GlobalD() ?? new \GlobalD());
expr_type($a, "\GlobalD|\Classes\D");
expr_type($a, "\Classes\D|\GlobalD");
}
}

12 changes: 6 additions & 6 deletions src/test/fixtures/generics/class_resolving.fixture.php
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ function mirror($arg) {

"Класс из пространства имен"; {
$a = mirror/*<?\Classes\A>*/(new \Classes\A());
expr_type($a, "null|\Classes\A");
expr_type($a, "\Classes\A|null");
}

"Импортированный класс из пространства имен"; {
@@ -102,24 +102,24 @@ function mirror($arg) {

"Импортированный класс из пространства имен с алиасом"; {
$a = mirror/*<?GlobalC>*/(new GlobalC);
expr_type($a, "null|\Classes\C");
expr_type($a, "\Classes\C|null");
}

"Импортированный класс из пространства имен с алиасом как у глобально класса"; {
$a = mirror/*<?GlobalD*/(new GlobalD());
expr_type($a, "null|\Classes\D");
expr_type($a, "\Classes\D|null");
}

"Глобальный класс с именем как у локального алиаса для другого класса"; {
$a = mirror/*<?\GlobalD*/(new \GlobalD());
expr_type($a, "null|\GlobalD");
expr_type($a, "\GlobalD|null");
}
}

"Union"; {
"Класс из глобального скоупа + Класс из пространства имен"; {
$a = mirror/*<GlobalA|\Classes\A>*/(new GlobalA());
expr_type($a, "\GlobalA|\Classes\A");
expr_type($a, "\Classes\A|\GlobalA");
}

"Импортированный класс из пространства имен + Импортированный класс из пространства имен с алиасом"; {
@@ -129,7 +129,7 @@ function mirror($arg) {

"Импортированный класс из пространства имен с алиасом как у глобально класса + Глобальный класс с именем как у локального алиаса для другого класса"; {
$a = mirror/*<GlobalD|\GlobalD*/(new GlobalD());
expr_type($a, "\GlobalD|\Classes\D");
expr_type($a, "\Classes\D|\GlobalD");
}
}

4 changes: 2 additions & 2 deletions src/test/fixtures/generics/primitives_explicit.fixture.php
Original file line number Diff line number Diff line change
@@ -55,8 +55,8 @@ function combine($a1, $a2) {

"Примитивные типы в комплексных"; {
$a = combine/*<string, tuple(int, string)>*/("", tuple(1, ""));
expr_type($a, "tuple(int,string)|string");
expr_type($a, "string|tuple(int,string)");

$a1 = combine/*<string[], shape(key1: int, key2: ?string)>*/([""], shape(["key1" => 1, "key2" => ""]));
expr_type($a1, "string[]|shape(key1:int,key2:?string)");
expr_type($a1, "shape(key1:int,key2:?string)|string[]");
}
8 changes: 4 additions & 4 deletions src/test/fixtures/generics/primitives_implicit.fixture.php
Original file line number Diff line number Diff line change
@@ -54,9 +54,9 @@ function combine($a1, $a2) {
}

"Примитивные типы в комплексных"; {
$a = combine("", tuple(1, ""));
expr_type($a, "tuple(int,string)|string");
// $a = combine("", tuple(1, true));
// expr_type($a, "string|tuple(int,bool)");

$a1 = combine([""], shape(["key1" => 1, "key2" => ""]));
expr_type($a1, "string[]|shape(key1:int,key2:string)");
// $a1 = combine([""], shape(["key1" => 1, "key2" => $a]));
// expr_type($a1, "string[]|shape(key1:int,key2:string)");
}
18 changes: 9 additions & 9 deletions src/test/fixtures/generics/simple_functions.fixture.php
Original file line number Diff line number Diff line change
@@ -81,21 +81,21 @@ function mirror($arg) {

"Nullable"; {
$a1 = mirror/*<?string>*/("hello");
expr_type($a1, "string|null");
expr_type($a1, "null|string");

$a2 = mirror/*<?int>*/(1);
expr_type($a2, "null|int");
expr_type($a2, "int|null");
}

"Union"; {
$a1 = mirror/*<int|string>*/("hello");
expr_type($a1, "int|string");

$a2 = mirror/*<float|bool>*/(1.5);
expr_type($a2, "float|bool");
expr_type($a2, "bool|float");

$a3 = mirror/*<float|bool>*/(1.5);
expr_type($a3, "float|bool");
expr_type($a3, "bool|float");
}

"tuple"; {
@@ -135,24 +135,24 @@ function mirror($arg) {
expr_type($a1, "\GlobalA|null");

$a2 = mirror/*<?\Classes\A>*/([new \Classes\A()]);
expr_type($a2, "null|\Classes\A");
expr_type($a2, "\Classes\A|null");

$a3 = mirror/*<?B>*/([new B()]);
expr_type($a3, "null|\Classes\B");
expr_type($a3, "\Classes\B|null");

$a4 = mirror/*<?GlobalC>*/([new GlobalC]);
expr_type($a4, "null|\Classes\C");
expr_type($a4, "\Classes\C|null");
}

"Union"; {
$a1 = mirror/*<\GlobalA|\Classes\A>*/("hello");
expr_type($a1, "\GlobalA|\Classes\A");
expr_type($a1, "\Classes\A|\GlobalA");

$a2 = mirror/*<B|GlobalC>*/(1.5);
expr_type($a2, "\Classes\B|\Classes\C");

$a3 = mirror/*<\Classes\A|\GlobalC>*/(1.5);
expr_type($a3, "\GlobalC|\Classes\A");
expr_type($a3, "\Classes\A|\GlobalC");
}

"tuple"; {
30 changes: 30 additions & 0 deletions src/test/fixtures/generics/tests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/** @noinspection FunctionUnnecessaryExplicitGenericInstantiationListInspection */
/** @noinspection PhpUndefinedFunctionInspection */
/** @noinspection PhpExpressionResultUnusedInspection */


/**
* @kphp-generic T
* @param T $arg
* @return T
*/
function nullable($arg) {
if (0) return null;
return $arg;
}

/**
* @kphp-generic T
* @param T $arg
* @return T
*/
function mirror($arg) {
return $arg;
}

$a1 = mirror([new GlobalA()]);
expr_type($a1, "\GlobalA[]");



Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import com.jetbrains.php.lang.psi.elements.PhpTypedElement
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression
import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor
import com.vk.kphpstorm.configuration.KphpStormConfiguration
import com.vk.kphpstorm.exphptype.ExPhpTypePipe
import com.vk.kphpstorm.exphptype.PsiToExPhpType
import com.vk.kphpstorm.helpers.toExPhpType

@@ -47,7 +48,13 @@ abstract class TypeTestBase : BasePlatformTestCase() {
val expectedTypePsi = call.parameters.last() as StringLiteralExpression
val expectedType = expectedTypePsi.contents
val type = expr.type.global(myFixture.project).toExPhpType()?.let { PsiToExPhpType.dropGenerics(it) }
val typeString = type.toString()

val sortedType = if (type is ExPhpTypePipe)
ExPhpTypePipe(type.items.sortedBy { it.toString() })
else type

val typeString = sortedType.toString().ifEmpty { "<empty>" }

// TODO: add location (line and test name)
check(typeString == expectedType) {
"Type mismatch. Expected: $expectedType, found: $typeString"