Skip to content

Re-analyse files with errors of rules on collected data when a symbol appears - #6896

Merged
ondrejmirtes merged 1 commit into
2.3.xfrom
fix-collector-sweep
Nov 1, 2026
Merged

ondrejmirtes merged 1 commit into
2.3.xfrom
fix-collector-sweep

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Summary

When a new symbol appears, ResultCacheManager::restore() re-analyses the files in the cached error map. That's how the cache recovers an error that could record no dependency, because the symbol it is about did not exist yet. Errors of rules on CollectedDataNode are never in that map: those rules re-run on the merged collected data every time, so a cached copy would be reported twice. A file whose only error came from such a rule was never re-analysed. It kept the error after the symbol arrived, while a run with a cleared cache reported nothing.

The collectedDataRuleResults already saved after the finalizer don't cover this, because they're only stored with the reuseCollectedDataRuleErrors toggle (bleeding edge). Without the toggle, the finalizer deliberately behaves as before.

The fix

  • The cache keeps a new filesWithCollectedDataRuleErrors list. It's built from FinalizerResult::getCollectorErrors() on every save, whatever the toggle.
  • restore() treats those files like files in the error map. They're re-analysed when a symbol appears, and they count towards the "every file with errors changed anyway" shortcut.
  • ResultCacheProcessResult::save() takes the FinalizerResult instead of only the rule results.
  • CACHE_VERSION is bumped.

Test

This includes the E2E fixture from #6358 (e2e/result-cache-collector-sweep). Before this change the second run reported Caller.php:7 function.resultUnused and exited 1. Now it re-analyses Caller.php and exits 0. Deleting the new file brings both errors back. I also checked the same scenario with bleeding edge: the affected rule re-runs and the others are reused. Warm no-op runs still skip rewriting the cache.

Closes phpstan/phpstan#15162
Supersedes #6358

… appears

Rules on CollectedDataNode run in AnalyserResultFinalizer, and their errors
are not in the cached error map: they re-run on the merged collected data on
every run, so a cached copy would be reported twice. That map is what
restore() sweeps when a new symbol appears - the recovery for an error that
could record no dependency because the symbol it is about did not exist yet.
A file whose only error came from collected data was never swept, so it kept
the error after the symbol arrived, while a run with a cleared cache reported
nothing. The rule results stored for reuseCollectedDataRuleErrors do not cover
this: they are only stored with that toggle on.

The cache now keeps the files with errors of these rules, taken from the
finalizer result on every save regardless of the toggle, and restore() treats
them like the files in the error map.

Amp-Thread-ID: https://ampcode.com/threads/T-01a19695-f1c1-75fa-ac1f-125f79f272c7
@ondrejmirtes
ondrejmirtes merged commit 524520f into 2.3.x Nov 1, 2026
492 of 493 checks passed
@ondrejmirtes
ondrejmirtes deleted the fix-collector-sweep branch November 1, 2026 10:30
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.

Result cache keeps an error from a rule built on collected data after the symbol appears

1 participant