Skip to content

Fix phpstan/phpstan#8985: Expression result remembered on new()#5449

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-r89jstk
Open

Fix phpstan/phpstan#8985: Expression result remembered on new()#5449
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-r89jstk

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When assert((new Repository())->getAll() === []) narrowed the type of the method call result, the narrowed type array{} was incorrectly remembered for subsequent (new Repository())->getAll() calls, even though each new Repository() creates a fresh object. This caused a false positive "Offset 0 does not exist on array{}".

Changes

  • Added expressionHasNewInChain() helper method in src/Analyser/MutatingScope.php that checks whether an expression's receiver chain contains a New_ node
  • Modified resolveType() in src/Analyser/MutatingScope.php to skip the stored expression type lookup when the expression has new in its receiver chain
  • The check specifically targets method calls, property fetches, array dim fetches, static calls, etc. where the root receiver is a new expression -- it does NOT skip the lookup for new Foo() itself, preserving @var annotation support on new expressions

Root cause

PHPStan uses expression string keys (like (new \Foo())->getAll()) to store and retrieve narrowed types in MutatingScope::expressionTypes. When assert() narrows a type, it stores the narrowed type under this key. The problem is that two different new Foo() AST nodes at different source locations produce the same string key, so the narrowed type from one assert incorrectly applies to a completely different new instantiation.

The fix skips the expression type lookup for expressions whose receiver chain contains new, since each new creates a fresh object and narrowed types from one instance should not apply to another.

Test

Added tests/PHPStan/Analyser/nsrt/bug-8985.php - an NSRT test that verifies (new Repository())->getAll() returns array<int, Entity> even after a prior assert((new Repository())->getAll() === []).

Fixes phpstan/phpstan#8985

- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants