diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 15e69dc664..08d4d8ec77 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -158,6 +158,12 @@ public function findSpecifiedType( } $specifiedTypes = $this->typeSpecifier->specifyTypesInCondition($scope, $node, TypeSpecifierContext::createTruthy()); + + // don't validate types on overwrite + if ($specifiedTypes->shouldOverwrite()) { + return null; + } + $sureTypes = $specifiedTypes->getSureTypes(); $sureNotTypes = $specifiedTypes->getSureNotTypes(); diff --git a/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php new file mode 100644 index 0000000000..7d12e3f769 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeGenericOverwriteRuleTest.php @@ -0,0 +1,40 @@ + + */ +class ImpossibleCheckTypeGenericOverwriteRuleTest extends RuleTestCase +{ + + public function getRule(): Rule + { + return new ImpossibleCheckTypeMethodCallRule( + new ImpossibleCheckTypeHelper( + $this->createReflectionProvider(), + $this->getTypeSpecifier(), + [], + true, + ), + true, + true, + ); + } + + public function testNoReportedErrorOnOverwrite(): void + { + $this->analyse([__DIR__ . '/data/generic-type-override.php'], []); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/impossible-check-type-generic-overwrite.neon', + ]; + } + +} diff --git a/tests/PHPStan/Rules/Comparison/data/TestTypeOverwriteSpecifyingExtensions.php b/tests/PHPStan/Rules/Comparison/data/TestTypeOverwriteSpecifyingExtensions.php new file mode 100644 index 0000000000..4e2acbd198 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/TestTypeOverwriteSpecifyingExtensions.php @@ -0,0 +1,58 @@ +typeSpecifier = $typeSpecifier; + } + + public function getClass(): string + { + return \GenericTypeOverride\Foo::class; + } + + public function isMethodSupported( + MethodReflection $methodReflection, + MethodCall $node, + TypeSpecifierContext $context + ): bool + { + return $methodReflection->getName() === 'setFetchMode'; + } + + public function specifyTypes( + MethodReflection $methodReflection, + MethodCall $node, + Scope $scope, + TypeSpecifierContext $context + ): SpecifiedTypes + { + $newType = new GenericObjectType(\GenericTypeOverride\Foo::class, [new ObjectType(\GenericTypeOverride\Bar::class)]); + + return $this->typeSpecifier->create( + $node->var, + $newType, + TypeSpecifierContext::createTruthy(), + true + ); + } + +} diff --git a/tests/PHPStan/Rules/Comparison/data/generic-type-override.php b/tests/PHPStan/Rules/Comparison/data/generic-type-override.php new file mode 100644 index 0000000000..e5dad0ece2 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/generic-type-override.php @@ -0,0 +1,39 @@ +createGenericFoo(); + assertType('Foo', $foo); + + // $foo generic will be overridden via MethodTypeSpecifyingExtension + $foo->setFetchMode(); + assertType('Foo', $foo); + } + + /** + * @return Foo + */ + public function createGenericFoo() { + + } +} + + +/** + * @template T + */ +class Foo +{ + public function setFetchMode() { + + } +} + + +class Bar +{ +} diff --git a/tests/PHPStan/Rules/Comparison/impossible-check-type-generic-overwrite.neon b/tests/PHPStan/Rules/Comparison/impossible-check-type-generic-overwrite.neon new file mode 100644 index 0000000000..beb99fbc14 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/impossible-check-type-generic-overwrite.neon @@ -0,0 +1,5 @@ +services: + - + class: PHPStan\Rules\Comparison\GenericTypeOverride + tags: + - phpstan.typeSpecifier.methodTypeSpecifyingExtension