Bulk suppressions: a same-count swap in one file+rule bucket is invisible, and --prune-suppressions doesn't catch it
ESLint version
v10.8.1
What problem do you want to solve?
The suppressions file (eslint-suppressions.json) records a count per (file, rule), not per specific violation. The docs already say this: suppressions are tracked per file and rule, not per line or code change. In practice that means one specific kind of change is invisible to both a normal lint run and to --prune-suppressions.
I reproduced it locally with ESLint 10.8.1. Two real no-unused-vars violations in one file (alpha, beta), suppressed with --suppress-all, giving count: 2 for that file+rule. Then I fixed alpha for real and introduced a different, unrelated no-unused-vars violation in the same file (gamma). beta was still there, unfixed. Total violations stayed at 2.
Running eslint with the suppressions file in place: clean, exit 0, nothing reported. Running eslint --prune-suppressions: no change to the file, no warning, nothing to prune, because the bucket is still exactly full at count 2, just covering a different combination of lines than before.
So a real fix and a real regression of the same rule, in the same file, cancel out completely, and there's no command in the tool, not even the one meant for auditing the suppressions file, that surfaces it.
What do you think is the correct solution?
I don't think per-line tracking forever is the right ask, that's a much bigger design change and probably not worth it for what's meant to be a lightweight bulk-suppression tool. But --prune-suppressions already has to re-run the lint and compare against the suppressions file to decide what's stale. It could also flag, even just as a warning, buckets where the count still matches but the specific occurrences it's covering shifted (the file changed in a way that touches that rule, even though the recorded count didn't move). That wouldn't require storing per-line identity long-term, just a diff at prune time between the file's current state and its state when the suppression was last generated or pruned.
Short of that, it would help to say explicitly in the docs that --prune-suppressions won't catch this case, since the only caveat mentioned right now is about tracking granularity, not about what the audit command itself can and can't detect.
Participation
- I am willing to submit a pull request for this change.
AI acknowledgment
- I did not use AI to generate this issue report.
- (If the above is not checked) I have reviewed the AI-generated content before submitting.
Additional comments
For context: I ran into this while looking at how a few different tools' baseline/ignore mechanisms handle the same underlying problem, a permitted-violations file that isn't tied to specific instances. Wrote it up more formally here if it's useful: https://doi.org/10.5281/zenodo.21908527
Source: eslint/eslint