diff --git a/src/Type/BenevolentUnionType.php b/src/Type/BenevolentUnionType.php index bb4a47761b..d6b43fbd85 100644 --- a/src/Type/BenevolentUnionType.php +++ b/src/Type/BenevolentUnionType.php @@ -19,6 +19,16 @@ public function __construct(array $types, bool $normalized = false) parent::__construct($types, $normalized); } + public function filterTypes(callable $filterCb): Type + { + $result = parent::filterTypes($filterCb); + if (!$result instanceof self && $result instanceof UnionType) { + return TypeUtils::toBenevolentUnion($result); + } + + return $result; + } + public function describe(VerbosityLevel $level): string { return '(' . parent::describe($level) . ')'; diff --git a/src/Type/Generic/TemplateBenevolentUnionType.php b/src/Type/Generic/TemplateBenevolentUnionType.php index cc630fd0dd..aea8573131 100644 --- a/src/Type/Generic/TemplateBenevolentUnionType.php +++ b/src/Type/Generic/TemplateBenevolentUnionType.php @@ -47,4 +47,21 @@ public function withTypes(array $types): self ); } + public function filterTypes(callable $filterCb): Type + { + $result = parent::filterTypes($filterCb); + if (!$result instanceof TemplateType) { + return TemplateTypeFactory::create( + $this->getScope(), + $this->getName(), + $result, + $this->getVariance(), + $this->getStrategy(), + $this->getDefault(), + ); + } + + return $result; + } + } diff --git a/src/Type/Generic/TemplateUnionType.php b/src/Type/Generic/TemplateUnionType.php index cc196a07f4..dc58af565a 100644 --- a/src/Type/Generic/TemplateUnionType.php +++ b/src/Type/Generic/TemplateUnionType.php @@ -34,4 +34,21 @@ public function __construct( $this->default = $default; } + public function filterTypes(callable $filterCb): Type + { + $result = parent::filterTypes($filterCb); + if (!$result instanceof TemplateType) { + return TemplateTypeFactory::create( + $this->getScope(), + $this->getName(), + $result, + $this->getVariance(), + $this->getStrategy(), + $this->getDefault(), + ); + } + + return $result; + } + } diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index b7e794378a..f0bef45903 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -92,14 +92,20 @@ public function getTypes(): array public function filterTypes(callable $filterCb): Type { $newTypes = []; + $changed = false; foreach ($this->getTypes() as $innerType) { if (!$filterCb($innerType)) { + $changed = true; continue; } $newTypes[] = $innerType; } + if (!$changed) { + return $this; + } + return TypeCombinator::union(...$newTypes); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-6609-83.php b/tests/PHPStan/Analyser/nsrt/bug-6609-83.php index 65d7f22f1d..4a5f5bb781 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-6609-83.php +++ b/tests/PHPStan/Analyser/nsrt/bug-6609-83.php @@ -50,7 +50,7 @@ function modify2(\DateTimeInterface $date) { */ function modify3(\DateTimeInterface $date, string $s) { $date = $date->modify($s); - assertType('DateTime|DateTimeImmutable', $date); + assertType('T of DateTime|DateTimeImmutable (method Bug6609Php83\Foo::modify3(), argument)', $date); return $date; } diff --git a/tests/PHPStan/Analyser/nsrt/bug-6609.php b/tests/PHPStan/Analyser/nsrt/bug-6609.php index 046f9c8403..571f97d988 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-6609.php +++ b/tests/PHPStan/Analyser/nsrt/bug-6609.php @@ -50,7 +50,7 @@ function modify2(\DateTimeInterface $date) { */ function modify3(\DateTimeInterface $date, string $s) { $date = $date->modify($s); - assertType('(DateTime|DateTimeImmutable|false)', $date); + assertType('((T of DateTime|DateTimeImmutable (method Bug6609\Foo::modify3(), argument))|false)', $date); return $date; } diff --git a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php index 6d374a6f1c..fcbc0643c9 100644 --- a/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php @@ -1054,4 +1054,9 @@ public function testBug10715(): void $this->analyse([__DIR__ . '/data/bug-10715.php'], []); } + public function testBug11663(): void + { + $this->analyse([__DIR__ . '/data/bug-11663.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-11663.php b/tests/PHPStan/Rules/Methods/data/bug-11663.php new file mode 100644 index 0000000000..ac2ed03a93 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-11663.php @@ -0,0 +1,79 @@ +where('test'); + } + + /** + * @param __benevolent $template + * @return __benevolent + */ + public function test2($template) + { + return $template->where('test'); + } + + + /** + * @template T of A|B + * @param T $ab + * @return T + */ + function foo(A|B $ab): A|B + { + return $ab->doFoo(); + } + + /** + * @template T of __benevolent + * @param T $ab + * @return T + */ + function foo2(A|B $ab): A|B + { + return $ab->doFoo(); + } +}