Bug report
With a warm result cache, errors inside a trait body can silently disappear — the same code reports them fine right after clear-result-cache. So a cached run and a clean run disagree on identical source.
In practice it shows up through baselines: the dropped errors stop matching their ignoreErrors entries, and with reportUnmatchedIgnoredErrors on (the default) they become non-ignorable ignore.unmatched errors. So CI goes red with a warm cache and green after clearing it.
I narrowed the trigger down to three things happening together:
- a trait method body gains an error while its signature stays the same,
- the trait's file also declares a non-trait symbol (a class/enum/etc. next to the trait),
- the cache was warmed before the change and the classes that
use the trait didn't change themselves.
A file containing only the trait does not trigger it.
It seems to come from the trait optimization in ResultCacheManager (the continue 2 around line 436 in 2.2.2): when the changed file contains any non-trait node, the classes that use the trait are never re-queued, so their trait errors are lost. And since ignoreErrors doesn't invalidate the cache, a stale cache from before a refactor keeps being reused.
Code snippet that reproduces the problem
It's a result-cache bug (it needs two runs sharing a cache), so the playground can't show it. Minimal reproduction:
phpstan/phpstan 2.2.2 (also reproduces on 2.1.17+ and current 2.2.x), PHP 8.5, level 9, result cache enabled.
Expected output
A warm cache should report the same errors as a clean run — the trait's property.nonObject errors show up, the baseline matches, and no ignore.unmatched errors appear.
Bug report
With a warm result cache, errors inside a trait body can silently disappear — the same code reports them fine right after
clear-result-cache. So a cached run and a clean run disagree on identical source.In practice it shows up through baselines: the dropped errors stop matching their
ignoreErrorsentries, and withreportUnmatchedIgnoredErrorson (the default) they become non-ignorableignore.unmatchederrors. So CI goes red with a warm cache and green after clearing it.I narrowed the trigger down to three things happening together:
usethe trait didn't change themselves.A file containing only the trait does not trigger it.
It seems to come from the trait optimization in
ResultCacheManager(thecontinue 2around line 436 in 2.2.2): when the changed file contains any non-trait node, the classes thatusethe trait are never re-queued, so their trait errors are lost. And sinceignoreErrorsdoesn't invalidate the cache, a stale cache from before a refactor keeps being reused.Code snippet that reproduces the problem
It's a result-cache bug (it needs two runs sharing a cache), so the playground can't show it. Minimal reproduction:
composer install && ./reproduce.shphpstan/phpstan2.2.2 (also reproduces on 2.1.17+ and current2.2.x), PHP 8.5, level 9, result cache enabled.Expected output
A warm cache should report the same errors as a clean run — the trait's
property.nonObjecterrors show up, the baseline matches, and noignore.unmatchederrors appear.