Skip to content

[6.x] Skip trait in getMethodMutations when storage is missing (fixes "Could not get class storage")#11789

Open
alies-dev wants to merge 2 commits intovimeo:6.xfrom
alies-dev:fix/trait-storage-missing-crash-v2
Open

[6.x] Skip trait in getMethodMutations when storage is missing (fixes "Could not get class storage")#11789
alies-dev wants to merge 2 commits intovimeo:6.xfrom
alies-dev:fix/trait-storage-missing-crash-v2

Conversation

@alies-dev
Copy link
Copy Markdown
Contributor

@alies-dev alies-dev commented Apr 6, 2026

Fixes #8953

Psalm 6.x crashes with InvalidArgumentException: Could not get class storage for illuminate\console\concerns\promptsformissinginput when analyzing Laravel projects containing classes that extend Illuminate\Console\Command.

Root cause: ClassLikeAnalyzer::getMethodMutations() iterates AST TraitUse nodes unconditionally. A trait may be registered via reflection (present in existing_traits) but absent from ClassLikeStorageProvider::$storage. The call to getTraitNode()classlike_storage_provider->get() throws InvalidArgumentException.
Fix: Add a 4-line has() guard before accessing trait storage, same pattern as the interface fix in 03037f7.
Real-world trigger: Any Laravel class extending Illuminate\Console\Command (which uses PromptsForMissingInput trait) with an uninitialized typed property that triggers collect_initializationsgetMethodMutations → crash.

Stack Trace

InvalidArgumentException: Could not get class storage for illuminate\console\concerns\promptsformissinginput
  at src/Psalm/Internal/Provider/ClassLikeStorageProvider.php:45
  ClassLikes.php:771       → getTraitNode("Illuminate\Console\Concerns\PromptsForMissingInput")
  ClassLikeAnalyzer.php:141 → getTraitNode(...)        ← CRASH SITE
  FileAnalyzer.php:409     → getMethodMutations(...)
  ProjectAnalyzer.php:1205 → getMethodMutations(...)
  FileAnalyzer.php:372     → analyzing ExportMakeCommand.php

Test

php vendor/bin/phpunit tests/IncludeTest.php --filter=testGetMethodMutationsDoesNotCrashWhenTraitStorageMissing

It failed with:

InvalidArgumentException: Could not get class storage for foo\mytrait
  at src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php:137

The stack trace matched the expected crash path through ClassLikeAnalyzer::getMethodMutations()ClassLikeStorageProvider::get().

Notes

Follows the same guard pattern as the interface fix in 03037f7 (#11760)

… missing

Demonstrates the crash in ClassLikeAnalyzer::getMethodMutations() when
a trait is registered via reflection (present in existing_traits) but
has no entry in ClassLikeStorageProvider::$storage.

Real-world trigger: any Laravel class extending Illuminate\Console\Command,
which uses the PromptsForMissingInput trait.
ClassLikeAnalyzer::getMethodMutations() iterates AST TraitUse nodes
unconditionally, but a trait may be registered via reflection (present
in existing_traits) without having an entry in
ClassLikeStorageProvider::$storage. This causes an
InvalidArgumentException crash with zero JSON output.

Add a has() guard before accessing trait storage, matching the pattern
used in the interface fix (03037f7).

Real-world trigger: Laravel projects using Illuminate\Console\Command,
which uses the PromptsForMissingInput trait. Any class extending
Command with an uninitialized typed property triggers
collect_initializations → getMethodMutations → crash.

Relates to vimeo#11006, vimeo#9306, vimeo#8953
@alies-dev alies-dev changed the title fix(analyzer): skip trait in getMethodMutations when storage is missing [6.x] Fix analyzer: skip trait in getMethodMutations when storage is missing (fixes "Could not get class storage") Apr 6, 2026
@alies-dev alies-dev changed the title [6.x] Fix analyzer: skip trait in getMethodMutations when storage is missing (fixes "Could not get class storage") [6.x] Skip trait in getMethodMutations when storage is missing (fixes "Could not get class storage") Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant