Skip to content

Commit b2ae082

Browse files
authored
Allow for suppression of custom rules (#1145)
* Fix rule name lookup mismatch for custom rules to allow for suppresion and unify code * Remove note that custom rules suppression are not supported * fix CustomizedRule.tests.ps1 * do not change GetName method and add GetFullName method instead to avoid breaking existing functionality, therefore also revert the last change in test file * Add test and fix dummy test rule to return script extent. TODO: Fix suppressing custom rules when there is no Extent supplied * Make test custom rule return null extent again by fixing the Engine to be able to cope with it
1 parent dce4119 commit b2ae082

File tree

8 files changed

+39
-14
lines changed

8 files changed

+39
-14
lines changed

Engine/Generic/ExternalRule.cs

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System.Globalization;
5+
46
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
57
{
68
internal class ExternalRule : IExternalRule
@@ -20,6 +22,11 @@ public string GetName()
2022
return this.name;
2123
}
2224

25+
public string GetFullName()
26+
{
27+
return string.Format(CultureInfo.CurrentCulture, "{0}\\{1}", this.GetSourceName(), this.name);
28+
}
29+
2330
public string GetCommonName()
2431
{
2532
return this.commonName;

Engine/Generic/IExternalRule.cs

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
88
/// </summary>
99
internal interface IExternalRule : IRule
1010
{
11+
/// <summary>
12+
/// Adds module name (source name) to handle ducplicate function names in different modules.
13+
/// </summary>
14+
/// <returns></returns>
15+
string GetFullName();
16+
1117
/// <summary>
1218
/// GetParameter: Retrieves AstType parameter
1319
/// </summary>

Engine/Generic/RuleSuppression.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public string RuleName
6464
&& (ScriptAnalyzer.Instance.TokenRules != null
6565
&& ScriptAnalyzer.Instance.TokenRules.Count(item => String.Equals(item.GetName(), _ruleName, StringComparison.OrdinalIgnoreCase)) == 0)
6666
&& (ScriptAnalyzer.Instance.ExternalRules != null
67-
&& ScriptAnalyzer.Instance.ExternalRules.Count(item => String.Equals(item.GetName(), _ruleName, StringComparison.OrdinalIgnoreCase)) == 0)
67+
&& ScriptAnalyzer.Instance.ExternalRules.Count(item => String.Equals(item.GetFullName(), _ruleName, StringComparison.OrdinalIgnoreCase)) == 0)
6868
&& (ScriptAnalyzer.Instance.DSCResourceRules != null
6969
&& ScriptAnalyzer.Instance.DSCResourceRules.Count(item => String.Equals(item.GetName(), _ruleName, StringComparison.OrdinalIgnoreCase)) == 0))
7070
{

Engine/Helper.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1420,7 +1420,7 @@ public Tuple<List<SuppressedRecord>, List<DiagnosticRecord>> SuppressRule(
14201420
while (startRecord < diagnostics.Count
14211421
// && diagnostics[startRecord].Extent.StartOffset < ruleSuppression.StartOffset)
14221422
// && diagnostics[startRecord].Extent.StartLineNumber < ruleSuppression.st)
1423-
&& offsetArr[startRecord].Item1 < ruleSuppression.StartOffset)
1423+
&& offsetArr[startRecord] != null && offsetArr[startRecord].Item1 < ruleSuppression.StartOffset)
14241424
{
14251425
startRecord += 1;
14261426
}
@@ -1434,7 +1434,7 @@ public Tuple<List<SuppressedRecord>, List<DiagnosticRecord>> SuppressRule(
14341434
var curOffset = offsetArr[recordIndex];
14351435

14361436
//if (record.Extent.EndOffset > ruleSuppression.EndOffset)
1437-
if (curOffset.Item2 > ruleSuppression.EndOffset)
1437+
if (curOffset != null && curOffset.Item2 > ruleSuppression.EndOffset)
14381438
{
14391439
break;
14401440
}
@@ -1490,6 +1490,10 @@ private Tuple<int,int>[] GetOffsetArray(List<DiagnosticRecord> diagnostics)
14901490
for (int k = 0; k < diagnostics.Count; k++)
14911491
{
14921492
var ext = diagnostics[k].Extent;
1493+
if (ext == null)
1494+
{
1495+
continue;
1496+
}
14931497
if (ext.StartOffset == 0 && ext.EndOffset == 0)
14941498
{
14951499
// check if line and column number correspond to 0 offsets

Engine/ScriptAnalyzer.cs

+3-7
Original file line numberDiff line numberDiff line change
@@ -1204,9 +1204,7 @@ internal IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token,
12041204

12051205
// Adds command to run external analyzer rule, like
12061206
// Measure-CurlyBracket -ScriptBlockAst $ScriptBlockAst
1207-
// Adds module name (source name) to handle ducplicate function names in different modules.
1208-
string ruleName = string.Format("{0}\\{1}", rule.GetSourceName(), rule.GetName());
1209-
posh.Commands.AddCommand(ruleName);
1207+
posh.Commands.AddCommand(rule.GetFullName());
12101208
posh.Commands.AddParameter(rule.GetParameter(), token);
12111209

12121210
// Merges results because external analyzer rules may throw exceptions.
@@ -1234,9 +1232,7 @@ internal IEnumerable<DiagnosticRecord> GetExternalRecord(Ast ast, Token[] token,
12341232

12351233
// Adds command to run external analyzer rule, like
12361234
// Measure-CurlyBracket -ScriptBlockAst $ScriptBlockAst
1237-
// Adds module name (source name) to handle ducplicate function names in different modules.
1238-
string ruleName = string.Format("{0}\\{1}", rule.GetSourceName(), rule.GetName());
1239-
posh.Commands.AddCommand(ruleName);
1235+
posh.Commands.AddCommand(rule.GetFullName());
12401236
posh.Commands.AddParameter(rule.GetParameter(), childAst);
12411237

12421238
// Merges results because external analyzer rules may throw exceptions.
@@ -2276,7 +2272,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
22762272
{
22772273
if (IsRuleAllowed(exRule))
22782274
{
2279-
string ruleName = string.Format(CultureInfo.CurrentCulture, "{0}\\{1}", exRule.GetSourceName(), exRule.GetName());
2275+
string ruleName = exRule.GetFullName();
22802276
this.outputWriter.WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, ruleName));
22812277

22822278
// Ensure that any unhandled errors from Rules are converted to non-terminating errors

README.md

-2
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,6 @@ Suppress violation in `start-bar`, `start-baz` and `start-bam` but not in `start
307307
Param()
308308
```
309309

310-
**Note**: Rule suppression is currently supported only for built-in rules.
311-
312310
**Note**: Parser Errors cannot be suppressed via the `SuppressMessageAttribute`
313311

314312
[Back to ToC](#table-of-contents)

Tests/Engine/CustomizedRule.tests.ps1

+11
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ Describe "Test importing correct customized rules" {
160160
$violations[0].SuggestedCorrections.Text | Should -Be 'text'
161161
$violations[0].SuggestedCorrections.File | Should -Be 'filePath'
162162
$violations[0].SuggestedCorrections.Description | Should -Be 'description'
163+
}
164+
165+
It "can suppress custom rule" {
166+
$script = "[Diagnostics.CodeAnalysis.SuppressMessageAttribute('samplerule\$measure','')]Param()"
167+
$testScriptPath = "TestDrive:\SuppressedCustomRule.ps1"
168+
Set-Content -Path $testScriptPath -Value $script
169+
170+
$customizedRulePath = Invoke-ScriptAnalyzer -Path $testScriptPath -CustomizedRulePath $directory\samplerule\samplerule.psm1 |
171+
Where-Object { $_.Message -eq $message }
172+
173+
$customizedRulePath.Count | Should -Be 0
163174
}
164175

165176
if (!$testingLibraryUsage)

Tests/Engine/samplerule/samplerule.psm1

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ function Measure-RequiresRunAsAdministrator
2727
[ValidateNotNullOrEmpty()]
2828
[System.Management.Automation.Language.ScriptBlockAst]
2929
$testAst
30-
)
30+
)
31+
32+
# The implementation is mocked out for testing purposes only and many properties are deliberately set to null to test if PSSA can cope with it
3133
$l=(new-object System.Collections.ObjectModel.Collection["Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent"])
3234
$c = (new-object Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent 1,2,3,4,'text','filePath','description')
3335
$l.Add($c)
36+
$extent = $null
3437
$dr = New-Object `
3538
-Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" `
36-
-ArgumentList "This is help",$ast.Extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null,$null,$l
39+
-ArgumentList "This is help",$extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null,$null,$l
3740
return $dr
3841
}
3942
Export-ModuleMember -Function Measure*

0 commit comments

Comments
 (0)