Skip to content

Prevent duplicate rule ID suppression errors in -Fix and DSC scenarios#2181

Open
liamjpeters wants to merge 2 commits intoPowerShell:mainfrom
liamjpeters:DuplicateFixSuppressionError
Open

Prevent duplicate rule ID suppression errors in -Fix and DSC scenarios#2181
liamjpeters wants to merge 2 commits intoPowerShell:mainfrom
liamjpeters:DuplicateFixSuppressionError

Conversation

@liamjpeters
Copy link
Copy Markdown
Contributor

PR Summary

  • When using -Fix, only emit errors for unused rule suppressions IDs on the final pass - not intermediate Fix() passes.
  • Make the two DSC branches mutually exclusive (skip the module-layout branch when the class-based branch already handled the AST). Prevents multiple copies of unused rule suppression ID error.

Added regression tests covering these 2 scenarios.

Fixes #2177

PR Checklist

@liamjpeters liamjpeters requested review from a team and bergmeister as code owners April 28, 2026 19:01
Copy link
Copy Markdown
Member

@andyleejordan andyleejordan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up — the repro in #2177 was solid and this fix matches it cleanly.

For the -Fix case, threading emitSuppressionErrors: false through the Fix() loop's AnalyzeSyntaxTree / AnalyzeScriptDefinition calls is the right shape: every iteration inside the loop suppresses, the post-loop AnalyzeFile emits once, no duplicates. For the DSC case, swapping the second if to else if so the class-DSC and module-DSC handler branches are mutually exclusive is exactly the right minimal fix. Both regression tests pass locally for me.

LGTM — approving pending CI passing.

Drafted by Copilot (Claude Opus 4.7)

The `Library Usage` describe block in `LibraryUsage.tests.ps1` (only
active on Windows PowerShell 5.1, since it's gated `-Skip:$IsCoreCLR`)
re-runs `RuleSuppression.tests.ps1` against a hand-rolled
`Invoke-ScriptAnalyzer` wrapper that drives the analyzer as a .NET
library. That wrapper plugs in `PesterTestOutputWriter`, whose
`WriteError` is intentionally a no-op:

    public void WriteError(ErrorRecord error)
    {
        // We don't write errors to avoid misleading
        // error messages in test output
    }

So the unapplied-suppression `ErrorRecord` we now emit during the final
`-Fix` pass never reaches `-ErrorVariable`, and `$fixErr | Should
-HaveCount 1` fails with "Expected a collection with size 1, but got an
empty collection". The behaviour itself is correct - the assertion is
just unobservable through this test harness.

Mark the new `It` block `-Skip:$testingLibraryUsage`, matching the
existing pattern already used by the `Bad Rule Suppression` and
`External Rule Suppression` contexts in the same file for the same
reason. The regular pwsh and WinPS runs of `RuleSuppression.tests.ps1`
(which `[+]` in the failing CI log) continue to exercise the assertion.

The new `UseDSCResourceFunctions.tests.ps1` test isn't dot-sourced by
`LibraryUsage.tests.ps1`, so it doesn't need the same guard.

Drafted by Copilot (Claude Opus 4.7).
Copilot AI review requested due to automatic review settings May 4, 2026 20:40
@andyleejordan
Copy link
Copy Markdown
Member

@bergmeister I'll need your review since the super stringent code review requirements now bar me from merging after having Claude fix the failing test in CI.

@bergmeister
Copy link
Copy Markdown
Collaborator

@bergmeister I'll need your review since the super stringent code review requirements now bar me from merging after having Claude fix the failing test in CI.

Sure. Will look at this and others tomorrow

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR prevents duplicate “unapplied suppression” errors that can occur during -Fix reanalysis passes and in DSC scenarios where both class-based and module-layout DSC logic apply to the same file.

Changes:

  • Adds an emitSuppressionErrors switch to avoid emitting suppression errors during intermediate -Fix passes.
  • Makes DSC class-based vs module-layout analysis mutually exclusive to avoid duplicate suppression error emission.
  • Adds regression tests for -Fix reanalysis and mixed DSC layout/class scenarios.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
Tests/Rules/UseDSCResourceFunctions.tests.ps1 Adds regression test ensuring DSC suppression errors aren’t duplicated when both DSC detection paths apply.
Tests/Engine/RuleSuppression.tests.ps1 Adds regression test ensuring -Fix reanalysis doesn’t emit multiple unapplied suppression errors.
Engine/ScriptAnalyzer.cs Introduces emitSuppressionErrors plumbing and makes DSC analysis branches mutually exclusive to prevent duplicate suppression errors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Engine/ScriptAnalyzer.cs
Comment on lines +1453 to 1454
public List<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefinition, out ScriptBlockAst scriptAst, out Token[] scriptTokens, bool skipVariableAnalysis = false, bool emitSuppressionErrors = true)
{
Comment thread Engine/ScriptAnalyzer.cs
Comment on lines +2046 to 2048
bool skipVariableAnalysis = false,
bool emitSuppressionErrors = true)
{
Comment thread Engine/ScriptAnalyzer.cs
Comment on lines 1450 to 1451
/// <param name="scriptTokens">Parsed tokens of <paramref name="scriptDefinition"/.></param>
/// <param name="skipVariableAnalysis">Whether variable analysis can be skipped (applicable if rules do not use variable analysis APIs).</param>
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.

Duplicate "Cannot find any DiagnosticRecord with the Rule Suppression ID" errors with -Fix and class-based DSC resources

4 participants