Skip to content

Fix #12639: phpstan ignoring use paths in trait context#4966

Open
phpstan-bot wants to merge 1 commit into2.1.xfrom
create-pull-request/patch-cunugmg
Open

Fix #12639: phpstan ignoring use paths in trait context#4966
phpstan-bot wants to merge 1 commit into2.1.xfrom
create-pull-request/patch-cunugmg

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When a trait file contained a statement (e.g. if (...) die(...)) before use import declarations, PHPStan would ignore those use imports when resolving PHPDoc types on trait properties. This caused generic types like ObjectRefT<Account> to resolve Account against the consuming class's namespace instead of the trait's namespace, producing incorrect types.

Changes

  • Added condition && ($lookForTrait === null || $traitFound) to FileTypeMapper::createPhpDocNodeMap() to skip creating name scope map entries for statements that appear before the target trait during recursive trait file processing (src/Type/FileTypeMapper.php)
  • Added regression test testBug12639TraitPropertyPhpDocResolution in tests/PHPStan/Type/FileTypeMapperTest.php
  • Added test data files tests/PHPStan/Type/data/bug-12639-trait.php and tests/PHPStan/Type/data/bug-12639-class.php (separate-file trait with a statement before use declarations)
  • Added NSRT test tests/PHPStan/Analyser/nsrt/bug-12639.php for cross-namespace trait PHPDoc type resolution

Root cause

In FileTypeMapper::createPhpDocNodeMap(), when recursively processing a trait file ($lookForTrait !== null), the method traverses the entire AST. Any non-use/non-namespace statement (like if/die) encountered before the use declarations triggers creation of a $nameScopeMap entry with an empty $uses array. Because of the !array_key_exists($nameScopeKey, $nameScopeMap) guard, this premature entry prevents the correct entry (with populated $uses from the actual import declarations) from being created when the trait's properties are later processed.

The fix adds a check so that when processing a trait file recursively, name scope map entries are only created after the target trait has been found ($traitFound), skipping statements that precede it.

Test

The regression test in FileTypeMapperTest creates a trait file (bug-12639-trait.php) that has an if (true) {} statement before use declarations, and a class file (bug-12639-class.php) in a different namespace that uses the trait. The test calls FileTypeMapper::getResolvedPhpDoc() the same way PhpClassReflectionExtension does for trait properties, and verifies that ObjectRefT<Account> resolves to Bug12639Separate\Types\ObjectRefT<Bug12639Separate\Accounts\Account> (from the trait's use imports) rather than falling back to the consuming class's namespace.

An additional NSRT test verifies trait PHPDoc type resolution across namespaces in a single-file scenario.

Fixes phpstan/phpstan#12639

…eclarations

- When a trait file had a statement (e.g. `if/die`) before `use` declarations,
  FileTypeMapper::createPhpDocNodeMap() would create a premature name scope
  entry with empty `$uses` for non-trait statements during recursive trait
  processing, preventing the correct entry from being created later
- Added condition to skip name scope map entries for statements before the
  trait is found during recursive trait file processing ($lookForTrait)
- New regression tests in FileTypeMapperTest and NSRT for bug #12639

Closes phpstan/phpstan#12639
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.

1 participant

Comments