Skip to content

Commit 9a972fe

Browse files
author
Quoc Truong
committed
Merge pull request #56 from PowerShell/justificationprototype
Add -SuppressedOnly switch to ScriptAnalyzer
2 parents cd8176e + 4d64baf commit 9a972fe

9 files changed

+257
-67
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ public SwitchParameter Recurse
9999
}
100100
private bool recurse;
101101

102+
/// <summary>
103+
/// ShowSuppressed: Show the suppressed message
104+
/// </summary>
105+
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
106+
public SwitchParameter SuppressedOnly
107+
{
108+
get { return suppressedOnly; }
109+
set { suppressedOnly = value; }
110+
}
111+
private bool suppressedOnly;
112+
102113
#endregion Parameters
103114

104115
#region Private Members
@@ -256,6 +267,7 @@ private void AnalyzeFile(string filePath)
256267
Token[] tokens = null;
257268
ParseError[] errors = null;
258269
List<DiagnosticRecord> diagnostics = new List<DiagnosticRecord>();
270+
List<SuppressedRecord> suppressed = new List<SuppressedRecord>();
259271

260272
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
261273
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
@@ -328,7 +340,9 @@ private void AnalyzeFile(string filePath)
328340
// We want the Engine to continue functioning even if one or more Rules throws an exception
329341
try
330342
{
331-
diagnostics.AddRange(Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(ast, filePath).ToList()));
343+
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(ast, filePath).ToList());
344+
diagnostics.AddRange(records.Item2);
345+
suppressed.AddRange(records.Item1);
332346
}
333347
catch (Exception scriptRuleException)
334348
{
@@ -356,7 +370,9 @@ private void AnalyzeFile(string filePath)
356370
// We want the Engine to continue functioning even if one or more Rules throws an exception
357371
try
358372
{
359-
diagnostics.AddRange(Helper.Instance.SuppressRule(tokenRule.GetName(), ruleSuppressions, tokenRule.AnalyzeTokens(tokens, fileName).ToList()));
373+
var records = Helper.Instance.SuppressRule(tokenRule.GetName(), ruleSuppressions, tokenRule.AnalyzeTokens(tokens, filePath).ToList());
374+
diagnostics.AddRange(records.Item2);
375+
suppressed.AddRange(records.Item1);
360376
}
361377
catch (Exception tokenRuleException)
362378
{
@@ -384,7 +400,9 @@ private void AnalyzeFile(string filePath)
384400
// We want the Engine to continue functioning even if one or more Rules throws an exception
385401
try
386402
{
387-
diagnostics.AddRange(Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCClass(ast, filePath).ToList()));
403+
var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCClass(ast, filePath).ToList());
404+
diagnostics.AddRange(records.Item2);
405+
suppressed.AddRange(records.Item1);
388406
}
389407
catch (Exception dscResourceRuleException)
390408
{
@@ -426,7 +444,9 @@ private void AnalyzeFile(string filePath)
426444
// We want the Engine to continue functioning even if one or more Rules throws an exception
427445
try
428446
{
429-
diagnostics.AddRange(Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCResource(ast, filePath).ToList()));
447+
var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCResource(ast, filePath).ToList());
448+
diagnostics.AddRange(records.Item2);
449+
suppressed.AddRange(records.Item1);
430450
}
431451
catch (Exception dscResourceRuleException)
432452
{
@@ -484,9 +504,19 @@ private void AnalyzeFile(string filePath)
484504
//Output through loggers
485505
foreach (ILogger logger in ScriptAnalyzer.Instance.Loggers)
486506
{
487-
foreach (DiagnosticRecord diagnostic in diagnostics)
507+
if (SuppressedOnly)
488508
{
489-
logger.LogMessage(diagnostic, this);
509+
foreach (DiagnosticRecord suppressRecord in suppressed)
510+
{
511+
logger.LogObject(suppressRecord, this);
512+
}
513+
}
514+
else
515+
{
516+
foreach (DiagnosticRecord diagnostic in diagnostics)
517+
{
518+
logger.LogObject(diagnostic, this);
519+
}
490520
}
491521
}
492522
}

Engine/Generic/ILogger.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ public interface ILogger
1515
/// <summary>
1616
/// LogMessage: Logs the given diagnostic, using the command for Write methods if needed.
1717
/// </summary>
18-
/// <param name="diagnostic">The DiagnosticRecord to be logged.</param>
18+
/// <param name="obj">The object to be logged.</param>
1919
/// <param name="command">The InvokePSScriptAnalyzerCommand that this logger is running off of.</param>
20-
void LogMessage(DiagnosticRecord diagnostic, InvokeScriptAnalyzerCommand command);
20+
void LogObject(Object obj, InvokeScriptAnalyzerCommand command);
2121

2222
/// <summary>
2323
/// GetName: Retrieves the name of the logger.

Engine/Generic/RuleSuppression.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ public string Error
118118
set;
119119
}
120120

121+
/// <summary>
122+
/// Returns the justification for the suppression
123+
/// </summary>
124+
public string Justification
125+
{
126+
get;
127+
set;
128+
}
129+
121130
private static HashSet<string> scopeSet;
122131

123132
/// <summary>
@@ -161,6 +170,10 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
161170
{
162171
switch (count)
163172
{
173+
case 5:
174+
Justification = (positionalArguments[4] as StringConstantExpressionAst).Value;
175+
goto case 4;
176+
164177
case 4:
165178
Target = (positionalArguments[3] as StringConstantExpressionAst).Value;
166179
goto case 3;
@@ -242,6 +255,15 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
242255
Target = (name.Argument as StringConstantExpressionAst).Value;
243256
goto default;
244257

258+
case "justification":
259+
if (!String.IsNullOrWhiteSpace(Justification))
260+
{
261+
Error = String.Format(Strings.NamedAndPositionalArgumentsConflictError, name);
262+
}
263+
264+
Justification = (name.Argument as StringConstantExpressionAst).Value;
265+
goto default;
266+
245267
default:
246268
break;
247269
}
@@ -278,20 +300,22 @@ public RuleSuppression(AttributeAst attrAst, int start, int end)
278300
}
279301

280302
/// <summary>
281-
/// Constructs rule expression from rule name, id, start, end and startAttributeLine
303+
/// Constructs rule expression from rule name, id, start, end, startAttributeLine and justification
282304
/// </summary>
283305
/// <param name="ruleName"></param>
284306
/// <param name="ruleSuppressionID"></param>
285307
/// <param name="start"></param>
286308
/// <param name="end"></param>
287309
/// <param name="startAttributeLine"></param>
288-
public RuleSuppression(string ruleName, string ruleSuppressionID, int start, int end, int startAttributeLine)
310+
/// <param name="justification"></param>
311+
public RuleSuppression(string ruleName, string ruleSuppressionID, int start, int end, int startAttributeLine, string justification)
289312
{
290313
RuleName = ruleName;
291314
RuleSuppressionID = ruleSuppressionID;
292315
StartOffset = start;
293316
EndOffset = end;
294317
StartAttributeLine = startAttributeLine;
318+
Justification = justification;
295319
}
296320

297321
/// <summary>
@@ -327,7 +351,7 @@ public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> at
327351
}
328352

329353
// regex for wild card *
330-
Regex reg = new Regex(String.Format("^{0}$", Regex.Escape(ruleSupp.Target).Replace(@"\*", ".*")));
354+
Regex reg = new Regex(String.Format("^{0}$", Regex.Escape(ruleSupp.Target).Replace(@"\*", ".*")), RegexOptions.IgnoreCase);
331355
IEnumerable<Ast> targetAsts = null;
332356

333357
switch (ruleSupp.Scope.ToLower())
@@ -356,7 +380,8 @@ public static List<RuleSuppression> GetSuppressions(IEnumerable<AttributeAst> at
356380

357381
foreach (Ast targetAst in targetAsts)
358382
{
359-
result.Add(new RuleSuppression(ruleSupp.RuleName, ruleSupp.RuleSuppressionID, targetAst.Extent.StartOffset, targetAst.Extent.EndOffset, attributeAst.Extent.StartLineNumber));
383+
result.Add(new RuleSuppression(ruleSupp.RuleName, ruleSupp.RuleSuppressionID, targetAst.Extent.StartOffset,
384+
targetAst.Extent.EndOffset, attributeAst.Extent.StartLineNumber, ruleSupp.Justification));
360385
}
361386
}
362387

Engine/Generic/SuppressedRecord.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic
8+
{
9+
/// <summary>
10+
/// Represents a suppressed diagnostic record
11+
/// </summary>
12+
public class SuppressedRecord : DiagnosticRecord
13+
{
14+
/// <summary>
15+
/// The rule suppression of this record
16+
/// </summary>
17+
public RuleSuppression Suppression
18+
{
19+
get;
20+
set;
21+
}
22+
23+
/// <summary>
24+
/// Creates a suppressed record based on a diagnostic record and the rule suppression
25+
/// </summary>
26+
/// <param name="record"></param>
27+
/// <param name="Suppression"></param>
28+
public SuppressedRecord(DiagnosticRecord record, RuleSuppression suppression)
29+
{
30+
Suppression = suppression;
31+
if (record != null)
32+
{
33+
RuleName = record.RuleName;
34+
Message = record.Message;
35+
Extent = record.Extent;
36+
Severity = record.Severity;
37+
ScriptName = record.ScriptName;
38+
RuleSuppressionID = record.RuleSuppressionID;
39+
}
40+
}
41+
}
42+
}

Engine/Helper.cs

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -716,25 +716,28 @@ internal List<RuleSuppression> GetSuppressionsClass(TypeDefinitionAst typeAst)
716716
}
717717

718718
/// <summary>
719-
/// Suppress the rules from the diagnostic records list and return the result
719+
/// Suppress the rules from the diagnostic records list.
720+
/// Returns a list of suppressed records as well as the ones that are not suppressed
720721
/// </summary>
721722
/// <param name="ruleSuppressions"></param>
722723
/// <param name="diagnostics"></param>
723-
public List<DiagnosticRecord> SuppressRule(string ruleName, Dictionary<string, List<RuleSuppression>> ruleSuppressionsDict, List<DiagnosticRecord> diagnostics)
724+
public Tuple<List<SuppressedRecord>, List<DiagnosticRecord>> SuppressRule(string ruleName, Dictionary<string, List<RuleSuppression>> ruleSuppressionsDict, List<DiagnosticRecord> diagnostics)
724725
{
725-
List<DiagnosticRecord> results = new List<DiagnosticRecord>();
726+
List<SuppressedRecord> suppressedRecords = new List<SuppressedRecord>();
727+
List<DiagnosticRecord> unSuppressedRecords = new List<DiagnosticRecord>();
728+
Tuple<List<SuppressedRecord>, List<DiagnosticRecord>> result = Tuple.Create(suppressedRecords, unSuppressedRecords);
726729

727730
if (ruleSuppressionsDict == null || !ruleSuppressionsDict.ContainsKey(ruleName)
728731
|| diagnostics == null || diagnostics.Count == 0)
729732
{
730-
return diagnostics;
733+
return result;
731734
}
732735

733736
List<RuleSuppression> ruleSuppressions = ruleSuppressionsDict[ruleName];
734737

735738
if (ruleSuppressions.Count == 0)
736739
{
737-
return diagnostics;
740+
return result;
738741
}
739742

740743
int recordIndex = 0;
@@ -760,46 +763,54 @@ public List<DiagnosticRecord> SuppressRule(string ruleName, Dictionary<string, L
760763
continue;
761764
}
762765

763-
// the record precedes the rule suppression so don't apply the suppression
764-
if (record.Extent.StartOffset < ruleSuppression.StartOffset)
765-
{
766-
results.Add(record);
767-
}
768-
// end of the rule suppression is less than the record start offset so move on to next rule suppression
769-
else if (ruleSuppression.EndOffset < record.Extent.StartOffset)
766+
// if the record precedes the rule suppression then we don't apply the suppression
767+
// so we check that start of record is greater than start of suppression
768+
if (record.Extent.StartOffset >= ruleSuppression.StartOffset)
770769
{
771-
ruleSuppressionIndex += 1;
772-
773-
// If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly
774-
if (!String.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && suppressionCount == 0)
770+
// end of the rule suppression is less than the record start offset so move on to next rule suppression
771+
if (ruleSuppression.EndOffset < record.Extent.StartOffset)
775772
{
776-
ruleSuppression.Error = String.Format(CultureInfo.CurrentCulture, Strings.RuleSuppressionErrorFormat, ruleSuppression.StartAttributeLine,
777-
System.IO.Path.GetFileName(record.Extent.File), String.Format(Strings.RuleSuppressionIDError, ruleSuppression.RuleSuppressionID));
778-
Helper.Instance.MyCmdlet.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
779-
}
773+
ruleSuppressionIndex += 1;
780774

781-
if (ruleSuppressionIndex == ruleSuppressions.Count)
782-
{
783-
break;
784-
}
775+
// If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly
776+
if (!String.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && suppressionCount == 0)
777+
{
778+
ruleSuppression.Error = String.Format(CultureInfo.CurrentCulture, Strings.RuleSuppressionErrorFormat, ruleSuppression.StartAttributeLine,
779+
System.IO.Path.GetFileName(record.Extent.File), String.Format(Strings.RuleSuppressionIDError, ruleSuppression.RuleSuppressionID));
780+
Helper.Instance.MyCmdlet.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
781+
}
785782

786-
ruleSuppression = ruleSuppressions[ruleSuppressionIndex];
787-
suppressionCount = 0;
783+
if (ruleSuppressionIndex == ruleSuppressions.Count)
784+
{
785+
break;
786+
}
788787

789-
continue;
788+
ruleSuppression = ruleSuppressions[ruleSuppressionIndex];
789+
suppressionCount = 0;
790+
791+
continue;
792+
}
793+
// at this point, the record is inside the interval
794+
else
795+
{
796+
// if the rule suppression id from the rule suppression is not null and the one from diagnostic record is not null
797+
// and they are they are not the same then we cannot ignore the record
798+
if (!string.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && !string.IsNullOrWhiteSpace(record.RuleSuppressionID)
799+
&& !string.Equals(ruleSuppression.RuleSuppressionID, record.RuleSuppressionID, StringComparison.OrdinalIgnoreCase))
800+
{
801+
suppressionCount -= 1;
802+
unSuppressedRecords.Add(record);
803+
}
804+
// otherwise, we suppress the record, move on to the next.
805+
else
806+
{
807+
suppressedRecords.Add(new SuppressedRecord(record, ruleSuppression));
808+
}
809+
}
790810
}
791-
// at this point, the record is inside the interval
792811
else
793812
{
794-
// if the rule suppression id from the rule suppression is not null and the one from diagnostic record is not null
795-
// and they are they are not the same then we cannot ignore the record
796-
if (!string.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && !string.IsNullOrWhiteSpace(record.RuleSuppressionID)
797-
&& !string.Equals(ruleSuppression.RuleSuppressionID, record.RuleSuppressionID, StringComparison.OrdinalIgnoreCase))
798-
{
799-
results.Add(record);
800-
suppressionCount -= 1;
801-
}
802-
// otherwise, we ignore the record, move on to the next.
813+
unSuppressedRecords.Add(record);
803814
}
804815

805816
// important assumption: this point is reached only if we want to move to the next record
@@ -822,14 +833,13 @@ public List<DiagnosticRecord> SuppressRule(string ruleName, Dictionary<string, L
822833
record = diagnostics[recordIndex];
823834
}
824835

825-
// Add all unprocessed records to results
826836
while (recordIndex < diagnostics.Count)
827837
{
828-
results.Add(diagnostics[recordIndex]);
838+
unSuppressedRecords.Add(diagnostics[recordIndex]);
829839
recordIndex += 1;
830840
}
831841

832-
return results;
842+
return result;
833843
}
834844

835845
#endregion
@@ -918,15 +928,6 @@ public object VisitScriptBlock(ScriptBlockAst scriptBlockAst)
918928
return null;
919929
}
920930

921-
/// <summary>
922-
/// Do nothing
923-
/// </summary>
924-
/// <param name="baseCtorInvokeMemberExpressionAst"></param>
925-
/// <returns></returns>
926-
public object VisitBaseCtorInvokeMemberExpression(BaseCtorInvokeMemberExpressionAst baseCtorInvokeMemberExpressionAst)
927-
{
928-
return null;
929-
}
930931

931932
/// <summary>
932933
/// Do nothing

0 commit comments

Comments
 (0)