From 3b18b78dbe3c6632cb472886efbb13055b327934 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Mon, 14 May 2018 19:11:53 +0100 Subject: [PATCH 1/4] Populate SuggestedCorrections when using scriptule and make it public for easier construction in PowerShell --- Engine/Generic/DiagnosticRecord.cs | 3 ++- Engine/ScriptAnalyzer.cs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Engine/Generic/DiagnosticRecord.cs b/Engine/Generic/DiagnosticRecord.cs index ca3c3c882..ea3f7d60d 100644 --- a/Engine/Generic/DiagnosticRecord.cs +++ b/Engine/Generic/DiagnosticRecord.cs @@ -18,7 +18,7 @@ public class DiagnosticRecord private DiagnosticSeverity severity; private string scriptPath; private string ruleSuppressionId; - private List suggestedCorrections; + private IEnumerable suggestedCorrections; /// /// Represents a string from the rule about why this diagnostic was created. @@ -89,6 +89,7 @@ public string RuleSuppressionID public IEnumerable SuggestedCorrections { get { return suggestedCorrections; } + set { suggestedCorrections = value; } } /// diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs index 1c6a64fff..ae6e22255 100644 --- a/Engine/ScriptAnalyzer.cs +++ b/Engine/ScriptAnalyzer.cs @@ -1267,6 +1267,7 @@ internal IEnumerable GetExternalRecord(Ast ast, Token[] token, IScriptExtent extent; string message = string.Empty; string ruleName = string.Empty; + IEnumerable suggestedCorrections; if (psobject != null && psobject.ImmediateBaseObject != null) { @@ -1286,6 +1287,7 @@ internal IEnumerable GetExternalRecord(Ast ast, Token[] token, message = psobject.Properties["Message"].Value.ToString(); extent = (IScriptExtent)psobject.Properties["Extent"].Value; ruleName = psobject.Properties["RuleName"].Value.ToString(); + suggestedCorrections = (IEnumerable)psobject.Properties["SuggestedCorrections"].Value; } catch (Exception ex) { @@ -1295,7 +1297,7 @@ internal IEnumerable GetExternalRecord(Ast ast, Token[] token, if (!string.IsNullOrEmpty(message)) { - diagnostics.Add(new DiagnosticRecord(message, extent, ruleName, severity, filePath)); + diagnostics.Add(new DiagnosticRecord(message, extent, ruleName, severity, filePath) { SuggestedCorrections = suggestedCorrections }); } } } From 8eb354e22ce18100ebbb635f7d44cf36285c664f Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Mon, 14 May 2018 20:54:19 +0100 Subject: [PATCH 2/4] add documentation and test --- Engine/Generic/DiagnosticRecord.cs | 2 +- ScriptRuleDocumentation.md | 23 ++++++++++++++++++++++- Tests/Engine/CustomizedRule.tests.ps1 | 13 +++++++++++++ Tests/Engine/samplerule/samplerule.psm1 | 9 ++++++--- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/Engine/Generic/DiagnosticRecord.cs b/Engine/Generic/DiagnosticRecord.cs index ea3f7d60d..28d0e87bd 100644 --- a/Engine/Generic/DiagnosticRecord.cs +++ b/Engine/Generic/DiagnosticRecord.cs @@ -109,7 +109,7 @@ public DiagnosticRecord() /// The severity of this diagnostic /// The full path of the script file being analyzed /// The correction suggested by the rule to replace the extent text - public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null, List suggestedCorrections = null) + public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null, IEnumerable suggestedCorrections = null) { Message = message; RuleName = ruleName; diff --git a/ScriptRuleDocumentation.md b/ScriptRuleDocumentation.md index dca9151e8..45e558ef7 100644 --- a/ScriptRuleDocumentation.md +++ b/ScriptRuleDocumentation.md @@ -51,7 +51,7 @@ Param ) ``` -- DiagnosticRecord should have four properties: Message, Extent, RuleName and Severity +- DiagnosticRecord should have at least four properties: Message, Extent, RuleName and Severity ``` PowerShell $result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{ @@ -61,6 +61,27 @@ $result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[ "Severity" = "Warning" } ``` +Optionally, since version 1.17.0, a `SuggestedCorrections` property of type `IEnumerable` can also be added in script rules but care must be taken that the type is correct, an example is: +```powershell +[int]$startLineNumber = $ast.Extent.StartLineNumber +[int]$endLineNumber = $ast.Extent.EndLineNumber +[int]$startColumnNumber = $ast.Extent.StartColumnNumber +[int]$endColumnNumber = $ast.Extent.EndColumnNumber +[string]$correction = 'Correct text that replaces Extent text' +[string]$file = $MyInvocation.MyCommand.Definition +[string]$optionalDescription = 'Useful but optional description text' +$correctionExtent = New-Object 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent' $startLineNumber,$endLineNumber,$startColumnNumber,$endColumnNumber,$correction,$description +$suggestedCorrections = New-Object System.Collections.ObjectModel.Collection['Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent'] +$suggestedCorrections.add($correctionExtent) | out-null + +[Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ + "Message" = "This is a rule with a suggested correction" + "Extent" = $ast.Extent + "RuleName" = $PSCmdlet.MyInvocation.InvocationName + "Severity" = "Warning" + "SuggestedCorrections" = $suggestedCorrections +} +``` - Make sure you export the function(s) at the end of the script using Export-ModuleMember diff --git a/Tests/Engine/CustomizedRule.tests.ps1 b/Tests/Engine/CustomizedRule.tests.ps1 index 3ad6225ad..2062f995a 100644 --- a/Tests/Engine/CustomizedRule.tests.ps1 +++ b/Tests/Engine/CustomizedRule.tests.ps1 @@ -149,6 +149,19 @@ Describe "Test importing correct customized rules" { $violations[0].ScriptPath | Should -Be $expectedScriptPath } + It "will set SuggestedCorrections" { + $violations = Invoke-ScriptAnalyzer $directory\TestScript.ps1 -CustomizedRulePath $directory\samplerule + $expectedScriptPath = Join-Path $directory 'TestScript.ps1' + $violations[0].SuggestedCorrections | Should -Not -BeNullOrEmpty + $violations[0].SuggestedCorrections.StartLineNumber | Should -Be 1 + $violations[0].SuggestedCorrections.EndLineNumber | Should -Be 2 + $violations[0].SuggestedCorrections.StartColumnNumber | Should -Be 3 + $violations[0].SuggestedCorrections.EndColumnNumber | Should -Be 4 + $violations[0].SuggestedCorrections.Text | Should -Be 'text' + $violations[0].SuggestedCorrections.File | Should -Be 'filePath' + $violations[0].SuggestedCorrections.Description | Should -Be 'description' + } + if (!$testingLibraryUsage) { It "will show the custom rule in the results when given a rule folder path with trailing backslash" { diff --git a/Tests/Engine/samplerule/samplerule.psm1 b/Tests/Engine/samplerule/samplerule.psm1 index d3cc6516d..6cf0ea1fd 100644 --- a/Tests/Engine/samplerule/samplerule.psm1 +++ b/Tests/Engine/samplerule/samplerule.psm1 @@ -28,9 +28,12 @@ function Measure-RequiresRunAsAdministrator [System.Management.Automation.Language.ScriptBlockAst] $testAst ) - $dr = New-Object ` + $l=(new-object System.Collections.ObjectModel.Collection["Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent"]) + $c = (new-object Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent 1,2,3,4,'text','filePath','description') + $l.Add($c) + $dr = New-Object ` -Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" ` - -ArgumentList "This is help",$ast.Extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null - return @($dr) + -ArgumentList "This is help",$ast.Extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null,$null,$l + return $dr } Export-ModuleMember -Function Measure* \ No newline at end of file From aa90ea3113161de9bbbff56f95afb923fcc2900b Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Thu, 17 May 2018 22:16:46 +0100 Subject: [PATCH 3/4] use [Type]::new() constructor instead of new-object --- ScriptRuleDocumentation.md | 6 +++--- Tests/Engine/samplerule/samplerule.psm1 | 18 +++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ScriptRuleDocumentation.md b/ScriptRuleDocumentation.md index 45e558ef7..775c42c6d 100644 --- a/ScriptRuleDocumentation.md +++ b/ScriptRuleDocumentation.md @@ -70,9 +70,9 @@ Optionally, since version 1.17.0, a `SuggestedCorrections` property of type `IEn [string]$correction = 'Correct text that replaces Extent text' [string]$file = $MyInvocation.MyCommand.Definition [string]$optionalDescription = 'Useful but optional description text' -$correctionExtent = New-Object 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent' $startLineNumber,$endLineNumber,$startColumnNumber,$endColumnNumber,$correction,$description -$suggestedCorrections = New-Object System.Collections.ObjectModel.Collection['Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent'] -$suggestedCorrections.add($correctionExtent) | out-null +$correctionExtent = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]::new($startLineNumber,$endLineNumber,$startColumnNumber,$endColumnNumber,$correction,$description) +$suggestedCorrections = [System.Collections.ObjectModel.Collection['Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent']]::new() +$suggestedCorrections.Add($correctionExtent) [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ "Message" = "This is a rule with a suggested correction" diff --git a/Tests/Engine/samplerule/samplerule.psm1 b/Tests/Engine/samplerule/samplerule.psm1 index 6cf0ea1fd..732907129 100644 --- a/Tests/Engine/samplerule/samplerule.psm1 +++ b/Tests/Engine/samplerule/samplerule.psm1 @@ -28,12 +28,16 @@ function Measure-RequiresRunAsAdministrator [System.Management.Automation.Language.ScriptBlockAst] $testAst ) - $l=(new-object System.Collections.ObjectModel.Collection["Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent"]) - $c = (new-object Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent 1,2,3,4,'text','filePath','description') - $l.Add($c) - $dr = New-Object ` - -Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" ` - -ArgumentList "This is help",$ast.Extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null,$null,$l - return $dr + $correctionExtentList = [System.Collections.ObjectModel.Collection[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]]::new() + $correctionExtent = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]::new(1,2,3,4,'text','filePath','description') + $correctionExtentList.Add($correctionExtent) + $diagnosticRecord = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]::new( + "This is help", + $ast.Extent,$PSCmdlet.MyInvocation.InvocationName, + "Warning", + $null, + $null, + $correctionExtentList) + return $diagnosticRecord } Export-ModuleMember -Function Measure* \ No newline at end of file From 905e2defab84214c8aa4926cab7e2457b79aa642 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Thu, 17 May 2018 22:58:09 +0100 Subject: [PATCH 4/4] Revert "use [Type]::new() constructor instead of new-object" Reason: this was only introduced in PowerShell v5 and therefore fails the PS v4 build and we should not give examples that do not work on any supported version This reverts commit aa90ea3113161de9bbbff56f95afb923fcc2900b. --- ScriptRuleDocumentation.md | 6 +++--- Tests/Engine/samplerule/samplerule.psm1 | 18 +++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ScriptRuleDocumentation.md b/ScriptRuleDocumentation.md index 775c42c6d..45e558ef7 100644 --- a/ScriptRuleDocumentation.md +++ b/ScriptRuleDocumentation.md @@ -70,9 +70,9 @@ Optionally, since version 1.17.0, a `SuggestedCorrections` property of type `IEn [string]$correction = 'Correct text that replaces Extent text' [string]$file = $MyInvocation.MyCommand.Definition [string]$optionalDescription = 'Useful but optional description text' -$correctionExtent = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]::new($startLineNumber,$endLineNumber,$startColumnNumber,$endColumnNumber,$correction,$description) -$suggestedCorrections = [System.Collections.ObjectModel.Collection['Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent']]::new() -$suggestedCorrections.Add($correctionExtent) +$correctionExtent = New-Object 'Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent' $startLineNumber,$endLineNumber,$startColumnNumber,$endColumnNumber,$correction,$description +$suggestedCorrections = New-Object System.Collections.ObjectModel.Collection['Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent'] +$suggestedCorrections.add($correctionExtent) | out-null [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ "Message" = "This is a rule with a suggested correction" diff --git a/Tests/Engine/samplerule/samplerule.psm1 b/Tests/Engine/samplerule/samplerule.psm1 index 732907129..6cf0ea1fd 100644 --- a/Tests/Engine/samplerule/samplerule.psm1 +++ b/Tests/Engine/samplerule/samplerule.psm1 @@ -28,16 +28,12 @@ function Measure-RequiresRunAsAdministrator [System.Management.Automation.Language.ScriptBlockAst] $testAst ) - $correctionExtentList = [System.Collections.ObjectModel.Collection[Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]]::new() - $correctionExtent = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent]::new(1,2,3,4,'text','filePath','description') - $correctionExtentList.Add($correctionExtent) - $diagnosticRecord = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]::new( - "This is help", - $ast.Extent,$PSCmdlet.MyInvocation.InvocationName, - "Warning", - $null, - $null, - $correctionExtentList) - return $diagnosticRecord + $l=(new-object System.Collections.ObjectModel.Collection["Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent"]) + $c = (new-object Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent 1,2,3,4,'text','filePath','description') + $l.Add($c) + $dr = New-Object ` + -Typename "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord" ` + -ArgumentList "This is help",$ast.Extent,$PSCmdlet.MyInvocation.InvocationName,Warning,$null,$null,$l + return $dr } Export-ModuleMember -Function Measure* \ No newline at end of file