Skip to content

Report unmatched ignoreErrors entries of the analysed files when analysing only files - #7047

Open
ondrejmirtes wants to merge 2 commits into
2.3.xfrom
report-unmatched-ignores-in-only-files-runs
Open

ondrejmirtes wants to merge 2 commits into
2.3.xfrom
report-unmatched-ignores-in-only-files-runs

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #7044, #7045 and #7046. Two changes for a partial analysis:

Unmatched entries when analysing only files

When only files were analysed (a list of files on the command line, like git hooks or editor integrations pass), unmatched ignoreErrors entries with a path were never reported. That gate was added in c9df2f4 for an error in a trait, which is reported in the file of the class using it only when the trait is analysed too. #7046 handles that for every partial analysis, so the gate goes, and UnmatchedIgnoredErrorJudge decides for a list of files too:

  • An entry for an analysed file is reported when the file is analysed together with everything it depends on (below), including a count that is too high.
  • Entries without a path, directory and pattern entries are reported only when the analysed files cover them, so in practice not for a list of files.
  • Without paths in the configuration, the analysed directories are taken as the whole project (Report unmatched ignoreErrors entries with a directory or pattern path only under the analysed paths #7044), but a list of files is only a part of an unknown one. Every dependency that isn't analysed counts against a file then, even one from vendor.

This means only-files and editor runs now report unmatched entries of the files they analyse. That's what makes --fix-ignored with a list of files remove the baseline entries of the files it fixed.

What the errors of a file depend on: DependencyTracker::trackAnalysedFileDependency()

Rules on CollectedDataNode report errors in a file based on data collected in other files. CallToFunctionStatementWithoutImpurePointsRule and its siblings report a call only when the declaration of the called function, and of everything it calls, is analysed too. So analyse src/Calls reported the function.resultUnused entry of a call to a function declared elsewhere as unmatched, in a directory run as well as in a list of files, and --fix removed it.

DependencyTracker gets two methods, so that PHPStan and third-party collectors can say what the errors of the analysed file depend on:

  • trackAnalysedFileDependency(string $file): the errors of the current file depend on $file being analysed too. The dependencies are followed transitively. A file that isn't analysed even in the whole project (outside the configured paths, excludePaths: analyse:) doesn't count.
  • trackAnalysedProjectDependency(): they depend on the whole project.

The dependencies are emitted as collected data (AnalysedFileDependencyCollector, a CollectorWithPaths), so they're kept in the result cache with relative paths. Outside the analysis of a file, as in a rule on CollectedDataNode, both do nothing. DependencyTracker, DeclarationDependencyTracker, NodeCallbackInvoker and CollectedDataEmitter are now @api-do-not-implement, which allows adding the methods. Both are mirrored in the turbo extension's MutatingScope.

Callers:

Documentation: phpstan/phpstan#15711

When a rule on collected data skips its errors because only files are analysed, nothing is reported (#7045). PHPStan's own NotAnalysedTraitRule does that whenever an analysed file declares a trait, so on level 4 and up, a list of files that includes a trait file reports no unmatched entries.

Tests

  • e2e/bug-15659: case 1 of the issue as reported, directory runs and --fix --fix-ignored lib; fails before Report unmatched ignoreErrors entries with a directory or pattern path only under the analysed paths #7044
  • e2e/ignore-errors-partial-analysis/only-files_test.sh: entries and counts of the analysed files, entries of other files, directories, patterns and global entries, files with traits, a file declaring a trait
  • collected-data-dependencies_test.sh: function.resultUnused with the declaration analysed or not, transitively pure functions, a list of files, the result cache, and the named-argument rule
  • custom-dependencies_test.sh: a third-party collector, and a third-party rule emitting collected data, both tracking the dependency
  • no-config-paths_test.sh: a list of files without paths in the configuration
  • e2e/fix-ignored-entries: --fix-ignored with a list of files

On 2.3.x, 12 of the new tests fail; e2e/bug-15659 passes there, as #7044 and #7046 fixed case 1, and fails before #7044.

Closes phpstan/phpstan#14480
Closes phpstan/phpstan#15704
Closes phpstan/phpstan#15659

…ysing only files

When only files are analysed, unmatched entries with a path were never
reported. UnmatchedIgnoredErrorJudge already decides which entries can be
judged in a partial analysis, so it now decides for a list of files too:
an entry for an analysed file is reported when the file is analysed
together with its traits, and a count that is too high is reported for it.
Entries without a path, and directory and pattern entries, are reported
only when the analysed files cover them.

Without paths in the configuration, the analysed directories are taken as
the whole project, but a list of files is only a part of an unknown one,
so every trait that is not analysed counts against a file then.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba2c-2885-703b-90d3-0df9bf93b430
Rules on CollectedDataNode report errors in a file based on data collected
in other files, like a call to a function that is pure only when its
declaration is analysed too. In a partial analysis, the ignoreErrors
entries of such a file were reported as unmatched, and removed by --fix.

DependencyTracker::trackAnalysedFileDependency() declares that the errors
of the analysed file depend on another file being analysed too, and
trackAnalysedProjectDependency() that they depend on the whole project.
They are emitted as collected data, so the result cache keeps them, and
UnmatchedIgnoredErrorJudge follows them transitively. The collectors of
possibly pure calls, the trait files of classes and calls with named
arguments track them. DependencyTracker, DeclarationDependencyTracker,
NodeCallbackInvoker and CollectedDataEmitter are @api-do-not-implement.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1ba2c-2885-703b-90d3-0df9bf93b430
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