Context
Today the user has to choose how baseline violations are matched against the current run, via the --ignore-baseline-linenumbers / -i flag (and the corresponding ignoreBaselineLinenumbers() in the config file). This drives two separate matching code paths in Violations::remove() and Violations::countUnmatchedIn():
- with line numbers (default): a violation matches a baseline entry only if FQCN, line number, file path and the (stable part of the) error message match. Any change above the offending line shifts the line number and the check starts failing on an already-known violation.
- without line numbers (
-i): line numbers are dropped from the match, so unrelated edits don't reopen a baselined violation — but two violations of the same kind in the same class become indistinguishable.
Problem
The matching mechanism is an implementation detail, and it doesn't make sense that the user has to understand and choose it. It's not a decision about what to enforce (which is the user's job), it's a decision about how the baseline is compared (which is ours). The current situation:
- forces users to learn the trade-off between the two modes;
- makes baselines behave differently across projects/CI depending on a flag;
- the default (line-number-sensitive) produces false failures on unrelated edits, which is a common source of confusion.
Proposal
There should be a single matching strategy that works well for everyone, with no user-facing knob. The line-number sensitivity in particular should not be something the user opts into.
Approaches to evaluate:
- Hash of the line content instead of the line number. Anchoring a baseline entry to a hash of the offending line's source (optionally normalized for whitespace) makes matching stable against edits elsewhere in the file, while still distinguishing multiple violations of the same kind in the same class — which is exactly what the two current modes each fail at.
- Fuzzy line matching (nearest line with the same content) as a lighter-weight alternative.
- Keep FQCN + stable error key as the primary identity and use the line only as a tie-breaker.
Backwards compatibility
--ignore-baseline-linenumbers / ignoreBaselineLinenumbers() would be deprecated (kept as a no-op with a notice, then removed in a future major).
- Existing baseline files may need a one-time regeneration;
generate-baseline should produce the new format.
Related
Notes
Relevant code: src/Rules/Violations.php (remove(), countUnmatchedIn(), compareViolations(), extractViolationKey()), src/CLI/Baseline.php, and the option wiring in src/CLI/Command/CommonOptions.php.
Context
Today the user has to choose how baseline violations are matched against the current run, via the
--ignore-baseline-linenumbers/-iflag (and the correspondingignoreBaselineLinenumbers()in the config file). This drives two separate matching code paths inViolations::remove()andViolations::countUnmatchedIn():-i): line numbers are dropped from the match, so unrelated edits don't reopen a baselined violation — but two violations of the same kind in the same class become indistinguishable.Problem
The matching mechanism is an implementation detail, and it doesn't make sense that the user has to understand and choose it. It's not a decision about what to enforce (which is the user's job), it's a decision about how the baseline is compared (which is ours). The current situation:
Proposal
There should be a single matching strategy that works well for everyone, with no user-facing knob. The line-number sensitivity in particular should not be something the user opts into.
Approaches to evaluate:
Backwards compatibility
--ignore-baseline-linenumbers/ignoreBaselineLinenumbers()would be deprecated (kept as a no-op with a notice, then removed in a future major).generate-baselineshould produce the new format.Related
--ignore-baseline-linenumbersreporting the wrong violation as new. It explores the matching-identity solution space in depth (hash of source line, enclosing member, count-based grouping à la PHPStan/ESLint, git-diff à la golangci-lint). This issue is the DX counterpart: rather than fixing the flag's behaviour, it argues the flag (the user-facing choice) should not exist at all, and a single robust strategy should be the default for everyone. Whatever identity scheme --ignore-baseline-linenumbers can report the wrong violation as new #636 lands on is a strong candidate for that single strategy.prune-baselinecommand to remove fixed violations from the baseline #649 —prune-baselinecommand; touches the same area since pruning would refresh stale line numbers.Notes
Relevant code:
src/Rules/Violations.php(remove(),countUnmatchedIn(),compareViolations(),extractViolationKey()),src/CLI/Baseline.php, and the option wiring insrc/CLI/Command/CommonOptions.php.