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 3 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 3 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)


Note

Medium Risk
Touches core mutation-tracking logic for traits; while the change is a small guard, it can alter analysis behavior by silently skipping unscanned traits and should be validated against real-world projects.

Overview
Prevents ClassLikeAnalyzer::getMethodMutations() from throwing when encountering a TraitUse whose ClassLikeStorage is missing by adding a classlike_storage_provider->has() guard and skipping that trait.

Adds a regression test in IncludeTest that simulates a trait registered via reflection but absent from storage (by removing it from ClassLikeStorageProvider::$storage) and asserts analyzing a child class no longer crashes.

Reviewed by Cursor Bugbot for commit deff79d. Bugbot is set up for automated code reviews on this repo. Configure here.

… 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