Skip to content

Commit 3bfb674

Browse files
committed
Fix phpstan/phpstan#8985: Expression result remembered on new()
- Added expressionHasNewInChain() check in MutatingScope::resolveType() to skip stored expression type lookup when the expression's receiver chain contains a New_ node - New regression test in tests/PHPStan/Analyser/nsrt/bug-8985.php - The root cause was that (new Foo())->method() produced the same expression key regardless of source location, so type narrowing from assert() on one new instance incorrectly applied to subsequent ones
1 parent 5ab83ce commit 3bfb674

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ private function resolveType(string $exprString, Expr $node): Type
973973
!$node instanceof Variable
974974
&& !$node instanceof Expr\Closure
975975
&& !$node instanceof Expr\ArrowFunction
976+
&& !$this->expressionHasNewInChain($node)
976977
&& $this->hasExpressionType($node)->yes()
977978
) {
978979
return $this->expressionTypes[$exprString]->getType();
@@ -990,6 +991,17 @@ private function resolveType(string $exprString, Expr $node): Type
990991
return new MixedType();
991992
}
992993

994+
private function expressionHasNewInChain(Expr $expr): bool
995+
{
996+
if ($expr instanceof MethodCall || $expr instanceof PropertyFetch || $expr instanceof Expr\NullsafeMethodCall || $expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\ArrayDimFetch) {
997+
return $expr->var instanceof Expr\New_ || $this->expressionHasNewInChain($expr->var);
998+
}
999+
if (($expr instanceof Expr\StaticCall || $expr instanceof Expr\StaticPropertyFetch || $expr instanceof Expr\ClassConstFetch) && $expr->class instanceof Expr) {
1000+
return $expr->class instanceof Expr\New_ || $this->expressionHasNewInChain($expr->class);
1001+
}
1002+
return false;
1003+
}
1004+
9931005
/**
9941006
* @param callable(Type): ?bool $typeCallback
9951007
*/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug8985;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
class Entity
10+
{
11+
public function __construct(private string $value)
12+
{
13+
}
14+
15+
public function getValue(): string
16+
{
17+
return $this->value;
18+
}
19+
}
20+
21+
class Repository
22+
{
23+
/** @return array<int, Entity> */
24+
public function getAll(): array
25+
{
26+
return [new Entity('test')];
27+
}
28+
}
29+
30+
function () : void {
31+
assert((new Repository())->getAll() === []);
32+
33+
$all = (new Repository())->getAll();
34+
assertType('array<int, Bug8985\Entity>', $all);
35+
$value = $all[0]->getValue();
36+
};

0 commit comments

Comments
 (0)