diff --git a/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php b/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php index d336534264..2f199271ab 100644 --- a/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php +++ b/tests/PHPStan/Analyser/DynamicReturnTypeExtensionTypeInferenceTest.php @@ -20,6 +20,7 @@ public static function dataAsserts(): iterable yield from self::gatherAssertTypes(__DIR__ . '/data/dynamic-method-return-compound-types.php'); yield from self::gatherAssertTypes(__DIR__ . '/data/bug-7344.php'); yield from self::gatherAssertTypes(__DIR__ . '/data/bug-7391b.php'); + yield from self::gatherAssertTypes(__DIR__ . '/data/bug-7385.php'); } /** diff --git a/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php b/tests/PHPStan/Analyser/data/TestDynamicReturnTypeExtensions.php index dd72c4525e..376802584e 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; @@ -299,3 +304,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