diff --git a/src/Type/Generic/TemplateTypeTrait.php b/src/Type/Generic/TemplateTypeTrait.php index a1f381e6f5..aa6b5e17c0 100644 --- a/src/Type/Generic/TemplateTypeTrait.php +++ b/src/Type/Generic/TemplateTypeTrait.php @@ -18,6 +18,7 @@ use PHPStan\Type\TypeUtils; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; +use function count; use function sprintf; /** @@ -287,6 +288,23 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap ]))->union($map); } + if ($receivedType instanceof UnionType) { + $matchingTypes = []; + foreach ($receivedType->getTypes() as $innerType) { + if (!$resolvedBound->isSuperTypeOf($innerType)->yes()) { + continue; + } + + $matchingTypes[] = $innerType; + } + if (count($matchingTypes) > 0) { + $filteredType = TypeCombinator::union(...$matchingTypes); + return (new TemplateTypeMap([ + $this->name => $filteredType, + ]))->union($map); + } + } + return $map; } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index 3ecba00c33..82ee7e1566 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -931,6 +931,38 @@ public function testBug13556(): void $this->analyse([__DIR__ . '/data/bug-13556.php'], []); } + public function testBug9732(): void + { + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->checkImplicitMixed = true; + + $this->analyse([__DIR__ . '/data/bug-9732.php'], [ + [ + 'Parameter #1 $array of static method Bug9732\HelloWorld::stringifyKeys() expects array, array given.', + 21, + ], + ]); + } + + public function testBug12558(): void + { + $this->checkThisOnly = false; + $this->checkExplicitMixed = true; + $this->checkImplicitMixed = true; + + $this->analyse([__DIR__ . '/data/bug-12558.php'], [ + [ + 'Parameter #1 $object of static method Bug12558\Foo::assertStatic() expects Bug12558\Foo, Bug12558\Foo|null given.', + 45, + ], + [ + 'Parameter #1 $object of static method Bug12558\Foo::assertStatic() expects Bug12558\Foo, bool|Bug12558\Foo given.', + 46, + ], + ]); + } + #[RequiresPhp('>= 8.5')] public function testPipeOperator(): void { diff --git a/tests/PHPStan/Rules/Methods/data/bug-12558.php b/tests/PHPStan/Rules/Methods/data/bug-12558.php new file mode 100644 index 0000000000..88a8224924 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-12558.php @@ -0,0 +1,48 @@ +createFoo())->foo(); + (static::class)::assertStatic($this->createFooNullable())->foo(); + (static::class)::assertStatic($this->createFooUnionedWithBool())->foo(); + } +} diff --git a/tests/PHPStan/Rules/Methods/data/bug-9732.php b/tests/PHPStan/Rules/Methods/data/bug-9732.php new file mode 100644 index 0000000000..292649803f --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-9732.php @@ -0,0 +1,23 @@ + $array + * @phpstan-return \Generator + */ + public static function stringifyKeys(array $array) : \Generator{ + foreach($array as $key => $value){ + yield (string) $key => $value; + } + } + + public function sayHello(): void + { + self::stringifyKeys($GLOBALS); + } +}