Skip to content

Commit 5e2127c

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 03834ec commit 5e2127c

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
@@ -971,6 +971,7 @@ private function resolveType(string $exprString, Expr $node): Type
971971
!$node instanceof Variable
972972
&& !$node instanceof Expr\Closure
973973
&& !$node instanceof Expr\ArrowFunction
974+
&& !$this->expressionHasNewInChain($node)
974975
&& $this->hasExpressionType($node)->yes()
975976
) {
976977
return $this->expressionTypes[$exprString]->getType();
@@ -988,6 +989,17 @@ private function resolveType(string $exprString, Expr $node): Type
988989
return new MixedType();
989990
}
990991

992+
private function expressionHasNewInChain(Expr $expr): bool
993+
{
994+
if ($expr instanceof MethodCall || $expr instanceof PropertyFetch || $expr instanceof Expr\NullsafeMethodCall || $expr instanceof Expr\NullsafePropertyFetch || $expr instanceof Expr\ArrayDimFetch) {
995+
return $expr->var instanceof Expr\New_ || $this->expressionHasNewInChain($expr->var);
996+
}
997+
if (($expr instanceof Expr\StaticCall || $expr instanceof Expr\StaticPropertyFetch || $expr instanceof Expr\ClassConstFetch) && $expr->class instanceof Expr) {
998+
return $expr->class instanceof Expr\New_ || $this->expressionHasNewInChain($expr->class);
999+
}
1000+
return false;
1001+
}
1002+
9911003
/**
9921004
* @param callable(Type): ?bool $typeCallback
9931005
*/
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)