Skip to content

Commit d1942a1

Browse files
authored
Implement -IncludeSuppressions parameter (#1701)
1 parent 9112135 commit d1942a1

25 files changed

+319
-155
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

+67-42
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands
2121
/// </summary>
2222
[Cmdlet(VerbsLifecycle.Invoke,
2323
"ScriptAnalyzer",
24-
DefaultParameterSetName = "File",
24+
DefaultParameterSetName = ParameterSet_Path_SuppressedOnly,
2525
SupportsShouldProcess = true,
2626
HelpUri = "https://go.microsoft.com/fwlink/?LinkId=525914")]
27-
[OutputType(typeof(DiagnosticRecord))]
28-
[OutputType(typeof(SuppressedRecord))]
27+
[OutputType(typeof(DiagnosticRecord), typeof(SuppressedRecord))]
2928
public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter
3029
{
30+
private const string ParameterSet_Path_SuppressedOnly = nameof(Path) + "_" + nameof(SuppressedOnly);
31+
private const string ParameterSet_Path_IncludeSuppressed = nameof(Path) + "_" + nameof(IncludeSuppressed);
32+
private const string ParameterSet_ScriptDefinition_SuppressedOnly = nameof(ScriptDefinition) + "_" + nameof(SuppressedOnly);
33+
private const string ParameterSet_ScriptDefinition_IncludeSuppressed = nameof(ScriptDefinition) + "_" + nameof(IncludeSuppressed);
34+
3135
#region Private variables
3236
List<string> processedPaths;
3337
#endregion // Private variables
@@ -37,7 +41,12 @@ public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter
3741
/// Path: The path to the file or folder to invoke PSScriptAnalyzer on.
3842
/// </summary>
3943
[Parameter(Position = 0,
40-
ParameterSetName = "File",
44+
ParameterSetName = ParameterSet_Path_IncludeSuppressed,
45+
Mandatory = true,
46+
ValueFromPipeline = true,
47+
ValueFromPipelineByPropertyName = true)]
48+
[Parameter(Position = 0,
49+
ParameterSetName = ParameterSet_Path_SuppressedOnly,
4150
Mandatory = true,
4251
ValueFromPipeline = true,
4352
ValueFromPipelineByPropertyName = true)]
@@ -54,7 +63,12 @@ public string Path
5463
/// ScriptDefinition: a script definition in the form of a string to run rules on.
5564
/// </summary>
5665
[Parameter(Position = 0,
57-
ParameterSetName = "ScriptDefinition",
66+
ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed,
67+
Mandatory = true,
68+
ValueFromPipeline = true,
69+
ValueFromPipelineByPropertyName = true)]
70+
[Parameter(Position = 0,
71+
ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly,
5872
Mandatory = true,
5973
ValueFromPipeline = true,
6074
ValueFromPipelineByPropertyName = true)]
@@ -84,7 +98,6 @@ public string[] CustomRulePath
8498
/// RecurseCustomRulePath: Find rules within subfolders under the path
8599
/// </summary>
86100
[Parameter(Mandatory = false)]
87-
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
88101
public SwitchParameter RecurseCustomRulePath
89102
{
90103
get { return recurseCustomRulePath; }
@@ -96,7 +109,6 @@ public SwitchParameter RecurseCustomRulePath
96109
/// IncludeDefaultRules: Invoke default rules along with Custom rules
97110
/// </summary>
98111
[Parameter(Mandatory = false)]
99-
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
100112
public SwitchParameter IncludeDefaultRules
101113
{
102114
get { return includeDefaultRules; }
@@ -143,11 +155,15 @@ public string[] Severity
143155
}
144156
private string[] severity;
145157

158+
// TODO: This should be only in the Path parameter sets, and is ignored otherwise,
159+
// but we already have a test that depends on it being otherwise
160+
//[Parameter(ParameterSetName = ParameterSet_Path_IncludeSuppressed)]
161+
//[Parameter(ParameterSetName = ParameterSet_Path_SuppressedOnly)]
162+
//
146163
/// <summary>
147164
/// Recurse: Apply to all files within subfolders under the path
148165
/// </summary>
149-
[Parameter(Mandatory = false)]
150-
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
166+
[Parameter]
151167
public SwitchParameter Recurse
152168
{
153169
get { return recurse; }
@@ -158,19 +174,22 @@ public SwitchParameter Recurse
158174
/// <summary>
159175
/// ShowSuppressed: Show the suppressed message
160176
/// </summary>
161-
[Parameter(Mandatory = false)]
162-
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
163-
public SwitchParameter SuppressedOnly
164-
{
165-
get { return suppressedOnly; }
166-
set { suppressedOnly = value; }
167-
}
168-
private bool suppressedOnly;
177+
[Parameter(ParameterSetName = ParameterSet_Path_SuppressedOnly)]
178+
[Parameter(ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly)]
179+
public SwitchParameter SuppressedOnly { get; set; }
180+
181+
/// <summary>
182+
/// Include suppressed diagnostics in the output.
183+
/// </summary>
184+
[Parameter(ParameterSetName = ParameterSet_Path_IncludeSuppressed, Mandatory = true)]
185+
[Parameter(ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed, Mandatory = true)]
186+
public SwitchParameter IncludeSuppressed { get; set; }
169187

170188
/// <summary>
171189
/// Resolves rule violations automatically where possible.
172190
/// </summary>
173-
[Parameter(Mandatory = false, ParameterSetName = "File")]
191+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_Path_IncludeSuppressed)]
192+
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_Path_SuppressedOnly)]
174193
public SwitchParameter Fix
175194
{
176195
get { return fix; }
@@ -334,14 +353,20 @@ protected override void BeginProcessing()
334353
this.settings));
335354
}
336355

356+
SuppressionPreference suppressionPreference = SuppressedOnly
357+
? SuppressionPreference.SuppressedOnly
358+
: IncludeSuppressed
359+
? SuppressionPreference.Include
360+
: SuppressionPreference.Omit;
361+
337362
ScriptAnalyzer.Instance.Initialize(
338363
this,
339364
combRulePaths,
340365
this.includeRule,
341366
this.excludeRule,
342367
this.severity,
343368
combRulePaths == null || combIncludeDefaultRules,
344-
this.suppressedOnly);
369+
suppressionPreference);
345370
}
346371

347372
/// <summary>
@@ -402,29 +427,32 @@ protected override void StopProcessing()
402427

403428
private void ProcessInput()
404429
{
405-
IEnumerable<DiagnosticRecord> diagnosticsList = Enumerable.Empty<DiagnosticRecord>();
406-
if (IsFileParameterSet())
430+
WriteToOutput(RunAnalysis());
431+
}
432+
433+
private IEnumerable<DiagnosticRecord> RunAnalysis()
434+
{
435+
if (!IsFileParameterSet())
407436
{
408-
foreach (var p in processedPaths)
409-
{
410-
if (fix)
411-
{
412-
ShouldProcess(p, $"Analyzing and fixing path with Recurse={this.recurse}");
413-
diagnosticsList = ScriptAnalyzer.Instance.AnalyzeAndFixPath(p, this.ShouldProcess, this.recurse);
414-
}
415-
else
416-
{
417-
ShouldProcess(p, $"Analyzing path with Recurse={this.recurse}");
418-
diagnosticsList = ScriptAnalyzer.Instance.AnalyzePath(p, this.ShouldProcess, this.recurse);
419-
}
420-
WriteToOutput(diagnosticsList);
421-
}
437+
return ScriptAnalyzer.Instance.AnalyzeScriptDefinition(scriptDefinition, out _, out _);
422438
}
423-
else if (String.Equals(this.ParameterSetName, "ScriptDefinition", StringComparison.OrdinalIgnoreCase))
439+
440+
var diagnostics = new List<DiagnosticRecord>();
441+
foreach (string path in this.processedPaths)
424442
{
425-
diagnosticsList = ScriptAnalyzer.Instance.AnalyzeScriptDefinition(scriptDefinition, out _, out _);
426-
WriteToOutput(diagnosticsList);
443+
if (fix)
444+
{
445+
ShouldProcess(path, $"Analyzing and fixing path with Recurse={this.recurse}");
446+
diagnostics.AddRange(ScriptAnalyzer.Instance.AnalyzeAndFixPath(path, this.ShouldProcess, this.recurse));
447+
}
448+
else
449+
{
450+
ShouldProcess(path, $"Analyzing path with Recurse={this.recurse}");
451+
diagnostics.AddRange(ScriptAnalyzer.Instance.AnalyzePath(path, this.ShouldProcess, this.recurse));
452+
}
427453
}
454+
455+
return diagnostics;
428456
}
429457

430458
private void WriteToOutput(IEnumerable<DiagnosticRecord> diagnosticRecords)
@@ -497,10 +525,7 @@ private void ProcessPath()
497525
}
498526
}
499527

500-
private bool IsFileParameterSet()
501-
{
502-
return String.Equals(this.ParameterSetName, "File", StringComparison.OrdinalIgnoreCase);
503-
}
528+
private bool IsFileParameterSet() => Path is not null;
504529

505530
private bool OverrideSwitchParam(bool paramValue, string paramName)
506531
{

Engine/Engine.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<AssemblyVersion>1.20.0</AssemblyVersion>
88
<PackageId>Engine</PackageId>
99
<RootNamespace>Microsoft.Windows.PowerShell.ScriptAnalyzer</RootNamespace> <!-- Namespace needs to match Assembly name for ressource binding -->
10+
<LangVersion>9.0</LangVersion>
1011
</PropertyGroup>
1112

1213
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">

Engine/Formatter.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public static string Format<TCmdlet>(
6060

6161
var currentSettings = GetCurrentSettings(settings, rule);
6262
ScriptAnalyzer.Instance.UpdateSettings(currentSettings);
63-
ScriptAnalyzer.Instance.Initialize(cmdlet, null, null, null, null, true, false);
63+
ScriptAnalyzer.Instance.Initialize(cmdlet, null, null, null, null, true, SuppressionPreference.Omit);
6464

6565
text = ScriptAnalyzer.Instance.Fix(text, range, skipParsing, out Range updatedRange, out bool fixesWereApplied, ref scriptAst, ref scriptTokens, skipVariableAnalysis: true);
6666
skipParsing = !fixesWereApplied;

Engine/Generic/DiagnosticRecord.cs

+10-3
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,13 @@ public IEnumerable<CorrectionExtent> SuggestedCorrections
9292
set { suggestedCorrections = value; }
9393
}
9494

95+
public bool IsSuppressed { get; protected set; } = false;
96+
9597
/// <summary>
9698
/// DiagnosticRecord: The constructor for DiagnosticRecord class.
9799
/// </summary>
98100
public DiagnosticRecord()
99101
{
100-
101102
}
102103

103104
/// <summary>
@@ -109,7 +110,14 @@ public DiagnosticRecord()
109110
/// <param name="severity">The severity of this diagnostic</param>
110111
/// <param name="scriptPath">The full path of the script file being analyzed</param>
111112
/// <param name="suggestedCorrections">The correction suggested by the rule to replace the extent text</param>
112-
public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null, IEnumerable<CorrectionExtent> suggestedCorrections = null)
113+
public DiagnosticRecord(
114+
string message,
115+
IScriptExtent extent,
116+
string ruleName,
117+
DiagnosticSeverity severity,
118+
string scriptPath,
119+
string ruleId = null,
120+
IEnumerable<CorrectionExtent> suggestedCorrections = null)
113121
{
114122
Message = message;
115123
RuleName = ruleName;
@@ -119,7 +127,6 @@ public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, D
119127
RuleSuppressionID = ruleId;
120128
this.suggestedCorrections = suggestedCorrections;
121129
}
122-
123130
}
124131

125132

Engine/Generic/SuppressedRecord.cs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class SuppressedRecord : DiagnosticRecord
2424
public SuppressedRecord(DiagnosticRecord record, IReadOnlyList<RuleSuppression> suppressions)
2525
{
2626
Suppression = new ReadOnlyCollection<RuleSuppression>(new List<RuleSuppression>(suppressions));
27+
IsSuppressed = true;
2728
if (record != null)
2829
{
2930
RuleName = record.RuleName;

Engine/ScriptAnalyzer.cs

+27-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525

2626
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
2727
{
28+
internal enum SuppressionPreference
29+
{
30+
Omit = 0,
31+
Include = 1,
32+
SuppressedOnly = 2,
33+
}
34+
2835
public sealed class ScriptAnalyzer
2936
{
3037
#region Private members
@@ -41,7 +48,7 @@ public sealed class ScriptAnalyzer
4148
string[] severity;
4249
List<Regex> includeRegexList;
4350
List<Regex> excludeRegexList;
44-
bool suppressedOnly;
51+
private SuppressionPreference _suppressionPreference;
4552
#if !PSV3
4653
ModuleDependencyHandler moduleHandler;
4754
#endif
@@ -118,7 +125,7 @@ internal void Initialize<TCmdlet>(
118125
string[] excludeRuleNames = null,
119126
string[] severity = null,
120127
bool includeDefaultRules = false,
121-
bool suppressedOnly = false)
128+
SuppressionPreference suppressionPreference = SuppressionPreference.Omit)
122129
where TCmdlet : PSCmdlet, IOutputWriter
123130
{
124131
if (cmdlet == null)
@@ -135,7 +142,7 @@ internal void Initialize<TCmdlet>(
135142
excludeRuleNames,
136143
severity,
137144
includeDefaultRules,
138-
suppressedOnly);
145+
suppressionPreference);
139146
}
140147

141148
/// <summary>
@@ -150,6 +157,7 @@ public void Initialize(
150157
string[] severity = null,
151158
bool includeDefaultRules = false,
152159
bool suppressedOnly = false,
160+
bool includeSuppression = false,
153161
string profile = null)
154162
{
155163
if (runspace == null)
@@ -163,6 +171,11 @@ public void Initialize(
163171
outputWriter);
164172
Helper.Instance.Initialize();
165173

174+
SuppressionPreference suppressionPreference = suppressedOnly
175+
? SuppressionPreference.SuppressedOnly
176+
: includeSuppression
177+
? SuppressionPreference.Include
178+
: SuppressionPreference.Omit;
166179

167180
this.Initialize(
168181
outputWriter,
@@ -173,7 +186,7 @@ public void Initialize(
173186
excludeRuleNames,
174187
severity,
175188
includeDefaultRules,
176-
suppressedOnly,
189+
suppressionPreference,
177190
profile);
178191
}
179192

@@ -187,7 +200,7 @@ public void CleanUp()
187200
severity = null;
188201
includeRegexList = null;
189202
excludeRegexList = null;
190-
suppressedOnly = false;
203+
_suppressionPreference = SuppressionPreference.Omit;
191204
}
192205

193206
/// <summary>
@@ -671,7 +684,7 @@ private void Initialize(
671684
string[] excludeRuleNames,
672685
string[] severity,
673686
bool includeDefaultRules = false,
674-
bool suppressedOnly = false,
687+
SuppressionPreference suppressionPreference = SuppressionPreference.Omit,
675688
string profile = null)
676689
{
677690
if (outputWriter == null)
@@ -730,7 +743,7 @@ private void Initialize(
730743
}
731744
}
732745

733-
this.suppressedOnly = suppressedOnly;
746+
_suppressionPreference = suppressionPreference;
734747
this.includeRegexList = new List<Regex>();
735748
this.excludeRegexList = new List<Regex>();
736749

@@ -2324,9 +2337,13 @@ public IEnumerable<DiagnosticRecord> AnalyzeSyntaxTree(
23242337
// Need to reverse the concurrentbag to ensure that results are sorted in the increasing order of line numbers
23252338
IEnumerable<DiagnosticRecord> diagnosticsList = diagnostics.Reverse();
23262339

2327-
return this.suppressedOnly ?
2328-
suppressed.OfType<DiagnosticRecord>() :
2329-
diagnosticsList;
2340+
return _suppressionPreference switch
2341+
{
2342+
SuppressionPreference.SuppressedOnly => suppressed.OfType<DiagnosticRecord>(),
2343+
SuppressionPreference.Omit => diagnosticsList,
2344+
SuppressionPreference.Include => diagnosticsList.Concat(suppressed.OfType<DiagnosticRecord>()),
2345+
_ => throw new ArgumentException($"SuppressionPreference has invalid value '{_suppressionPreference}'"),
2346+
};
23302347
}
23312348
}
23322349
}

0 commit comments

Comments
 (0)