Skip to content

Commit 420f995

Browse files
committed
2 parents ea65794 + b9d2792 commit 420f995

30 files changed

+1173
-328
lines changed

Engine/Commands/InvokeScriptAnalyzerCommand.cs

Lines changed: 61 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ public SwitchParameter Recurse
112112
}
113113
private bool recurse;
114114

115+
/// <summary>
116+
/// ShowSuppressed: Show the suppressed message
117+
/// </summary>
118+
[Parameter(Mandatory = false)]
119+
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
120+
public SwitchParameter SuppressedOnly
121+
{
122+
get { return suppressedOnly; }
123+
set { suppressedOnly = value; }
124+
}
125+
private bool suppressedOnly;
126+
115127
#endregion Parameters
116128

117129
#region Private Members
@@ -183,7 +195,6 @@ protected override void BeginProcessing()
183195
#region Verify rules
184196

185197
rules = ScriptAnalyzer.Instance.ScriptRules.Union<IRule>(
186-
ScriptAnalyzer.Instance.CommandRules).Union<IRule>(
187198
ScriptAnalyzer.Instance.TokenRules).Union<IRule>(
188199
ScriptAnalyzer.Instance.ExternalRules ?? Enumerable.Empty<IExternalRule>());
189200

@@ -270,7 +281,7 @@ private void AnalyzeFile(string filePath)
270281
Token[] tokens = null;
271282
ParseError[] errors = null;
272283
List<DiagnosticRecord> diagnostics = new List<DiagnosticRecord>();
273-
IEnumerable<Ast> funcDefAsts;
284+
List<SuppressedRecord> suppressed = new List<SuppressedRecord>();
274285

275286
// Use a List of KVP rather than dictionary, since for a script containing inline functions with same signature, keys clash
276287
List<KeyValuePair<CommandInfo, IScriptExtent>> cmdInfoTable = new List<KeyValuePair<CommandInfo, IScriptExtent>>();
@@ -325,6 +336,19 @@ private void AnalyzeFile(string filePath)
325336
return;
326337
}
327338

339+
Dictionary<string, List<RuleSuppression>> ruleSuppressions = Helper.Instance.GetRuleSuppression(ast);
340+
341+
foreach (List<RuleSuppression> ruleSuppressionsList in ruleSuppressions.Values)
342+
{
343+
foreach (RuleSuppression ruleSuppression in ruleSuppressionsList)
344+
{
345+
if (!String.IsNullOrWhiteSpace(ruleSuppression.Error))
346+
{
347+
WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression));
348+
}
349+
}
350+
}
351+
328352
#region Run VariableAnalysis
329353
try
330354
{
@@ -353,6 +377,7 @@ private void AnalyzeFile(string filePath)
353377
break;
354378
}
355379
}
380+
356381
foreach (Regex exclude in excludeRegexList)
357382
{
358383
if (exclude.IsMatch(scriptRule.GetName()))
@@ -361,97 +386,22 @@ private void AnalyzeFile(string filePath)
361386
break;
362387
}
363388
}
364-
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
389+
390+
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
365391
{
366392
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, scriptRule.GetName()));
367393

368394
// Ensure that any unhandled errors from Rules are converted to non-terminating errors
369395
// We want the Engine to continue functioning even if one or more Rules throws an exception
370396
try
371397
{
372-
diagnostics.AddRange(scriptRule.AnalyzeScript(ast, filePath));
398+
var records = Helper.Instance.SuppressRule(scriptRule.GetName(), ruleSuppressions, scriptRule.AnalyzeScript(ast, filePath).ToList());
399+
diagnostics.AddRange(records.Item2);
400+
suppressed.AddRange(records.Item1);
373401
}
374402
catch (Exception scriptRuleException)
375403
{
376-
WriteError(new ErrorRecord(scriptRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
377-
}
378-
}
379-
}
380-
}
381-
382-
#endregion
383-
384-
#region Run Command Rules
385-
386-
funcDefAsts = ast.FindAll(new Func<Ast, bool>((testAst) => (testAst is FunctionDefinitionAst)), true);
387-
if (funcDefAsts != null)
388-
{
389-
foreach (FunctionDefinitionAst funcDefAst in funcDefAsts)
390-
{
391-
//Create command info object here
392-
var sb = new StringBuilder();
393-
sb.AppendLine(funcDefAst.Extent.Text);
394-
sb.AppendFormat("Get-Command –CommandType Function –Name {0}", funcDefAst.Name);
395-
396-
var funcDefPS = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
397-
funcDefPS.AddScript(sb.ToString());
398-
399-
try
400-
{
401-
var commandInfo = funcDefPS.Invoke<CommandInfo>();
402-
403-
foreach (CommandInfo cmdInfo in commandInfo)
404-
{
405-
cmdInfoTable.Add(new KeyValuePair<CommandInfo, IScriptExtent>(cmdInfo as CommandInfo, funcDefAst.Extent));
406-
}
407-
}
408-
catch (ParseException)
409-
{
410-
WriteError(new ErrorRecord(new CommandNotFoundException(),
411-
string.Format(CultureInfo.CurrentCulture, Strings.CommandInfoNotFound, funcDefAst.Name),
412-
ErrorCategory.SyntaxError, funcDefAst));
413-
}
414-
}
415-
}
416-
417-
if (ScriptAnalyzer.Instance.CommandRules != null)
418-
{
419-
foreach (ICommandRule commandRule in ScriptAnalyzer.Instance.CommandRules)
420-
{
421-
bool includeRegexMatch = false;
422-
bool excludeRegexMatch = false;
423-
foreach (Regex include in includeRegexList)
424-
{
425-
if (include.IsMatch(commandRule.GetName()))
426-
{
427-
includeRegexMatch = true;
428-
break;
429-
}
430-
}
431-
foreach (Regex exclude in excludeRegexList)
432-
{
433-
if (exclude.IsMatch(commandRule.GetName()))
434-
{
435-
excludeRegexMatch = true;
436-
break;
437-
}
438-
}
439-
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch))
440-
{
441-
foreach (KeyValuePair<CommandInfo, IScriptExtent> commandInfo in cmdInfoTable)
442-
{
443-
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, commandRule.GetName()));
444-
445-
// Ensure that any unhandled errors from Rules are converted to non-terminating errors
446-
// We want the Engine to continue functioning even if one or more Rules throws an exception
447-
try
448-
{
449-
diagnostics.AddRange(commandRule.AnalyzeCommand(commandInfo.Key, commandInfo.Value, fileName));
450-
}
451-
catch (Exception commandRuleException)
452-
{
453-
WriteError(new ErrorRecord(commandRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName));
454-
}
404+
WriteError(new ErrorRecord(scriptRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, filePath));
455405
}
456406
}
457407
}
@@ -491,11 +441,13 @@ private void AnalyzeFile(string filePath)
491441
// We want the Engine to continue functioning even if one or more Rules throws an exception
492442
try
493443
{
494-
diagnostics.AddRange(tokenRule.AnalyzeTokens(tokens, fileName));
444+
var records = Helper.Instance.SuppressRule(tokenRule.GetName(), ruleSuppressions, tokenRule.AnalyzeTokens(tokens, filePath).ToList());
445+
diagnostics.AddRange(records.Item2);
446+
suppressed.AddRange(records.Item1);
495447
}
496448
catch (Exception tokenRuleException)
497449
{
498-
WriteError(new ErrorRecord(tokenRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName));
450+
WriteError(new ErrorRecord(tokenRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, fileName));
499451
}
500452
}
501453
}
@@ -511,6 +463,7 @@ private void AnalyzeFile(string filePath)
511463
{
512464
bool includeRegexMatch = false;
513465
bool excludeRegexMatch = false;
466+
514467
foreach (Regex include in includeRegexList)
515468
{
516469
if (include.IsMatch(dscResourceRule.GetName()))
@@ -519,6 +472,7 @@ private void AnalyzeFile(string filePath)
519472
break;
520473
}
521474
}
475+
522476
foreach (Regex exclude in excludeRegexList)
523477
{
524478
if (exclude.IsMatch(dscResourceRule.GetName()))
@@ -527,6 +481,7 @@ private void AnalyzeFile(string filePath)
527481
break;
528482
}
529483
}
484+
530485
if ((includeRule == null || includeRegexMatch) && (excludeRule == null || excludeRegexMatch))
531486
{
532487
WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName()));
@@ -535,11 +490,13 @@ private void AnalyzeFile(string filePath)
535490
// We want the Engine to continue functioning even if one or more Rules throws an exception
536491
try
537492
{
538-
diagnostics.AddRange(dscResourceRule.AnalyzeDSCClass(ast, filePath));
493+
var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCClass(ast, filePath).ToList());
494+
diagnostics.AddRange(records.Item2);
495+
suppressed.AddRange(records.Item1);
539496
}
540497
catch (Exception dscResourceRuleException)
541498
{
542-
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
499+
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, filePath));
543500
}
544501
}
545502
}
@@ -592,11 +549,13 @@ private void AnalyzeFile(string filePath)
592549
// We want the Engine to continue functioning even if one or more Rules throws an exception
593550
try
594551
{
595-
diagnostics.AddRange(dscResourceRule.AnalyzeDSCResource(ast, filePath));
552+
var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCResource(ast, filePath).ToList());
553+
diagnostics.AddRange(records.Item2);
554+
suppressed.AddRange(records.Item1);
596555
}
597556
catch (Exception dscResourceRuleException)
598557
{
599-
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, filePath));
558+
WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, filePath));
600559
}
601560
}
602561
}
@@ -630,7 +589,7 @@ private void AnalyzeFile(string filePath)
630589
}
631590
catch (Exception externalRuleException)
632591
{
633-
WriteError(new ErrorRecord(externalRuleException, Strings.RuleError, ErrorCategory.InvalidOperation, fileName));
592+
WriteError(new ErrorRecord(externalRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, fileName));
634593
}
635594
}
636595
}
@@ -649,9 +608,19 @@ private void AnalyzeFile(string filePath)
649608
//Output through loggers
650609
foreach (ILogger logger in ScriptAnalyzer.Instance.Loggers)
651610
{
652-
foreach (DiagnosticRecord diagnostic in diagnostics)
611+
if (SuppressedOnly)
653612
{
654-
logger.LogMessage(diagnostic, this);
613+
foreach (DiagnosticRecord suppressRecord in suppressed)
614+
{
615+
logger.LogObject(suppressRecord, this);
616+
}
617+
}
618+
else
619+
{
620+
foreach (DiagnosticRecord diagnostic in diagnostics)
621+
{
622+
logger.LogObject(diagnostic, this);
623+
}
655624
}
656625
}
657626
}

Engine/Generic/AvoidParameterGeneric.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4747
{
4848
if (ParameterCondition(cmdAst, ceAst))
4949
{
50-
yield return new DiagnosticRecord(GetError(fileName, cmdAst), cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
50+
yield return new DiagnosticRecord(GetError(fileName, cmdAst), cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName, cmdAst.GetCommandName());
5151
}
5252
}
5353
}

Engine/Generic/DiagnosticRecord.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class DiagnosticRecord
3030
private string ruleName;
3131
private DiagnosticSeverity severity;
3232
private string scriptName;
33+
private string ruleSuppressionId;
3334

3435
/// <summary>
3536
/// Represents a string from the rule about why this diagnostic was created.
@@ -74,7 +75,16 @@ public string ScriptName
7475
{
7576
get { return scriptName; }
7677
//Trim down to the leaf element of the filePath and pass it to Diagnostic Record
77-
set { scriptName = System.IO.Path.GetFileName(value); ; }
78+
set { scriptName = System.IO.Path.GetFileName(value); }
79+
}
80+
81+
/// <summary>
82+
/// Returns the rule id for this record
83+
/// </summary>
84+
public string RuleSuppressionID
85+
{
86+
get { return ruleSuppressionId; }
87+
set { ruleSuppressionId = value; }
7888
}
7989

8090
/// <summary>
@@ -93,13 +103,14 @@ public DiagnosticRecord()
93103
/// <param name="ruleName">The name of the rule that created this diagnostic</param>
94104
/// <param name="severity">The severity of this diagnostic</param>
95105
/// <param name="scriptName">The name of the script file being analyzed</param>
96-
public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptName)
106+
public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptName, string ruleId = null)
97107
{
98108
Message = string.IsNullOrEmpty(message) ? string.Empty : message;
99109
RuleName = string.IsNullOrEmpty(ruleName) ? string.Empty : ruleName;
100110
Extent = extent;
101111
Severity = severity;
102112
ScriptName = string.IsNullOrEmpty(scriptName) ? string.Empty : scriptName;
113+
ruleSuppressionId = ruleId;
103114
}
104115
}
105116

Engine/Generic/ICommandRule.cs

Lines changed: 0 additions & 37 deletions
This file was deleted.

Engine/Generic/ILogger.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ public interface ILogger
2727
/// <summary>
2828
/// LogMessage: Logs the given diagnostic, using the command for Write methods if needed.
2929
/// </summary>
30-
/// <param name="diagnostic">The DiagnosticRecord to be logged.</param>
30+
/// <param name="obj">The object to be logged.</param>
3131
/// <param name="command">The InvokePSScriptAnalyzerCommand that this logger is running off of.</param>
32-
void LogMessage(DiagnosticRecord diagnostic, InvokeScriptAnalyzerCommand command);
32+
void LogObject(Object obj, InvokeScriptAnalyzerCommand command);
3333

3434
/// <summary>
3535
/// GetName: Retrieves the name of the logger.

0 commit comments

Comments
 (0)