From ab4b63d413e9625dc66ac4f6c5f5dde5c078ff54 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Wed, 6 Aug 2025 01:45:30 +0200 Subject: [PATCH 01/14] do not skip Generator for MissingTypehintCheck --- src/Rules/MissingTypehintCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/MissingTypehintCheck.php b/src/Rules/MissingTypehintCheck.php index e92904cd09..4563400daa 100644 --- a/src/Rules/MissingTypehintCheck.php +++ b/src/Rules/MissingTypehintCheck.php @@ -43,7 +43,7 @@ final class MissingTypehintCheck Traversable::class, Iterator::class, IteratorAggregate::class, - Generator::class, + //Generator::class, ]; /** From 5f2ba73f466ca8721f000d13f71b14571c7b3cd6 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 14 Sep 2025 21:04:25 +0200 Subject: [PATCH 02/14] do not skip generic iterables for MissingTypehintCheck on bleeding edge # Conflicts: # conf/bleedingEdge.neon # conf/config.neon # conf/parametersSchema.neon --- conf/bleedingEdge.neon | 1 + conf/config.neon | 1 + conf/parametersSchema.neon | 1 + src/Rules/MissingTypehintCheck.php | 9 +++++++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/conf/bleedingEdge.neon b/conf/bleedingEdge.neon index c9453e24a9..a68d1a352a 100644 --- a/conf/bleedingEdge.neon +++ b/conf/bleedingEdge.neon @@ -10,6 +10,7 @@ parameters: internalTag: true newStaticInAbstractClassStaticMethod: true checkExtensionsForComparisonOperators: true + checkGenericIterableClasses: true reportTooWideBool: true rawMessageInBaseline: true reportNestedTooWideType: false # tmp diff --git a/conf/config.neon b/conf/config.neon index 2b3799501b..f1c38ca286 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -36,6 +36,7 @@ parameters: internalTag: false newStaticInAbstractClassStaticMethod: false checkExtensionsForComparisonOperators: false + checkGenericIterableClasses: false reportTooWideBool: false rawMessageInBaseline: false reportNestedTooWideType: false diff --git a/conf/parametersSchema.neon b/conf/parametersSchema.neon index caef47711c..6b2ecc2b47 100644 --- a/conf/parametersSchema.neon +++ b/conf/parametersSchema.neon @@ -39,6 +39,7 @@ parametersSchema: internalTag: bool() newStaticInAbstractClassStaticMethod: bool() checkExtensionsForComparisonOperators: bool() + checkGenericIterableClasses: bool() reportTooWideBool: bool() rawMessageInBaseline: bool() reportNestedTooWideType: bool() diff --git a/src/Rules/MissingTypehintCheck.php b/src/Rules/MissingTypehintCheck.php index 4563400daa..647ba0c022 100644 --- a/src/Rules/MissingTypehintCheck.php +++ b/src/Rules/MissingTypehintCheck.php @@ -43,7 +43,7 @@ final class MissingTypehintCheck Traversable::class, Iterator::class, IteratorAggregate::class, - //Generator::class, + Generator::class, ]; /** @@ -54,6 +54,8 @@ public function __construct( private bool $checkMissingCallableSignature, #[AutowiredParameter(ref: '%featureToggles.skipCheckGenericClasses%')] private array $skipCheckGenericClasses, + #[AutowiredParameter(ref: '%featureToggles.checkGenericIterableClasses%')] + private bool $checkGenericIterableClasses, ) { } @@ -118,7 +120,10 @@ public function getNonGenericObjectTypesWithGenericClass(Type $type): array if ($classReflection === null) { return $type; } - if (in_array($classReflection->getName(), self::ITERABLE_GENERIC_CLASS_NAMES, true)) { + if ( + $this->checkGenericIterableClasses !== true && + in_array($classReflection->getName(), self::ITERABLE_GENERIC_CLASS_NAMES, true) + ) { // checked by getIterableTypesWithMissingValueTypehint() already return $type; } From 96a384b8f06b8f85d3543c5647f8558684a18815 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Mon, 1 Sep 2025 23:16:34 +0200 Subject: [PATCH 03/14] align MissingTypehintCheck constructs with new argument --- tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php | 2 +- .../PHPStan/Rules/Classes/LocalTypeTraitUseAliasesRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MethodTagRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MethodTagTraitRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MethodTagTraitUseRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MixinRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MixinTraitRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/MixinTraitUseRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/PropertyTagRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/PropertyTagTraitRuleTest.php | 2 +- tests/PHPStan/Rules/Classes/PropertyTagTraitUseRuleTest.php | 2 +- .../Rules/Constants/MissingClassConstantTypehintRuleTest.php | 2 +- .../Functions/MissingFunctionParameterTypehintRuleTest.php | 2 +- .../Rules/Functions/MissingFunctionReturnTypehintRuleTest.php | 2 +- .../Rules/Methods/MissingMethodParameterTypehintRuleTest.php | 2 +- .../Rules/Methods/MissingMethodReturnTypehintRuleTest.php | 2 +- .../PHPStan/Rules/Methods/MissingMethodSelfOutTypeRuleTest.php | 2 +- tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php | 2 +- tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php | 2 +- tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php | 2 +- .../Rules/Properties/MissingPropertyTypehintRuleTest.php | 2 +- .../Rules/Properties/SetPropertyHookParameterRuleTest.php | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php b/tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php index 937678e376..638fad3000 100644 --- a/tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php +++ b/tests/PHPStan/Rules/Classes/LocalTypeAliasesRuleTest.php @@ -29,7 +29,7 @@ protected function getRule(): Rule ['GlobalTypeAlias' => 'int|string'], self::createReflectionProvider(), $container->getByType(TypeNodeResolver::class), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new ClassNameCheck( new ClassCaseSensitivityCheck($reflectionProvider, true), new ClassForbiddenNameCheck($container), diff --git a/tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php b/tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php index 42fc04162c..e2c0836fe7 100644 --- a/tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php +++ b/tests/PHPStan/Rules/Classes/LocalTypeTraitAliasesRuleTest.php @@ -28,7 +28,7 @@ protected function getRule(): Rule ['GlobalTypeAlias' => 'int|string'], self::createReflectionProvider(), $container->getByType(TypeNodeResolver::class), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new ClassNameCheck( new ClassCaseSensitivityCheck($reflectionProvider, true), new ClassForbiddenNameCheck($container), diff --git a/tests/PHPStan/Rules/Classes/LocalTypeTraitUseAliasesRuleTest.php b/tests/PHPStan/Rules/Classes/LocalTypeTraitUseAliasesRuleTest.php index 9cb131f4b0..dd998ea14c 100644 --- a/tests/PHPStan/Rules/Classes/LocalTypeTraitUseAliasesRuleTest.php +++ b/tests/PHPStan/Rules/Classes/LocalTypeTraitUseAliasesRuleTest.php @@ -28,7 +28,7 @@ protected function getRule(): Rule ['GlobalTypeAlias' => 'int|string'], self::createReflectionProvider(), $container->getByType(TypeNodeResolver::class), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new ClassNameCheck( new ClassCaseSensitivityCheck($reflectionProvider, true), new ClassForbiddenNameCheck($container), diff --git a/tests/PHPStan/Rules/Classes/MethodTagRuleTest.php b/tests/PHPStan/Rules/Classes/MethodTagRuleTest.php index 7ad38610fc..21cf1a62b9 100644 --- a/tests/PHPStan/Rules/Classes/MethodTagRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MethodTagRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/MethodTagTraitRuleTest.php b/tests/PHPStan/Rules/Classes/MethodTagTraitRuleTest.php index c49384db82..a75edf63c1 100644 --- a/tests/PHPStan/Rules/Classes/MethodTagTraitRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MethodTagTraitRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/MethodTagTraitUseRuleTest.php b/tests/PHPStan/Rules/Classes/MethodTagTraitUseRuleTest.php index 7b297798f2..8d0950b6da 100644 --- a/tests/PHPStan/Rules/Classes/MethodTagTraitUseRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MethodTagTraitUseRuleTest.php @@ -33,7 +33,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/MixinRuleTest.php b/tests/PHPStan/Rules/Classes/MixinRuleTest.php index e3604e12d8..604af92e56 100644 --- a/tests/PHPStan/Rules/Classes/MixinRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MixinRuleTest.php @@ -33,7 +33,7 @@ protected function getRule(): Rule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/MixinTraitRuleTest.php b/tests/PHPStan/Rules/Classes/MixinTraitRuleTest.php index b71109d5e2..1d10c554b7 100644 --- a/tests/PHPStan/Rules/Classes/MixinTraitRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MixinTraitRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): Rule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/MixinTraitUseRuleTest.php b/tests/PHPStan/Rules/Classes/MixinTraitUseRuleTest.php index a1a6adfd04..673783bb6a 100644 --- a/tests/PHPStan/Rules/Classes/MixinTraitUseRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MixinTraitUseRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): Rule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/PropertyTagRuleTest.php b/tests/PHPStan/Rules/Classes/PropertyTagRuleTest.php index 09c07f76a7..e2e6f0dda6 100644 --- a/tests/PHPStan/Rules/Classes/PropertyTagRuleTest.php +++ b/tests/PHPStan/Rules/Classes/PropertyTagRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/PropertyTagTraitRuleTest.php b/tests/PHPStan/Rules/Classes/PropertyTagTraitRuleTest.php index 6ce25d3c3d..dd6631d0c6 100644 --- a/tests/PHPStan/Rules/Classes/PropertyTagTraitRuleTest.php +++ b/tests/PHPStan/Rules/Classes/PropertyTagTraitRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Classes/PropertyTagTraitUseRuleTest.php b/tests/PHPStan/Rules/Classes/PropertyTagTraitUseRuleTest.php index 8f8dd243b7..244b79acaa 100644 --- a/tests/PHPStan/Rules/Classes/PropertyTagTraitUseRuleTest.php +++ b/tests/PHPStan/Rules/Classes/PropertyTagTraitUseRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): TRule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/Constants/MissingClassConstantTypehintRuleTest.php b/tests/PHPStan/Rules/Constants/MissingClassConstantTypehintRuleTest.php index 2c95dbb317..aa0251ed2d 100644 --- a/tests/PHPStan/Rules/Constants/MissingClassConstantTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Constants/MissingClassConstantTypehintRuleTest.php @@ -15,7 +15,7 @@ class MissingClassConstantTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingClassConstantTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingClassConstantTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php index c88fb550da..7d43d80c8d 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php @@ -14,7 +14,7 @@ class MissingFunctionParameterTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingFunctionParameterTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingFunctionParameterTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php index 2b64aba5ba..8f6faef47f 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php @@ -14,7 +14,7 @@ class MissingFunctionReturnTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingFunctionReturnTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingFunctionReturnTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Methods/MissingMethodParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Methods/MissingMethodParameterTypehintRuleTest.php index fce941c3ce..db4b818f92 100644 --- a/tests/PHPStan/Rules/Methods/MissingMethodParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MissingMethodParameterTypehintRuleTest.php @@ -14,7 +14,7 @@ class MissingMethodParameterTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingMethodParameterTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingMethodParameterTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Methods/MissingMethodReturnTypehintRuleTest.php b/tests/PHPStan/Rules/Methods/MissingMethodReturnTypehintRuleTest.php index ffeb6ce61b..564a49b30e 100644 --- a/tests/PHPStan/Rules/Methods/MissingMethodReturnTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MissingMethodReturnTypehintRuleTest.php @@ -15,7 +15,7 @@ class MissingMethodReturnTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingMethodReturnTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingMethodReturnTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Methods/MissingMethodSelfOutTypeRuleTest.php b/tests/PHPStan/Rules/Methods/MissingMethodSelfOutTypeRuleTest.php index cc74d519e9..c529e67daa 100644 --- a/tests/PHPStan/Rules/Methods/MissingMethodSelfOutTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MissingMethodSelfOutTypeRuleTest.php @@ -14,7 +14,7 @@ class MissingMethodSelfOutTypeRuleTest extends RuleTestCase protected function getRule(): TRule { - return new MissingMethodSelfOutTypeRule(new MissingTypehintCheck(true, [])); + return new MissingMethodSelfOutTypeRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php b/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php index 84d94062bb..247ce2a007 100644 --- a/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/FunctionAssertRuleTest.php @@ -29,7 +29,7 @@ protected function getRule(): Rule $reflectionProvider, $container, ), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new GenericObjectTypeCheck(), true, true, diff --git a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php index 687597c13a..0c911449d2 100644 --- a/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php @@ -32,7 +32,7 @@ protected function getRule(): Rule $container, ), new GenericObjectTypeCheck(), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new UnresolvableTypeHelper(), true, true, diff --git a/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php b/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php index d4a8da69de..e23fa3eb55 100644 --- a/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php +++ b/tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest.php @@ -29,7 +29,7 @@ protected function getRule(): Rule $reflectionProvider, $container, ), - new MissingTypehintCheck(true, []), + new MissingTypehintCheck(true, [], true), new GenericObjectTypeCheck(), true, true, diff --git a/tests/PHPStan/Rules/Properties/MissingPropertyTypehintRuleTest.php b/tests/PHPStan/Rules/Properties/MissingPropertyTypehintRuleTest.php index c274aff53c..73d4592458 100644 --- a/tests/PHPStan/Rules/Properties/MissingPropertyTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Properties/MissingPropertyTypehintRuleTest.php @@ -14,7 +14,7 @@ class MissingPropertyTypehintRuleTest extends RuleTestCase protected function getRule(): Rule { - return new MissingPropertyTypehintRule(new MissingTypehintCheck(true, [])); + return new MissingPropertyTypehintRule(new MissingTypehintCheck(true, [], true)); } public function testRule(): void diff --git a/tests/PHPStan/Rules/Properties/SetPropertyHookParameterRuleTest.php b/tests/PHPStan/Rules/Properties/SetPropertyHookParameterRuleTest.php index 588845b5a1..59f047aa86 100644 --- a/tests/PHPStan/Rules/Properties/SetPropertyHookParameterRuleTest.php +++ b/tests/PHPStan/Rules/Properties/SetPropertyHookParameterRuleTest.php @@ -15,7 +15,7 @@ class SetPropertyHookParameterRuleTest extends RuleTestCase protected function getRule(): TRule { - return new SetPropertyHookParameterRule(new MissingTypehintCheck(true, []), true, true); + return new SetPropertyHookParameterRule(new MissingTypehintCheck(true, [], true), true, true); } #[RequiresPhp('>= 8.4')] From fac1185b5aa6841f4b3a8bbca9592f165c8f458d Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 14 Sep 2025 22:16:42 +0200 Subject: [PATCH 04/14] more specific return type in Stub PDOStatement::getIterator --- stubs/PDOStatement.stub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/PDOStatement.stub b/stubs/PDOStatement.stub index f1b32e3d37..1b462cdc4b 100644 --- a/stubs/PDOStatement.stub +++ b/stubs/PDOStatement.stub @@ -21,7 +21,7 @@ class PDOStatement implements Traversable, IteratorAggregate public function getColumnMeta(int $column) {} /** - * @return Iterator + * @return Iterator> */ public function getIterator() {} } From 09f703684117bc5914616a1a90387f87ade14ae9 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Mon, 15 Sep 2025 00:55:36 +0200 Subject: [PATCH 05/14] align unit tests for new generic Iterable checks --- stubs/runtime/ReflectionAttribute.php | 6 ++++++ tests/PHPStan/Analyser/nsrt/bug-8886.php | 2 +- tests/PHPStan/Rules/Classes/MixinRuleTest.php | 4 ++++ .../Functions/MissingFunctionParameterTypehintRuleTest.php | 5 +++++ .../Functions/data/missing-function-parameter-typehint.php | 6 +++--- .../Rules/Methods/data/missing-method-return-typehint.php | 2 +- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/stubs/runtime/ReflectionAttribute.php b/stubs/runtime/ReflectionAttribute.php index 19fe0a66ff..c5a8e92c9a 100644 --- a/stubs/runtime/ReflectionAttribute.php +++ b/stubs/runtime/ReflectionAttribute.php @@ -22,10 +22,16 @@ public function isRepeated(): bool { } + /** + * @return array; + */ public function getArguments(): array { } + /** + * @return self + */ public function newInstance(): object { } diff --git a/tests/PHPStan/Analyser/nsrt/bug-8886.php b/tests/PHPStan/Analyser/nsrt/bug-8886.php index 47a385dea5..146cbb590b 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-8886.php +++ b/tests/PHPStan/Analyser/nsrt/bug-8886.php @@ -9,5 +9,5 @@ function testPDOStatementGetIterator(): void { $pdo = new PDO('sqlite::memory:'); $stmt = $pdo->query('SELECT 1'); - assertType('Iterator', $stmt->getIterator()); + assertType('Iterator>', $stmt->getIterator()); } diff --git a/tests/PHPStan/Rules/Classes/MixinRuleTest.php b/tests/PHPStan/Rules/Classes/MixinRuleTest.php index 604af92e56..4ef414df2a 100644 --- a/tests/PHPStan/Rules/Classes/MixinRuleTest.php +++ b/tests/PHPStan/Rules/Classes/MixinRuleTest.php @@ -69,6 +69,10 @@ public function testRule(): void 'PHPDoc tag @mixin contains generic class ReflectionClass but does not specify its types: T', 50, ], + [ + 'PHPDoc tag @mixin contains generic interface Iterator but does not specify its types: TKey, TValue', + 50, + ], [ 'PHPDoc tag @mixin contains unknown class MixinRule\UnknownestClass.', 50, diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php index 7d43d80c8d..09fef18b4d 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php @@ -71,6 +71,11 @@ public function testRule(): void 148, MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, ], + [ + 'Function MissingFunctionParameterTypehint\missingTraversableTypehint() has parameter $traversable with generic interface Traversable but does not specify its types: TKey, TValu', + 148, + MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, + ], [ 'Function MissingFunctionParameterTypehint\missingTraversableTypehintPhpDoc() has parameter $traversable with no value type specified in iterable type Traversable.', 156, diff --git a/tests/PHPStan/Rules/Functions/data/missing-function-parameter-typehint.php b/tests/PHPStan/Rules/Functions/data/missing-function-parameter-typehint.php index 7b01bfbf96..a1540e657d 100644 --- a/tests/PHPStan/Rules/Functions/data/missing-function-parameter-typehint.php +++ b/tests/PHPStan/Rules/Functions/data/missing-function-parameter-typehint.php @@ -63,7 +63,7 @@ function unionTypeWithUnknownArrayValueTypehint($a) } /** - * @param iterable&\Traversable $a + * @param iterable&\Traversable $a */ function iterableIntersectionTypehint($a) { @@ -71,7 +71,7 @@ function iterableIntersectionTypehint($a) } /** - * @param iterable&\Traversable $a + * @param iterable&\Traversable $a */ function iterableIntersectionTypehint2($a) { @@ -151,7 +151,7 @@ function missingTraversableTypehint(\Traversable $traversable) } /** - * @param \Traversable $traversable + * @param \Traversable $traversable */ function missingTraversableTypehintPhpDoc($traversable) { diff --git a/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php b/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php index 480373825a..e4605c3c69 100644 --- a/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php +++ b/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php @@ -106,7 +106,7 @@ public function doFoo(): callable class IterableIntersection { - /** @return FooInterface[]|\Traversable */ + /** @return FooInterface[]|\Traversable */ public function doFoo(): \Traversable { From 47bbfb1764ce8ba662166281f10d4a633baf374e Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 11:25:55 +0100 Subject: [PATCH 06/14] simplify boolean check --- src/Rules/MissingTypehintCheck.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rules/MissingTypehintCheck.php b/src/Rules/MissingTypehintCheck.php index 647ba0c022..99afc4c9ea 100644 --- a/src/Rules/MissingTypehintCheck.php +++ b/src/Rules/MissingTypehintCheck.php @@ -121,7 +121,7 @@ public function getNonGenericObjectTypesWithGenericClass(Type $type): array return $type; } if ( - $this->checkGenericIterableClasses !== true && + !$this->checkGenericIterableClasses && in_array($classReflection->getName(), self::ITERABLE_GENERIC_CLASS_NAMES, true) ) { // checked by getIterableTypesWithMissingValueTypehint() already From 389a7289337d19a2f0240d691685470641c59ff2 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 12:03:07 +0100 Subject: [PATCH 07/14] fix typo in expected error message --- .../Functions/MissingFunctionParameterTypehintRuleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php index 09fef18b4d..fa85c96487 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php @@ -72,7 +72,7 @@ public function testRule(): void MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, ], [ - 'Function MissingFunctionParameterTypehint\missingTraversableTypehint() has parameter $traversable with generic interface Traversable but does not specify its types: TKey, TValu', + 'Function MissingFunctionParameterTypehint\missingTraversableTypehint() has parameter $traversable with generic interface Traversable but does not specify its types: TKey, TValue', 148, MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, ], From 226dc410dc301862e4bc7823f2e487491ea08071 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 12:03:41 +0100 Subject: [PATCH 08/14] add generator return type hint --- src/Analyser/Generator/NodeHandler/StmtsHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Analyser/Generator/NodeHandler/StmtsHandler.php b/src/Analyser/Generator/NodeHandler/StmtsHandler.php index a03d398e2d..552796e917 100644 --- a/src/Analyser/Generator/NodeHandler/StmtsHandler.php +++ b/src/Analyser/Generator/NodeHandler/StmtsHandler.php @@ -185,6 +185,7 @@ private function getNextUnreachableStatements(array $nodes, bool $earlyBinding): /** * @param Node\Stmt[] $nextStmts * @param (callable(Node, Scope, callable(Node, Scope): void): void)|null $alternativeNodeCallback + * @return Generator */ private function processUnreachableStatement(array $nextStmts, GeneratorScope $scope, ?callable $alternativeNodeCallback): Generator { From 1163e3c606190a42dabaa02bcab40ceb9c6b0113 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 12:42:05 +0100 Subject: [PATCH 09/14] add test stubs for Generator Return Types --- .../data/missing-function-return-typehint.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php b/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php index 6ab9bd061c..6f8d57b436 100644 --- a/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php +++ b/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php @@ -148,4 +148,38 @@ function callableNestedNoPrototype() : callable{ function callableNestedWithPrototype() : callable{ } + + function returnsGeneratorOfIntegersNoPrototype(): \Generator + { + yield 1; + yield 2; + yield 3; + } + + /** + * @return \Generator + */ + function returnsGeneratorOfIntegersWithPrototype(): \Generator + { + yield 1; + yield 2; + yield 3; + } + + function returnsGeneratorOfIntegersByStringNoPrototype(): \Generator + { + yield '1' => 1; + yield '2' => 2; + yield '3' => 3; + } + + /** + * @return \Generator + */ + function returnsGeneratorOfIntegersByStringwithPrototype(): \Generator + { + yield '1' => 1; + yield '2' => 2; + yield '3' => 3; + } } From f754efd1980eacdbc36c7aaec68137bcf40c50da Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 13:50:21 +0100 Subject: [PATCH 10/14] add expected reports of generator without specified types --- .../Functions/MissingFunctionReturnTypehintRuleTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php index 8f6faef47f..99f8293467 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php @@ -58,6 +58,14 @@ public function testRule(): void 'Function MissingFunctionReturnTypehint\callableNestedNoPrototype() return type has no signature specified for callable.', 141, ], + [ + 'Function MissingFunctionReturnTypehint\returnsGeneratorOfIntegersNoPrototype() return type with generic class Generator does not specify its types: TKey, TValue, TSend, TReturn', + 152, + ], + [ + 'Function MissingFunctionReturnTypehint\returnsGeneratorOfIntegersByStringNoPrototype() return type with generic class Generator does not specify its types: TKey, TValue, TSend, TReturn', + 169, + ], ]); } From 1c4fbea7be3edf485c013ac93206a839335218b5 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 13:55:04 +0100 Subject: [PATCH 11/14] cleanup duplicated entry and not exsting typehint tip --- .../Functions/MissingFunctionParameterTypehintRuleTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php index fa85c96487..c36e69875f 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionParameterTypehintRuleTest.php @@ -74,12 +74,6 @@ public function testRule(): void [ 'Function MissingFunctionParameterTypehint\missingTraversableTypehint() has parameter $traversable with generic interface Traversable but does not specify its types: TKey, TValue', 148, - MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, - ], - [ - 'Function MissingFunctionParameterTypehint\missingTraversableTypehintPhpDoc() has parameter $traversable with no value type specified in iterable type Traversable.', - 156, - MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, ], [ 'Function MissingFunctionParameterTypehint\missingCallableSignature() has parameter $cb with no signature specified for callable.', From 127f66665b1c89ab69b08b3a7a38144bb5d41deb Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 14:51:08 +0100 Subject: [PATCH 12/14] add more testcase for generic types --- .../MissingFunctionReturnTypehintRuleTest.php | 16 ++++++++ .../data/missing-function-return-typehint.php | 41 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php index 99f8293467..1c8077f7df 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php @@ -66,6 +66,22 @@ public function testRule(): void 'Function MissingFunctionReturnTypehint\returnsGeneratorOfIntegersByStringNoPrototype() return type with generic class Generator does not specify its types: TKey, TValue, TSend, TReturn', 169, ], + [ + 'Function MissingFunctionReturnTypehint\returnsIteratorNoPrototype() return type with generic interface Iterator does not specify its types: TKey, TValue, TSend, TReturn', + 186, + ], + [ + 'Function MissingFunctionReturnTypehint\returnsIteratorAggregateNoPrototype() return type with generic interface IteratorAggregate does not specify its types: TKey, TValue, TSend, TReturn', + 199, + ], + [ + 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type has no value type specified in iterable type Traversable', + 212, + ], + [ + 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type with generic interface Traversable does not specify its types: TKey, TValue, TSend, TReturn', + 212, + ], ]); } diff --git a/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php b/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php index 6f8d57b436..3858adadd9 100644 --- a/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php +++ b/tests/PHPStan/Rules/Functions/data/missing-function-return-typehint.php @@ -176,10 +176,49 @@ function returnsGeneratorOfIntegersByStringNoPrototype(): \Generator /** * @return \Generator */ - function returnsGeneratorOfIntegersByStringwithPrototype(): \Generator + function returnsGeneratorOfIntegersByStringWithPrototype(): \Generator { yield '1' => 1; yield '2' => 2; yield '3' => 3; } + + function returnsIteratorNoPrototype(): \Iterator + { + yield 'test'; + } + + /** + * @return \Iterator + */ + function returnsIteratorWithPrototype(): \Iterator + { + yield 'test'; + } + + function returnsIteratorAggregateNoPrototype(): \IteratorAggregate + { + return new \ArrayObject([]); + } + + /** + * @return \IteratorAggregate + */ + function returnsIteratorAggregateWithPrototype(): \IteratorAggregate + { + return new \ArrayObject([]); + } + + function returnsTraversableNoPrototype(): \Traversable + { + yield 'test'; + } + + /** + * @return \Traversable + */ + function returnsTraversableWithPrototype(): \Traversable + { + yield 'test'; + } } From 9d068c9ce26746de61b484733de8729253a677c4 Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 16:31:57 +0100 Subject: [PATCH 13/14] correct expected reported errors --- .../Functions/MissingFunctionReturnTypehintRuleTest.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php index 1c8077f7df..eb081d9271 100644 --- a/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php +++ b/tests/PHPStan/Rules/Functions/MissingFunctionReturnTypehintRuleTest.php @@ -67,19 +67,20 @@ public function testRule(): void 169, ], [ - 'Function MissingFunctionReturnTypehint\returnsIteratorNoPrototype() return type with generic interface Iterator does not specify its types: TKey, TValue, TSend, TReturn', + 'Function MissingFunctionReturnTypehint\returnsIteratorNoPrototype() return type with generic interface Iterator does not specify its types: TKey, TValue', 186, ], [ - 'Function MissingFunctionReturnTypehint\returnsIteratorAggregateNoPrototype() return type with generic interface IteratorAggregate does not specify its types: TKey, TValue, TSend, TReturn', + 'Function MissingFunctionReturnTypehint\returnsIteratorAggregateNoPrototype() return type with generic interface IteratorAggregate does not specify its types: TKey, TValue', 199, ], [ - 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type has no value type specified in iterable type Traversable', + 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type has no value type specified in iterable type Traversable.', 212, + MissingTypehintCheck::MISSING_ITERABLE_VALUE_TYPE_TIP, ], [ - 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type with generic interface Traversable does not specify its types: TKey, TValue, TSend, TReturn', + 'Function MissingFunctionReturnTypehint\returnsTraversableNoPrototype() return type with generic interface Traversable does not specify its types: TKey, TValue', 212, ], ]); From d51ed98ef57bde6ea101a1e0c20acd740c9028ac Mon Sep 17 00:00:00 2001 From: Flyingmana Date: Sun, 23 Nov 2025 16:32:36 +0100 Subject: [PATCH 14/14] change union type to intersectional type --- .../Rules/Methods/data/missing-method-return-typehint.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php b/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php index e4605c3c69..f84c66f69b 100644 --- a/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php +++ b/tests/PHPStan/Rules/Methods/data/missing-method-return-typehint.php @@ -106,7 +106,7 @@ public function doFoo(): callable class IterableIntersection { - /** @return FooInterface[]|\Traversable */ + /** @return FooInterface[]&\Traversable */ public function doFoo(): \Traversable {