diff --git a/src/Type/Generic/TemplateTypeHelper.php b/src/Type/Generic/TemplateTypeHelper.php index 6f09c29ccb..48ac6f2159 100644 --- a/src/Type/Generic/TemplateTypeHelper.php +++ b/src/Type/Generic/TemplateTypeHelper.php @@ -142,9 +142,7 @@ public static function generalizeInferredTemplateType(TemplateType $templateType { if (!$templateType->getVariance()->covariant()) { $isArrayKey = $templateType->getBound()->describe(VerbosityLevel::precise()) === '(int|string)'; - if ($type->isScalar()->yes() && $isArrayKey) { - $type = $type->generalize(GeneralizePrecision::templateArgument()); - } elseif ($type->isConstantValue()->yes() && (!$templateType->getBound()->isScalar()->yes() || $isArrayKey)) { + if ($type->isConstantValue()->yes() && ($isArrayKey || !$templateType->getBound()->isScalar()->yes())) { $type = $type->generalize(GeneralizePrecision::templateArgument()); } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-12601.php b/tests/PHPStan/Analyser/nsrt/bug-12601.php new file mode 100644 index 0000000000..5b1bde9f73 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12601.php @@ -0,0 +1,54 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug12601; + +use ArrayIterator; +use IteratorAggregate; +use Traversable; +use function PHPStan\Testing\assertType; + +/** @implements IteratorAggregate */ +class HelloWorld implements IteratorAggregate +{ + /** @param array $map */ + public function __construct(private array $map) {} + + /** @return Traversable */ + public function getIterator(): Traversable + { + $iterator = new ArrayIterator($this->map); + assertType('ArrayIterator', $iterator); + return $iterator; + } +} + +class HelloWorld3 +{ + /** @var ArrayIterator */ + private ArrayIterator $a; + + /** @param list $map */ + public function __construct(private array $map) { + $a = new ArrayIterator($this->map); + assertType('ArrayIterator, string>', $a); + + $this->a = $a; + } +} + +/** @implements IteratorAggregate, non-empty-string> */ +class HelloWorld2 implements IteratorAggregate +{ + /** @param array, non-empty-string> $map */ + public function __construct(private array $map) {} + + /** @return ArrayIterator, non-empty-string> */ + public function getIterator(): Traversable + { + $iterator = new ArrayIterator($this->map); + assertType('ArrayIterator, non-empty-string>', $iterator); + return $iterator; + } +} diff --git a/tests/PHPStan/Analyser/nsrt/generics-do-not-generalize.php b/tests/PHPStan/Analyser/nsrt/generics-do-not-generalize.php index d00b8b699a..67b1a40ee8 100644 --- a/tests/PHPStan/Analyser/nsrt/generics-do-not-generalize.php +++ b/tests/PHPStan/Analyser/nsrt/generics-do-not-generalize.php @@ -97,7 +97,7 @@ function (): void { /** @var list $a */ $a = doFoo(); - assertType('ArrayIterator', new ArrayIterator($a)); + assertType('ArrayIterator, string>', new ArrayIterator($a)); }; /** @@ -115,7 +115,7 @@ function (): void { /** @var list $a */ $a = doFoo(); - assertType('ArrayIterator', createArrayIterator($a)); + assertType('ArrayIterator, string>', createArrayIterator($a)); }; /** @template T */