diff --git a/src/Analyser/SpecifiedTypes.php b/src/Analyser/SpecifiedTypes.php index ccb2c5e8a3..5cfa65dc53 100644 --- a/src/Analyser/SpecifiedTypes.php +++ b/src/Analyser/SpecifiedTypes.php @@ -119,6 +119,21 @@ public function getRootExpr(): ?Expr return $this->rootExpr; } + public function removeExpr(string $exprString): self + { + $sureTypes = $this->sureTypes; + $sureNotTypes = $this->sureNotTypes; + unset($sureTypes[$exprString]); + unset($sureNotTypes[$exprString]); + + $self = new self($sureTypes, $sureNotTypes); + $self->overwrite = $this->overwrite; + $self->newConditionalExpressionHolders = $this->newConditionalExpressionHolders; + $self->rootExpr = $this->rootExpr; + + return $self; + } + /** @api */ public function intersectWith(SpecifiedTypes $other): self { diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 4f3e7f9c43..fff86a02fa 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -788,6 +788,9 @@ public function specifyTypesInCondition( if ($context->null()) { $specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->expr, $context)->setRootExpr($expr); + if ($expr->var instanceof Expr\Variable && is_string($expr->var->name)) { + $specifiedTypes = $specifiedTypes->removeExpr('$' . $expr->var->name); + } } else { $specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context)->setRootExpr($expr); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-11565.php b/tests/PHPStan/Analyser/nsrt/bug-11565.php new file mode 100644 index 0000000000..7e43068a80 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11565.php @@ -0,0 +1,36 @@ + $iterable + * @return ($iterable is list ? never : list) + */ +function iteratorToList(iterable $iterable): array { + $list = []; + foreach ($iterable as $item) { + $list[] = $item; + } + return $list; +} + +/** + * @return iterable + */ +function getItems(): iterable { + yield 'a' => 'foo'; + yield 'b' => 'bar'; +} + +// Bug: when reassigning to the same variable, conditional return type resolves incorrectly +$items = getItems(); +$items = iteratorToList($items); +assertType('list', $items); + +// Works fine when using a different variable +$x = getItems(); +$items2 = iteratorToList($x); +assertType('list', $items2);