diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs index b39db8b87..4d9eacc63 100644 --- a/Engine/ScriptAnalyzer.cs +++ b/Engine/ScriptAnalyzer.cs @@ -1455,6 +1455,28 @@ private Tuple, List> SuppressRule( return records; } + /// + /// Wrapper around SuppressRule method. + /// + /// This enables suppressing external rules (written in PowerShell). + /// Since there can be an inconsistency between the rule name in + /// diagnostic record and the actual rule (function) name, we use + /// the diagnostic record rule name for suppression + /// + /// + /// + /// Returns a tuple of suppressed and diagnostic records + private Tuple, List> SuppressRule( + Dictionary> ruleSuppressions, + DiagnosticRecord ruleDiagnosticRecord + ) + { + return SuppressRule( + ruleDiagnosticRecord.RuleName, + ruleSuppressions, + new List { ruleDiagnosticRecord }); + } + /// /// Analyzes the syntax tree of a script file that has already been parsed. /// @@ -1752,13 +1774,21 @@ public IEnumerable AnalyzeSyntaxTree( } } - foreach (var record in this.GetExternalRecord(scriptAst, scriptTokens, exRules.ToArray(), fileName)) + foreach (var ruleRecord in this.GetExternalRecord(scriptAst, scriptTokens, exRules.ToArray(), fileName)) { - diagnostics.Add(record); + var records = SuppressRule(ruleSuppressions, ruleRecord); + foreach (var record in records.Item2) + { + diagnostics.Add(record); + } + foreach (var suppressedRec in records.Item1) + { + suppressed.Add(suppressedRec); + } } } - -#endregion + + #endregion // Need to reverse the concurrentbag to ensure that results are sorted in the increasing order of line numbers IEnumerable diagnosticsList = diagnostics.Reverse(); diff --git a/Tests/Engine/RuleSuppression.tests.ps1 b/Tests/Engine/RuleSuppression.tests.ps1 index 0ad171eab..3f31e4619 100644 --- a/Tests/Engine/RuleSuppression.tests.ps1 +++ b/Tests/Engine/RuleSuppression.tests.ps1 @@ -114,13 +114,29 @@ function SuppressPwdParam() if (!$testingLibraryUsage) { - Context "Bad Rule Suppression" { - It "Throws a non-terminating error" { - Invoke-ScriptAnalyzer -ScriptDefinition $ruleSuppressionBad -IncludeRule "PSAvoidUsingUserNameAndPassWordParams" -ErrorVariable errorRecord -ErrorAction SilentlyContinue - $errorRecord.Count | Should Be 1 - $errorRecord.FullyQualifiedErrorId | Should match "suppression message attribute error" - } - } + Context "Bad Rule Suppression" { + It "Throws a non-terminating error" { + Invoke-ScriptAnalyzer -ScriptDefinition $ruleSuppressionBad -IncludeRule "PSAvoidUsingUserNameAndPassWordParams" -ErrorVariable errorRecord -ErrorAction SilentlyContinue + $errorRecord.Count | Should Be 1 + $errorRecord.FullyQualifiedErrorId | Should match "suppression message attribute error" + } + } + + Context "External Rule Suppression" { + $externalRuleSuppression = @' +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('CommunityAnalyzerRules\Measure-WriteHost','')] +param() # without the param block, powershell parser throws up! +Write-Host "write-host" +'@ + It "Suppresses violation of an external ast rule" { + Invoke-ScriptAnalyzer ` + -ScriptDefinition $externalRuleSuppression ` + -CustomRulePath (Join-Path $directory "CommunityAnalyzerRules") ` + -OutVariable ruleViolations ` + -SuppressedOnly + $ruleViolations.Count | Should Be 1 + } + } } } diff --git a/build.ps1 b/build.ps1 index 22d1337d0..9af1a0d60 100644 --- a/build.ps1 +++ b/build.ps1 @@ -110,8 +110,12 @@ if ($BuildDocs) New-ExternalHelp -Path $markdownDocsPath -OutputPath $outputDocsPath -Force -Verbose:$verbosity } - -$moduleRootPath = Join-Path (Split-Path $profile) 'Modules' +# Appveyor errors out due to $profile being null. Hence... +$moduleRootPath = "$HOME/Documents/WindowsPowerShell/Modules" +if ($profile -ne $null) +{ + $moduleRootPath = Join-Path (Split-Path $profile) 'Modules' +} $modulePSSAPath = Join-Path $moduleRootPath 'PSScriptAnalyzer' if ($Install) {