Skip to content

Report unmatched baseline ignore rules when the origin trait file was analysed#5477

Open
sylfabre wants to merge 2 commits intophpstan:2.2.xfrom
sylfabre:fix_unmatched_ignored_errors
Open

Report unmatched baseline ignore rules when the origin trait file was analysed#5477
sylfabre wants to merge 2 commits intophpstan:2.2.xfrom
sylfabre:fix_unmatched_ignored_errors

Conversation

@sylfabre
Copy link
Copy Markdown

Problem

When reportUnmatchedIgnoredErrors: true and PHPStan runs in onlyFiles mode (incremental analysis), unmatched ignore rules with a specific path are silently suppressed — even when PHPStan has enough information to know the rule is genuinely unmatched.

Background

phpstan/phpstan#3099 reported false positives where unmatched path-specific ignore rules were reported even though the relevant file was never part of the analysis. This was fixed in c9df2f4 by skipping unmatched rules when the path was not in the analysed file set, and by unconditionally suppressing all path-specific unmatched rules in onlyFiles mode.

The unconditional suppression prevents a real false positive with trait errors: a trait error is attributed to the class file (Foo.php) but only surfaces when the trait file (FooTrait.php) is part of the analysis. In partial analysis without FooTrait.php, the baseline rule goes unmatched for a legitimate reason and must be suppressed.

However, this blanket suppression also means that when FooTrait.php was analysed and the rule still didn't match, the unmatched rule is silently swallowed — even though PHPStan has full information to report it correctly.

Solution

Introduce an origin field in baseline entries for errors that originate from a trait file (Error::getTraitFilePath()). This field records which file must be present in the analysis for the error to appear.

In IgnoredErrorHelperResult::process(), when an unmatched rule has a realOrigin:

  • If the origin file was not analysed → suppress (no false positive, behaviour preserved from c9df2f4)
  • If the origin file was analysed → report as unmatched (PHPStan has full information)

For rules without origin (user-written rules, non-trait baseline entries), the existing onlyFiles suppression is preserved unchanged.

Changes

  • BaselineNeonErrorFormatter / BaselinePhpErrorFormatter: emit origin for errors with a trait file path, grouped by (message, origin) so distinct trait sources produce separate entries
  • IgnoredErrorHelper: include origin in the deduplication key; normalise originrealOrigin alongside pathrealPath
  • IgnoredErrorHelperResult: check realOrigin against analysedFilesKeys before deciding whether to suppress an unmatched rule in onlyFiles mode
  • AnalyserTest: two new test cases — without origin (suppressed, existing behaviour preserved) and with origin where both files are analysed (reported, new behaviour)

@phpstan-bot
Copy link
Copy Markdown
Collaborator

You've opened the pull request against the latest branch 2.2.x. PHPStan 2.2 is not going to be released for months. If your code is relevant on 2.1.x and you want it to be released sooner, please rebase your pull request and change its target to 2.1.x.

@sylfabre sylfabre force-pushed the fix_unmatched_ignored_errors branch 3 times, most recently from 580698a to 3a59aa8 Compare April 15, 2026 10:11
…ed rules when origin is analysed

When an ignore rule has a specific path and an origin (the trait file where
the error actually originates), PHPStan can now determine whether the origin
file was part of the analysis. If both path and origin were analysed and the
rule was still unmatched, it is now reported — fixing a gap where unmatched
baseline entries were silently suppressed in onlyFiles mode even when
PHPStan had full information.

Baseline formatters (NEON and PHP) now emit an `origin` field for errors
that come from trait files, so baseline entries carry the necessary context
for this check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sylfabre sylfabre force-pushed the fix_unmatched_ignored_errors branch from 3a59aa8 to bf1517a Compare April 15, 2026 10:44
…atches

When an error message appears multiple times in the same file (e.g. once
directly and once from a trait), grouping by message+origin created separate
baseline entries that both tried to match the same errors, causing
'expected N, occurred M' count overflows.

Group by message only (restoring original behaviour). Track per-group
origins and emit 'origin' only when all errors in the group share the
same non-null trait file path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ondrejmirtes
Copy link
Copy Markdown
Member

This does not seem right.

even when PHPStan has enough information to know the rule is genuinely unmatched

It doesn't have enough information. Let's say you're ignoring something in FooTrait.php. You're running "partial" analysis, onlyFiles=true. There's an unmatched ignored error in FooTrait.php. But it only happens when you're analysing Bar.php (because the class uses that trait) which you're skipping here.

My rule of thumb: do not ever analyse only specific files when you can and should analyse the whole project: https://phpstan.org/blog/why-you-should-always-analyse-whole-project

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.

3 participants