Skip to content

Commit b80a609

Browse files
committed
Fix RuleSuppressionID not working with named arguments
Problem: SuppressMessageAttribute failed when using named arguments for RuleSuppressionID. Users could not use syntax like: [SuppressMessage("RuleName", RuleSuppressionId="MyId")] Root Cause: In RuleSuppression.cs, the named argument parser had two bugs: 1. Checked if RuleName was set instead of RuleSuppressionID 2. Assigned the value to RuleName instead of RuleSuppressionID This broke selective rule suppression for custom rules. Solution: - Fixed conflict check to validate RuleSuppressionID instead of RuleName - Fixed assignment to set RuleSuppressionID instead of RuleName - Added comprehensive tests for named argument syntax - Minor formatting improvements Now both syntaxes work correctly: [SuppressMessage("Rule", RuleSuppressionId="Id", Scope="Function")] [SuppressMessage("Rule", "Id", Scope="Function")]
1 parent a9898a6 commit b80a609

File tree

2 files changed

+73
-21
lines changed

2 files changed

+73
-21
lines changed

Engine/Generic/RuleSuppression.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,12 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
193193
}
194194
else if (argumentName.Equals("rulesuppressionid", StringComparison.OrdinalIgnoreCase))
195195
{
196-
if (!String.IsNullOrWhiteSpace(RuleName))
196+
if (!String.IsNullOrWhiteSpace(RuleSuppressionID))
197197
{
198198
Error = String.Format(Strings.NamedAndPositionalArgumentsConflictError, name);
199199
}
200200

201-
RuleName = (name.Argument as StringConstantExpressionAst).Value;
201+
RuleSuppressionID = (name.Argument as StringConstantExpressionAst).Value;
202202
}
203203
else if (argumentName.Equals("scope", StringComparison.OrdinalIgnoreCase))
204204
{
@@ -333,12 +333,12 @@ public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> at
333333
{
334334
targetAsts = scopeAst.FindAll(ast => ast is FunctionDefinitionAst && reg.IsMatch((ast as FunctionDefinitionAst).Name), true);
335335
}
336-
#if !(PSV3 || PSV4)
336+
#if !(PSV3 || PSV4)
337337
else if (scope.Equals("class", StringComparison.OrdinalIgnoreCase))
338338
{
339339
targetAsts = scopeAst.FindAll(ast => ast is TypeDefinitionAst && reg.IsMatch((ast as TypeDefinitionAst).Name), true);
340340
}
341-
#endif
341+
#endif
342342

343343
if (targetAsts != null)
344344
{

Tests/Engine/RuleSuppression.tests.ps1

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,28 @@ Describe "RuleSuppressionWithoutScope" {
5656

5757
It "Suppresses rule with extent created using ScriptExtent constructor" {
5858
Invoke-ScriptAnalyzer `
59-
-ScriptDefinition $ruleSuppressionAvoidUsernameAndPassword `
60-
-IncludeRule "PSAvoidUsingUserNameAndPassWordParams" `
61-
-OutVariable ruleViolations `
62-
-SuppressedOnly
59+
-ScriptDefinition $ruleSuppressionAvoidUsernameAndPassword `
60+
-IncludeRule "PSAvoidUsingUserNameAndPassWordParams" `
61+
-OutVariable ruleViolations `
62+
-SuppressedOnly
6363
$ruleViolations.Count | Should -Be 1
64-
}
64+
}
6565
}
6666

6767
Context "Script" {
6868
It "Does not raise violations" {
69-
$suppression = $violations | Where-Object {$_.RuleName -eq "PSProvideCommentHelp" }
69+
$suppression = $violations | Where-Object { $_.RuleName -eq "PSProvideCommentHelp" }
7070
$suppression.Count | Should -Be 0
71-
$suppression = $violationsUsingScriptDefinition | Where-Object {$_.RuleName -eq "PSProvideCommentHelp" }
71+
$suppression = $violationsUsingScriptDefinition | Where-Object { $_.RuleName -eq "PSProvideCommentHelp" }
7272
$suppression.Count | Should -Be 0
7373
}
7474
}
7575

7676
Context "RuleSuppressionID" {
7777
It "Only suppress violations for that ID" {
78-
$suppression = $violations | Where-Object {$_.RuleName -eq "PSAvoidDefaultValueForMandatoryParameter" }
78+
$suppression = $violations | Where-Object { $_.RuleName -eq "PSAvoidDefaultValueForMandatoryParameter" }
7979
$suppression.Count | Should -Be 1
80-
$suppression = $violationsUsingScriptDefinition | Where-Object {$_.RuleName -eq "PSAvoidDefaultValueForMandatoryParameter" }
80+
$suppression = $violationsUsingScriptDefinition | Where-Object { $_.RuleName -eq "PSAvoidDefaultValueForMandatoryParameter" }
8181
$suppression.Count | Should -Be 1
8282
}
8383

@@ -93,10 +93,10 @@ function SuppressPwdParam()
9393
}
9494
'@
9595
Invoke-ScriptAnalyzer `
96-
-ScriptDefinition $ruleSuppressionIdAvoidPlainTextPassword `
97-
-IncludeRule "PSAvoidUsingPlainTextForPassword" `
98-
-OutVariable ruleViolations `
99-
-SuppressedOnly
96+
-ScriptDefinition $ruleSuppressionIdAvoidPlainTextPassword `
97+
-IncludeRule "PSAvoidUsingPlainTextForPassword" `
98+
-OutVariable ruleViolations `
99+
-SuppressedOnly
100100
$ruleViolations.Count | Should -Be 1
101101
}
102102

@@ -246,8 +246,60 @@ function MyFunc
246246
}
247247
}
248248

249+
Context "RuleSuppressionID with named arguments" {
250+
It "Should work with named argument syntax" {
251+
$scriptWithNamedArgs = @'
252+
function SuppressPasswordParam()
253+
{
254+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(RuleName="PSAvoidUsingPlainTextForPassword", RuleSuppressionId="password1")]
255+
param(
256+
[string] $password1,
257+
[string] $password2
258+
)
259+
}
260+
'@
261+
262+
$diagnostics = Invoke-ScriptAnalyzer `
263+
-ScriptDefinition $scriptWithNamedArgs `
264+
-IncludeRule "PSAvoidUsingPlainTextForPassword"
265+
$suppressions = Invoke-ScriptAnalyzer `
266+
-ScriptDefinition $scriptWithNamedArgs `
267+
-IncludeRule "PSAvoidUsingPlainTextForPassword" `
268+
-SuppressedOnly
269+
270+
# There should be one unsuppressed diagnostic (password2) and one suppressed diagnostic (password1)
271+
$diagnostics | Should -HaveCount 1
272+
$diagnostics[0].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword"
273+
$diagnostics[0].RuleSuppressionID | Should -BeExactly "password2"
274+
275+
$suppressions | Should -HaveCount 1
276+
$suppressions[0].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword"
277+
$suppressions[0].RuleSuppressionID | Should -BeExactly "password1"
278+
}
279+
280+
It "Should work with mixed positional and named argument syntax" {
281+
$scriptWithMixedArgs = @'
282+
function SuppressPasswordParam()
283+
{
284+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", Scope="Function")]
285+
param(
286+
[string] $password1,
287+
[string] $password2
288+
)
289+
}
290+
'@
291+
292+
$diagnostics = Invoke-ScriptAnalyzer `
293+
-ScriptDefinition $scriptWithMixedArgs `
294+
-IncludeRule "PSAvoidUsingPlainTextForPassword"
295+
296+
# All violations should be suppressed since there's no RuleSuppressionID filtering
297+
$diagnostics | Should -HaveCount 0
298+
}
299+
}
300+
249301
Context "Rule suppression within DSC Configuration definition" {
250-
It "Suppresses rule" -skip:($IsLinux -or $IsMacOS -or ($PSVersionTable.PSVersion.Major -lt 5)) {
302+
It "Suppresses rule" -Skip:($IsLinux -or $IsMacOS -or ($PSVersionTable.PSVersion.Major -lt 5)) {
251303
$suppressedRule = Invoke-ScriptAnalyzer -ScriptDefinition $ruleSuppressionInConfiguration -SuppressedOnly
252304
$suppressedRule.Count | Should -Be 1
253305
}
@@ -281,9 +333,9 @@ function MyFunc
281333
Describe "RuleSuppressionWithScope" {
282334
Context "FunctionScope" {
283335
It "Does not raise violations" {
284-
$suppression = $violations | Where-Object {$_.RuleName -eq "PSAvoidUsingPositionalParameters" }
336+
$suppression = $violations | Where-Object { $_.RuleName -eq "PSAvoidUsingPositionalParameters" }
285337
$suppression.Count | Should -Be 0
286-
$suppression = $violationsUsingScriptDefinition | Where-Object {$_.RuleName -eq "PSAvoidUsingPositionalParameters" }
338+
$suppression = $violationsUsingScriptDefinition | Where-Object { $_.RuleName -eq "PSAvoidUsingPositionalParameters" }
287339
$suppression.Count | Should -Be 0
288340
}
289341
}
@@ -353,4 +405,4 @@ Describe "RuleSuppressionWithScope" {
353405
$suppressed.Count | Should -Be 1
354406
}
355407
}
356-
}
408+
}

0 commit comments

Comments
 (0)