From 897f64dbc71b6d2025701a53deb1563c24948cfe Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:44:34 +0300 Subject: [PATCH] update - fixed type compatibility for a callable and a typed callable - fixed default types handling - improved handling for union instantiations for methods and fields - generic hint now contains only "" - fixed InstantiationParamsCount inspection --- .../kphpstorm/exphptype/ExPhpTypeCallable.kt | 1 + .../generics/GenericConstructorCall.kt | 12 ++- .../kphpstorm/generics/GenericMethodCall.kt | 6 +- .../com/vk/kphpstorm/generics/GenericUtil.kt | 13 ++++ .../vk/kphpstorm/generics/GenericsReifier.kt | 28 +++---- .../generics/ResolvingGenericBase.kt | 15 +++- .../generics/ResolvingGenericFieldFetch.kt | 23 +++--- .../generics/ResolvingGenericMethodCall.kt | 17 ++-- .../highlighting/hints/InlayHintsCollector.kt | 11 +-- .../inspections/KphpGenericsInspection.kt | 47 +++++++---- .../GenericMethodsTypeProvider.kt | 2 +- .../generics/general/fields/main.fixture.php | 19 +++++ .../reifier/context/return.fixture.php | 8 ++ .../reifier/default/generic.fixture.php | 77 +++++++++++++++++++ .../general/reifier/default/main.fixture.php | 9 ++- .../general/reifier/default/wrong.fixture.php | 2 +- .../generics/general/vector_use.fixture.php | 6 +- .../instantiation_args_mismatch.fixture.php | 33 +++++++- .../no_enough_information.fixture.php | 2 +- .../several_reified_types.fixture.php | 4 +- .../generics/general/GenericsGeneralTest.kt | 5 ++ 21 files changed, 262 insertions(+), 78 deletions(-) create mode 100644 src/test/fixtures/generics/general/reifier/default/generic.fixture.php diff --git a/src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeCallable.kt b/src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeCallable.kt index 0a0ded3..f999ea0 100644 --- a/src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeCallable.kt +++ b/src/main/kotlin/com/vk/kphpstorm/exphptype/ExPhpTypeCallable.kt @@ -29,6 +29,7 @@ class ExPhpTypeCallable(val argTypes: List, val returnType: ExPhpType is ExPhpTypeAny -> true // TODO: добавить полноценную проверку? is ExPhpTypeCallable -> true + is ExPhpTypePrimitive -> rhs == ExPhpType.CALLABLE is ExPhpTypeNullable -> isAssignableFrom(rhs.inner, project) else -> false } diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/GenericConstructorCall.kt b/src/main/kotlin/com/vk/kphpstorm/generics/GenericConstructorCall.kt index 7649238..b34992f 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/GenericConstructorCall.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/GenericConstructorCall.kt @@ -39,18 +39,16 @@ class GenericConstructorCall(private val call: NewExpression) : GenericCall(call override fun isResolved() = method != null && klass != null override fun genericNames(): List { - val methodsNames = method?.genericNames() ?: emptyList() - val classesNames = klass?.genericNames() ?: emptyList() + val methodNames = method?.genericNames() ?: emptyList() + val classNames = klass?.genericNames() ?: emptyList() return mutableListOf() - .apply { addAll(methodsNames) } - .apply { addAll(classesNames) } + .apply { addAll(methodNames) } + .apply { addAll(classNames) } .toList() } - override fun ownGenericNames(): List { - return klass?.genericNames() ?: emptyList() - } + override fun ownGenericNames() = genericNames() override fun isGeneric() = genericNames().isNotEmpty() diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/GenericMethodCall.kt b/src/main/kotlin/com/vk/kphpstorm/generics/GenericMethodCall.kt index 2caba5b..2fdcc0e 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/GenericMethodCall.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/GenericMethodCall.kt @@ -7,7 +7,7 @@ import com.jetbrains.php.lang.psi.elements.PhpTypedElement import com.jetbrains.php.lang.psi.resolve.types.PhpType import com.vk.kphpstorm.exphptype.ExPhpType import com.vk.kphpstorm.generics.GenericUtil.genericNames -import com.vk.kphpstorm.generics.GenericUtil.getInstantiation +import com.vk.kphpstorm.generics.GenericUtil.getInstantiations import com.vk.kphpstorm.helpers.toExPhpType import com.vk.kphpstorm.kphptags.psi.KphpDocGenericParameterDecl import kotlin.math.min @@ -27,7 +27,9 @@ class GenericMethodCall(private val call: MethodReference) : GenericCall(call.pr val classType = PhpType().add(callType).global(project) val parsed = classType.toExPhpType() - val instantiation = parsed?.getInstantiation() + val instantiation = parsed?.getInstantiations()?.firstOrNull { + it.classFqn == klass?.fqn + } if (instantiation != null) { val specialization = instantiation.specializationList diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/GenericUtil.kt b/src/main/kotlin/com/vk/kphpstorm/generics/GenericUtil.kt index c089f57..205a631 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/GenericUtil.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/GenericUtil.kt @@ -118,6 +118,19 @@ object GenericUtil { } as? ExPhpTypeTplInstantiation } + fun ExPhpType.getInstantiations(): List { + if (this is ExPhpTypePipe) { + return items.filterIsInstance() + } + val inner = when (this) { + is ExPhpTypeNullable -> inner + is ExPhpTypeForcing -> inner + else -> this + } as? ExPhpTypeTplInstantiation ?: return emptyList() + + return listOf(inner) + } + fun findInstantiationComment(el: PsiElement): GenericInstantiationPsiCommentImpl? { if (el is Field) return null diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/GenericsReifier.kt b/src/main/kotlin/com/vk/kphpstorm/generics/GenericsReifier.kt index e064965..f796849 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/GenericsReifier.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/GenericsReifier.kt @@ -48,21 +48,15 @@ class GenericsReifier(val project: Project) { reifyArgumentGenericsT(argExType, paramExType) } - var instantiation = contextType?.getInstantiation() + // TODO: А что если тут несколько инстанциаций в типе? + val instantiation = contextType?.getInstantiation() if (instantiation != null) { - // TODO: сделать верный вывод типов тут - if (instantiation.classFqn == klass?.fqn) { - instantiation = instantiation.specializationList.firstOrNull() as? ExPhpTypeTplInstantiation - } - - if (instantiation != null) { - val specList = instantiation.specializationList - for (i in 0 until min(specList.size, genericTs.size)) { - val type = specList[i] - val genericT = genericTs[i] + val specList = instantiation.specializationList + for (i in 0 until min(specList.size, genericTs.size)) { + val type = specList[i] + val genericT = genericTs[i] - implicitSpecializationNameMap[genericT.name] = type - } + implicitSpecializationNameMap[genericT.name] = type } } @@ -73,7 +67,13 @@ class GenericsReifier(val project: Project) { return@forEach } - implicitSpecializationNameMap[it.name] = it.defaultType + val defaultType = if (it.defaultType.isGeneric()) { + it.defaultType.instantiateGeneric(implicitSpecializationNameMap) + } else { + it.defaultType + } + + implicitSpecializationNameMap[it.name] = defaultType } } diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericBase.kt b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericBase.kt index 8a12653..55db4a7 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericBase.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericBase.kt @@ -59,7 +59,20 @@ abstract class ResolvingGenericBase(val project: Project) { return specializationNameMap } - private fun specializationList() = explicitGenericsT.ifEmpty { reifier.implicitSpecs } + private fun explicitGenericTypes(): List { + if (explicitGenericsT.isEmpty()) return emptyList() + + val specMap = mutableMapOf() + + genericTs.forEachIndexed { index, genericT -> + val type = explicitGenericsT.getOrNull(index) ?: genericT.defaultType ?: return@forEachIndexed + specMap[genericT.name] = type.instantiateGeneric(specMap) + } + + return genericTs.mapNotNull { specMap[it.name] } + } + + private fun specializationList() = explicitGenericTypes().ifEmpty { reifier.implicitSpecs } private fun unpack(incompleteType: String): Boolean { val packedData = incompleteType.substring(2) diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericFieldFetch.kt b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericFieldFetch.kt index a99ebec..a2becf3 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericFieldFetch.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericFieldFetch.kt @@ -9,7 +9,7 @@ import com.jetbrains.php.lang.psi.resolve.types.PhpType import com.vk.kphpstorm.exphptype.ExPhpTypeForcing import com.vk.kphpstorm.exphptype.ExPhpTypeTplInstantiation import com.vk.kphpstorm.generics.GenericUtil.genericNames -import com.vk.kphpstorm.generics.GenericUtil.getInstantiation +import com.vk.kphpstorm.generics.GenericUtil.getInstantiations import com.vk.kphpstorm.helpers.toExPhpType import com.vk.kphpstorm.kphptags.psi.KphpDocGenericParameterDecl import com.vk.kphpstorm.typeProviders.GenericFieldsTypeProvider @@ -60,20 +60,23 @@ class ResolvingGenericFieldFetch(project: Project) : ResolvingGenericBase(projec val fqn = parts[0] val dotIndex = fqn.lastIndexOf('.') - val className = fqn.substring(0, dotIndex) + val classRawName = fqn.substring(0, dotIndex) val methodName = fqn.substring(dotIndex + 1) - val classType = PhpType().add(className).global(project) + val classType = PhpType().add(classRawName).global(project) val parsed = classType.toExPhpType() - val instantiation = parsed?.getInstantiation() - ?: - // Если не удалось найти класс, значит вывести - // тип поля мы не сможем, поэтому заканчиваем распаковку. - return false - classGenericType = instantiation + val instantiations = parsed?.getInstantiations() + val foundInstantiation = instantiations?.firstOrNull { + val klass = PhpIndex.getInstance(project).getClassesByFQN(it.classFqn).firstOrNull() + val field = klass?.findFieldByName(methodName, false) + + field != null + } ?: return false + + classGenericType = foundInstantiation - klass = PhpIndex.getInstance(project).getClassesByFQN(instantiation.classFqn).firstOrNull() ?: return false + klass = PhpIndex.getInstance(project).getClassesByFQN(foundInstantiation.classFqn).firstOrNull() ?: return false field = klass?.findFieldByName(methodName, false) ?: return false classGenericTs = klass!!.genericNames() diff --git a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericMethodCall.kt b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericMethodCall.kt index 45621e1..68c4903 100644 --- a/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericMethodCall.kt +++ b/src/main/kotlin/com/vk/kphpstorm/generics/ResolvingGenericMethodCall.kt @@ -9,7 +9,7 @@ import com.jetbrains.php.lang.psi.resolve.types.PhpType import com.vk.kphpstorm.exphptype.ExPhpTypeGenericsT import com.vk.kphpstorm.exphptype.ExPhpTypeTplInstantiation import com.vk.kphpstorm.generics.GenericUtil.genericNames -import com.vk.kphpstorm.generics.GenericUtil.getInstantiation +import com.vk.kphpstorm.generics.GenericUtil.getInstantiations import com.vk.kphpstorm.generics.GenericUtil.isReturnGeneric import com.vk.kphpstorm.helpers.toExPhpType import com.vk.kphpstorm.kphptags.psi.KphpDocGenericParameterDecl @@ -63,11 +63,18 @@ class ResolvingGenericMethodCall(project: Project) : ResolvingGenericBase(projec val classType = PhpType().add(classRawName).global(project) val parsed = classType.toExPhpType() - val instantiation = parsed?.getInstantiation() + val instantiations = parsed?.getInstantiations() - val className = if (instantiation != null && instantiation.specializationList.first() !is ExPhpTypeGenericsT) { - classGenericType = instantiation - instantiation.classFqn + val foundInstantiation = instantiations?.firstOrNull { + val klass = PhpIndex.getInstance(project).getClassesByFQN(it.classFqn).firstOrNull() + val method = klass?.findMethodByName(methodName) + + method != null + } + + val className = if (foundInstantiation != null && foundInstantiation.specializationList.first() !is ExPhpTypeGenericsT) { + classGenericType = foundInstantiation + foundInstantiation.classFqn } else { classRawName } diff --git a/src/main/kotlin/com/vk/kphpstorm/highlighting/hints/InlayHintsCollector.kt b/src/main/kotlin/com/vk/kphpstorm/highlighting/hints/InlayHintsCollector.kt index acc3875..419fca6 100644 --- a/src/main/kotlin/com/vk/kphpstorm/highlighting/hints/InlayHintsCollector.kt +++ b/src/main/kotlin/com/vk/kphpstorm/highlighting/hints/InlayHintsCollector.kt @@ -15,7 +15,6 @@ import com.vk.kphpstorm.generics.GenericCall import com.vk.kphpstorm.generics.GenericConstructorCall import com.vk.kphpstorm.generics.GenericFunctionCall import com.vk.kphpstorm.generics.GenericMethodCall -import com.vk.kphpstorm.generics.GenericUtil.genericNames @Suppress("UnstableApiUsage") class InlayHintsCollector( @@ -60,19 +59,13 @@ class InlayHintsCollector( return } - val genericNames = if (call is GenericConstructorCall) { - call.genericNames() - } else { - call.function()!!.genericNames() - }.joinToString(", ") { - it.toHumanReadable(place) - } + val genericNames = call.ownGenericNames() if (genericNames.isEmpty()) { return } - val simplePresentation = myHintsFactory.inlayHint("<$genericNames>") + val simplePresentation = myHintsFactory.inlayHint("") sink.addInlineElement(place.endOffset, false, simplePresentation, false) } diff --git a/src/main/kotlin/com/vk/kphpstorm/inspections/KphpGenericsInspection.kt b/src/main/kotlin/com/vk/kphpstorm/inspections/KphpGenericsInspection.kt index adcbf4a..a7b8e43 100644 --- a/src/main/kotlin/com/vk/kphpstorm/inspections/KphpGenericsInspection.kt +++ b/src/main/kotlin/com/vk/kphpstorm/inspections/KphpGenericsInspection.kt @@ -49,24 +49,24 @@ class KphpGenericsInspection : PhpInspection() { } override fun visitPhpDocTag(tag: PhpDocTag) { - checkGenericsTag(tag) + checkGenericTag(tag) } - private fun checkGenericsTag(tag: PhpDocTag) { + private fun checkGenericTag(tag: PhpDocTag) { if (tag !is KphpDocTagGenericPsiImpl) { return } - var wasNoDefault = false + var wasDefault = false tag.getGenericArgumentsWithExtends().forEach { - if (it.defaultType == null) { - wasNoDefault = true + if (it.defaultType != null) { + wasDefault = true } - if (it.defaultType != null && wasNoDefault) { + if (it.defaultType == null && wasDefault) { holder.registerProblem( tag, - "Generic parameters with a default type cannot come after parameters without a default type", + "Generic parameters with a default type cannot come before parameters without a default type", ProblemHighlightType.GENERIC_ERROR ) } @@ -78,7 +78,7 @@ class KphpGenericsInspection : PhpInspection() { val genericNames = call.genericNames() checkGenericTypesBounds(call, genericNames) - checkInstantiationArgsCount(call) + checkInstantiationParamsCount(call) checkReifiedGenericTypes(call, element, errorPsi) checkReifiedSeveralGenericTypes(call, element, errorPsi) } @@ -115,22 +115,37 @@ class KphpGenericsInspection : PhpInspection() { } } - private fun checkInstantiationArgsCount(call: GenericCall) { - val countGenericNames = if (call is GenericMethodCall && call.isStatic()) { - call.ownGenericNames().size - } else { - call.genericNames().size - call.implicitClassSpecializationNameMap.size - } + private fun checkInstantiationParamsCount(call: GenericCall) { + val minCount = call.ownGenericNames().filter { it.defaultType == null }.size + val maxCount = call.ownGenericNames().size val countExplicitSpecs = call.explicitSpecs.size val explicitSpecsPsi = call.explicitSpecsPsi - if (countGenericNames != countExplicitSpecs && explicitSpecsPsi != null) { + if (minCount == maxCount && minCount != countExplicitSpecs && explicitSpecsPsi != null) { holder.registerProblem( explicitSpecsPsi, - "$countGenericNames type arguments expected for ${call.function()!!.fqn}", + "$minCount generic parameters expected for call, but $countExplicitSpecs passed", ProblemHighlightType.GENERIC_ERROR ) + return + } + + if (countExplicitSpecs < minCount && explicitSpecsPsi != null) { + holder.registerProblem( + explicitSpecsPsi, + "Not enough generic parameters for call, expected at least $minCount", + ProblemHighlightType.GENERIC_ERROR, + ) + return + } + + if (countExplicitSpecs > maxCount && explicitSpecsPsi != null) { + holder.registerProblem( + explicitSpecsPsi, + "Too many generic parameters for call, expected at most $maxCount", + ProblemHighlightType.GENERIC_ERROR, + ) } } diff --git a/src/main/kotlin/com/vk/kphpstorm/typeProviders/GenericMethodsTypeProvider.kt b/src/main/kotlin/com/vk/kphpstorm/typeProviders/GenericMethodsTypeProvider.kt index a11dd44..a6eb139 100644 --- a/src/main/kotlin/com/vk/kphpstorm/typeProviders/GenericMethodsTypeProvider.kt +++ b/src/main/kotlin/com/vk/kphpstorm/typeProviders/GenericMethodsTypeProvider.kt @@ -47,7 +47,7 @@ class GenericMethodsTypeProvider : PhpTypeProvider4 { GenericFunctionsTypeProvider.KEY.signed(type) || GenericFieldsTypeProvider.KEY.signed(type) || KEY.signed(type) || - !type.startsWith("#") + (!type.startsWith("#") && !type.startsWith("%")) } val resultType = PhpType() diff --git a/src/test/fixtures/generics/general/fields/main.fixture.php b/src/test/fixtures/generics/general/fields/main.fixture.php index f668486..a4061ed 100644 --- a/src/test/fixtures/generics/general/fields/main.fixture.php +++ b/src/test/fixtures/generics/general/fields/main.fixture.php @@ -32,6 +32,14 @@ class GenericClass { public $class_string_field; } +/** + * @kphp-generic T + */ +class GenericClass2 { + /** @var T */ + public $class2_plain_field; +} + $a = new GenericClass/**/ (); $a->plain_field->fooMethod(); @@ -68,3 +76,14 @@ function getGenericClass(): GenericClass { return new GenericClass(); } expr_type(getGenericClass()->array_field[0]->fooMethod(), "?int"); expr_type(getGenericClass()->tuple_field[0]->fooMethod(), "?int"); expr_type(getGenericClass()->shape_field["key1"]->fooMethod(), "?int"); + +/** + * @return GenericClass|GenericClass2 + */ +function union() { + return new GenericClass(); +} + +$b = union(); +expr_type($b->class2_plain_field, "string"); +expr_type($b->plain_field, "int"); diff --git a/src/test/fixtures/generics/general/reifier/context/return.fixture.php b/src/test/fixtures/generics/general/reifier/context/return.fixture.php index e9f276b..0bc5c91 100644 --- a/src/test/fixtures/generics/general/reifier/context/return.fixture.php +++ b/src/test/fixtures/generics/general/reifier/context/return.fixture.php @@ -45,3 +45,11 @@ function returnClassOfStringViaGenericMethod() { $a = new ClassWithGenericMethod(); return $a->genericMethod(); } + +/** + * @return GenericClassWithSeveralTypes|GenericClass + */ +function returnClassOfStringViaGenericMethod1() { + $a = new ClassWithGenericMethod(); + return $a->genericMethod(); +} diff --git a/src/test/fixtures/generics/general/reifier/default/generic.fixture.php b/src/test/fixtures/generics/general/reifier/default/generic.fixture.php new file mode 100644 index 0000000..5342037 --- /dev/null +++ b/src/test/fixtures/generics/general/reifier/default/generic.fixture.php @@ -0,0 +1,77 @@ + + * @param T1 $a + * @param T2 $b + * @return T3 + */ +function foo($a, $b) { + return $a; +} + +$a = foo/**/(new Foo, new Boo); +expr_type($a, "\Vector|\Vector(\Reifier\Generic\Foo|\Reifier\Generic\Boo)"); + +$a = foo(new Foo, new Boo); +expr_type($a, "\Vector|\Vector(\Reifier\Generic\Foo|\Reifier\Generic\Boo)"); + + +/** + * @kphp-generic T1, T2 = Vector + * @param T1 $a + * @return T2 + */ +function foo1($a) { + return $a; +} + +$a = foo1/**/(new Foo); +expr_type($a, "\Vector|\Vector(\Reifier\Generic\Foo)"); + +$a = foo1(new Foo); +expr_type($a, "\Vector|\Vector(\Reifier\Generic\Foo)"); + + +/** + * @kphp-generic T1, T2 = Vector, T3 = Vector + * @param T1 $a + * @return T3 + */ +function foo2($a) { + return $a; +} + +$a = foo2(new Foo); +expr_type($a, "\Vector|\Vector(\Vector|\Vector(\Reifier\Generic\Foo))"); + + +/** + * @kphp-generic T1, T2, T3 = Vector|Pair + * @param T1 $a + * @param T2 $b + * @return T3 + */ +function foo3($a, $b) { + return $a; +} + +$a = foo3(new Foo, new Boo); +expr_type($a, "\Pair|\Pair(\Reifier\Generic\Boo,string)|\Vector|\Vector(\Reifier\Generic\Foo)"); + +$b = $a->get(0); +expr_type($b, "\Reifier\Generic\Foo"); + +$b = $a->first(); +expr_type($b, "\Reifier\Generic\Boo"); + +$b = $a->second(); +expr_type($b, "string"); diff --git a/src/test/fixtures/generics/general/reifier/default/main.fixture.php b/src/test/fixtures/generics/general/reifier/default/main.fixture.php index ff281df..2d0399d 100644 --- a/src/test/fixtures/generics/general/reifier/default/main.fixture.php +++ b/src/test/fixtures/generics/general/reifier/default/main.fixture.php @@ -28,13 +28,16 @@ function twoDefaultType() { return null; } $a = twoDefaultType/**/(); expr_type($a, "\Reifier\Default\Pair|\Reifier\Default\Vector"); -$b = twoDefaultType(); +$b = twoDefaultType/**/(); expr_type($b, "\Reifier\Default\Pair|\Reifier\Default\Vector"); +$c = twoDefaultType(); +expr_type($c, "\Reifier\Default\Pair|\Reifier\Default\Vector"); + /** - * @kphp-generic T1 = Pair, T2 - * @param T2 $a + * @kphp-generic T1, T2 = Pair + * @param T1 $a * @return T1|T2 */ function singleDefaultTypeAndSecondFromParam($a) { return null; } diff --git a/src/test/fixtures/generics/general/reifier/default/wrong.fixture.php b/src/test/fixtures/generics/general/reifier/default/wrong.fixture.php index 6f3e8f3..42a44c6 100644 --- a/src/test/fixtures/generics/general/reifier/default/wrong.fixture.php +++ b/src/test/fixtures/generics/general/reifier/default/wrong.fixture.php @@ -1,7 +1,7 @@ @kphp-generic T1, T2 = Pair + * @kphp-generic T1 = Pair, T2 * @param T2 $a * @return T1|T2 */ diff --git a/src/test/fixtures/generics/general/vector_use.fixture.php b/src/test/fixtures/generics/general/vector_use.fixture.php index e28dd90..e4425ef 100644 --- a/src/test/fixtures/generics/general/vector_use.fixture.php +++ b/src/test/fixtures/generics/general/vector_use.fixture.php @@ -57,7 +57,7 @@ function map(array $a, callable $fn) { var_dump($el); }); -$vec->foreach(fn(Goo $el) => var_dump($el)); +$vec->foreach(fn(Goo $el) => $el->getName()); $vec->foreach_key_value(function(string $key, Goo $el) { var_dump($key); @@ -93,8 +93,8 @@ function returnPair(): Pair { $x = returnPair()->second(); $x->gooMethod(); -$vecGoo = new Vector/**/ (); -$vecBoo = new Vector/**/ (); +$vecGoo = new Vector/**/(); +$vecBoo = new Vector/**/(); $combinedVec = $vecGoo->combine_with($vecBoo); $combinedVec->get(0)->booMethod(); diff --git a/src/test/fixtures/generics/inspections/instantiation_args_mismatch.fixture.php b/src/test/fixtures/generics/inspections/instantiation_args_mismatch.fixture.php index bc7c099..f2bc36d 100644 --- a/src/test/fixtures/generics/inspections/instantiation_args_mismatch.fixture.php +++ b/src/test/fixtures/generics/inspections/instantiation_args_mismatch.fixture.php @@ -12,7 +12,7 @@ class NotGeneric {} /** @kphp-generic T */ class GenericT {} -new GenericT/*<>*/(); +new GenericT/*<>*/(); new GenericT/**/(); // ok @@ -20,8 +20,8 @@ class GenericT {} /** @kphp-generic T1, T2 */ class GenericT1T2 {} -new GenericT1T2/*<>*/(); -new GenericT1T2/**/(); +new GenericT1T2/*<>*/(); +new GenericT1T2/**/(); new GenericT1T2/**/(); // ok @@ -36,3 +36,30 @@ function __construct($el) {} } new GenericExplicitConstructorT1AndT/**/(""); + +// Функция с дефолтными шаблонными параметрами +/** + * @kphp-generic T1, T2 = Vector + * @param T1 $a + * @return T2 + */ +function foo($a) { return $a; } + +$a = foo/**/(new Foo); +$a = foo/**/(new Foo); +$a = foo/*<>*/(new Foo); +$a = foo/**/(new Foo); + +/** + * @kphp-generic T1, T2 = Vector, T3 = int + * @param T1 $a + * @return T2 + */ +function foo1($a) { return $a; } + +$a = foo1/**/(new Foo); +$a = foo1/**/(new Foo); +$a = foo1/*<>*/(new Foo); +$a = foo1/**/(100); +$a = foo1/**/(new Foo); +$a = foo1/**/(new Foo); diff --git a/src/test/fixtures/generics/inspections/no_enough_information.fixture.php b/src/test/fixtures/generics/inspections/no_enough_information.fixture.php index 1acfcd2..8e56602 100644 --- a/src/test/fixtures/generics/inspections/no_enough_information.fixture.php +++ b/src/test/fixtures/generics/inspections/no_enough_information.fixture.php @@ -81,4 +81,4 @@ function __construct($el) {} // Выводим ошибку только для первого шаблонного типа new GenericExplicitConstructorT1AndImplicitT2(); new GenericExplicitConstructorT1AndImplicitT2(100); -new GenericExplicitConstructorT1AndImplicitT2/**/(100); +new GenericExplicitConstructorT1AndImplicitT2/**/(100); diff --git a/src/test/fixtures/generics/inspections/several_reified_types.fixture.php b/src/test/fixtures/generics/inspections/several_reified_types.fixture.php index b3e3e1e..28c17af 100644 --- a/src/test/fixtures/generics/inspections/several_reified_types.fixture.php +++ b/src/test/fixtures/generics/inspections/several_reified_types.fixture.php @@ -8,6 +8,6 @@ */ function takeSomething($a, $b) {} -takeSomething(100, ""); -takeSomething(new Foo, ""); +takeSomething(100, ""); +takeSomething(new Foo, ""); takeSomething/**/ (100, ""); diff --git a/src/test/kotlin/com/vk/kphpstorm/testing/tests/generics/general/GenericsGeneralTest.kt b/src/test/kotlin/com/vk/kphpstorm/testing/tests/generics/general/GenericsGeneralTest.kt index ac8be05..5d3ff11 100644 --- a/src/test/kotlin/com/vk/kphpstorm/testing/tests/generics/general/GenericsGeneralTest.kt +++ b/src/test/kotlin/com/vk/kphpstorm/testing/tests/generics/general/GenericsGeneralTest.kt @@ -42,6 +42,11 @@ class GenericsGeneralTest : GenericTestBase() { "generics/general/reifier/default/wrong.fixture.php", "generics/Containers/Pair.php", ) + runFixture( + "generics/general/reifier/default/generic.fixture.php", + "generics/Containers/Vector.php", + "generics/Containers/Pair.php", + ) } fun testReifyFromReturn() {