diff --git a/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php b/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php index 59ebef356e..572ec5921e 100644 --- a/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php +++ b/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php @@ -20,6 +20,7 @@ public function dataAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/dynamic-method-return-getsingle-conditional.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7344.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7391b.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7385.php'); } /** diff --git a/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php b/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php index 446e478cd0..d24bc484c9 100644 --- a/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php +++ b/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php @@ -7,6 +7,10 @@ use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; +use PHPStan\Analyser\SpecifiedTypes; +use PHPStan\Analyser\TypeSpecifier; +use PHPStan\Analyser\TypeSpecifierAwareExtension; +use PHPStan\Analyser\TypeSpecifierContext; use PHPStan\Reflection\Dummy\ChangedTypeMethodReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; @@ -18,6 +22,7 @@ use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; use PHPStan\Type\IntegerType; use PHPStan\Type\IntersectionType; +use PHPStan\Type\MethodTypeSpecifyingExtension; use PHPStan\Type\NeverType; use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; @@ -263,3 +268,34 @@ public function getTypeFromStaticMethodCall( return $scope->getType(new New_($methodCall->class)); } } + +class Bug7385MethodTypeSpecifyingExtension implements TypeSpecifierAwareExtension, MethodTypeSpecifyingExtension +{ + public function getClass(): string + { + return \Bug7385\Model::class; + } + + public function isMethodSupported(MethodReflection $methodReflection, MethodCall $methodCall = null, TypeSpecifierContext $context = null): bool + { + return $methodReflection->getName() === 'assertHasIface'; + } + + /** @var TypeSpecifier */ + protected $typeSpecifier; + + public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void + { + $this->typeSpecifier = $typeSpecifier; + } + + public function specifyTypes(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes + { + $type = TypeCombinator::intersect( + $scope->getType($methodCall->var), + new ObjectType(\Bug7385\Iface::class) + ); + + return $this->typeSpecifier->create($methodCall->var, $type, TypeSpecifierContext::createNull()); + } +} diff --git a/tests/PHPStan/Analyser/data/bug-7385.php b/tests/PHPStan/Analyser/data/bug-7385.php new file mode 100644 index 0000000000..b41d53fbd2 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-7385.php @@ -0,0 +1,24 @@ +assertHasIface(); + assertType('Bug7385\Iface&Bug7385\Model', $m); +}; diff --git a/tests/PHPStan/Analyser/dynamic-return-type.neon b/tests/PHPStan/Analyser/dynamic-return-type.neon index e80f2018a0..038a84da54 100644 --- a/tests/PHPStan/Analyser/dynamic-return-type.neon +++ b/tests/PHPStan/Analyser/dynamic-return-type.neon @@ -39,3 +39,7 @@ services: class: PHPStan\Tests\Bug7391BDynamicStaticMethodReturnTypeExtension tags: - phpstan.broker.dynamicStaticMethodReturnTypeExtension + - + class: PHPStan\Tests\Bug7385MethodTypeSpecifyingExtension + tags: + - phpstan.typeSpecifier.methodTypeSpecifyingExtension