diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index a444327e0..f15dde699 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -31,6 +31,8 @@ public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter private const string ParameterSet_Path_IncludeSuppressed = nameof(Path) + "_" + nameof(IncludeSuppressed); private const string ParameterSet_ScriptDefinition_SuppressedOnly = nameof(ScriptDefinition) + "_" + nameof(SuppressedOnly); private const string ParameterSet_ScriptDefinition_IncludeSuppressed = nameof(ScriptDefinition) + "_" + nameof(IncludeSuppressed); + private const string ParameterSet_ScriptBlock_SuppressedOnly = nameof(ScriptBlock) + "_" + nameof(SuppressedOnly); + private const string ParameterSet_ScriptBlock_IncludeSuppressed = nameof(ScriptBlock) + "_" + nameof(IncludeSuppressed); #region Private variables List processedPaths; @@ -65,15 +67,13 @@ public string Path /// /// ScriptDefinition: a script definition in the form of a string to run rules on. /// - [Parameter(Position = 0, + [Parameter( ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed, Mandatory = true, - ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [Parameter(Position = 0, + [Parameter( ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly, Mandatory = true, - ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNull] public string ScriptDefinition @@ -83,6 +83,31 @@ public string ScriptDefinition } private string scriptDefinition; + /// + /// ScriptBlock: a script block to run rules on. + /// + [Parameter(Position = 0, + ParameterSetName = ParameterSet_ScriptBlock_IncludeSuppressed, + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, + ParameterSetName = ParameterSet_ScriptBlock_SuppressedOnly, + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [ValidateNotNull] + public ScriptBlock ScriptBlock + { + get { return scriptBlock; } + set + { + scriptBlock = value; + scriptDefinition = value?.ToString(); + } + } + private ScriptBlock scriptBlock; + /// /// CustomRulePath: The path to the file containing custom rules to run. /// @@ -179,6 +204,7 @@ public SwitchParameter Recurse /// [Parameter(ParameterSetName = ParameterSet_Path_SuppressedOnly)] [Parameter(ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly)] + [Parameter(ParameterSetName = ParameterSet_ScriptBlock_SuppressedOnly)] public SwitchParameter SuppressedOnly { get; set; } /// @@ -186,6 +212,7 @@ public SwitchParameter Recurse /// [Parameter(ParameterSetName = ParameterSet_Path_IncludeSuppressed, Mandatory = true)] [Parameter(ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed, Mandatory = true)] + [Parameter(ParameterSetName = ParameterSet_ScriptBlock_IncludeSuppressed, Mandatory = true)] public SwitchParameter IncludeSuppressed { get; set; } /// diff --git a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 index b930c9980..86ea792b6 100644 --- a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 +++ b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 @@ -27,7 +27,7 @@ Describe "Test available parameters" { } } - Context "Path parameter" { + Context "ScriptDefinition parameter" { It "has a ScriptDefinition parameter" { $params.ContainsKey("ScriptDefinition") | Should -BeTrue } @@ -37,6 +37,16 @@ Describe "Test available parameters" { } } + Context "ScriptBlock parameter" { + It "has a ScriptBlock parameter" { + $params.ContainsKey("ScriptBlock") | Should -BeTrue + } + + It "accepts ScriptBlock" { + $params["ScriptBlock"].ParameterType.FullName | Should -Be "System.Management.Automation.ScriptBlock" + } + } + Context "CustomRulePath parameters" { It "has a CustomRulePath parameter" { $params.ContainsKey("CustomRulePath") | Should -BeTrue @@ -85,10 +95,12 @@ Describe "Test available parameters" { } } - It "Has 4 parameter sets" { + It "Has 6 parameter sets" { $parameterSets = @( 'Path_IncludeSuppressed' 'Path_SuppressedOnly' + 'ScriptBlock_IncludeSuppressed' + 'ScriptBlock_SuppressedOnly' 'ScriptDefinition_IncludeSuppressed' 'ScriptDefinition_SuppressedOnly' ) @@ -108,6 +120,16 @@ Describe "Test ScriptDefinition" { } } +Describe "Test ScriptBlock" { + Context "When given a script block" { + It "Runs rules on script with 1 warning" { + # this is a script with 1 error (var declared but not used) + $violations = Invoke-ScriptAnalyzer {$var = 1} + $violations.Count | Should -Be 1 + } + } +} + Describe "Test Path" { Context "When given a single file" { It "Has the same effect as without Path parameter" { diff --git a/Tests/Engine/LibraryUsage.tests.ps1 b/Tests/Engine/LibraryUsage.tests.ps1 index f8f30226b..a720b2226 100644 --- a/Tests/Engine/LibraryUsage.tests.ps1 +++ b/Tests/Engine/LibraryUsage.tests.ps1 @@ -18,6 +18,10 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { [parameter(Mandatory = $true, ParameterSetName="ScriptDefinition_IncludeSuppressed")] [string] $ScriptDefinition, + [parameter(Mandatory = $true, ParameterSetName="ScriptBlock_SuppressedOnly")] + [parameter(Mandatory = $true, ParameterSetName="ScriptBlock_IncludeSuppressed")] + [scriptblock] $ScriptBlock, + [Parameter(Mandatory = $false)] [Alias("CustomizedRulePath")] [string[]] $CustomRulePath = $null, @@ -43,10 +47,12 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { [Parameter(Mandatory = $false, ParameterSetName = "Path_SuppressedOnly")] [Parameter(Mandatory = $false, ParameterSetName = "ScriptDefinition_SuppressedOnly")] + [Parameter(Mandatory = $false, ParameterSetName = "ScriptBlock_SuppressedOnly")] [switch] $SuppressedOnly, [Parameter(Mandatory, ParameterSetName = "Path_IncludeSuppressed")] [Parameter(Mandatory, ParameterSetName = "ScriptDefinition_IncludeSuppressed")] + [Parameter(Mandatory, ParameterSetName = "ScriptBlock_IncludeSuppressed")] [switch] $IncludeSuppressed, [Parameter(Mandatory = $false)] @@ -97,6 +103,9 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { } else { + if ($null -ne $ScriptBlock) { + $ScriptDefinition = $ScriptBlock.ToString() + } $results = $scriptAnalyzer.AnalyzeScriptDefinition($ScriptDefinition, [ref] $null, [ref] $null) } diff --git a/Tests/Engine/ModuleHelp.Tests.ps1 b/Tests/Engine/ModuleHelp.Tests.ps1 index ac40dcf94..baa833d8e 100644 --- a/Tests/Engine/ModuleHelp.Tests.ps1 +++ b/Tests/Engine/ModuleHelp.Tests.ps1 @@ -240,8 +240,9 @@ Describe 'Cmdlet parameter help' { $parameterHelpPipelineInput = if ($parameterHelp.pipelineInput -eq 'True (ByPropertyName, ByValue)') { $true - } - else { + } elseif ($parameterHelp.pipelineInput -eq 'True (ByPropertyName)') { + $true + } else { [System.Boolean]::Parse($parameterHelp.pipelineInput) } diff --git a/docs/Cmdlets/Invoke-ScriptAnalyzer.md b/docs/Cmdlets/Invoke-ScriptAnalyzer.md index b3e72a337..823e1da35 100644 --- a/docs/Cmdlets/Invoke-ScriptAnalyzer.md +++ b/docs/Cmdlets/Invoke-ScriptAnalyzer.md @@ -51,6 +51,25 @@ Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [] ``` +### ScriptBlock_IncludeSuppressed + +``` +Invoke-ScriptAnalyzer [-ScriptBlock] -IncludeSuppressed [-CustomRulePath ] + [-RecurseCustomRulePath] [-IncludeDefaultRules] [-ExcludeRule ] + [-IncludeRule ] [-Severity ] [-Recurse] [-EnableExit] [-Settings ] + [-SaveDscDependency] [-ReportSummary] [-WhatIf] [-Confirm] [] +``` + +### ScriptBlock_SuppressedOnly + +``` +Invoke-ScriptAnalyzer [-ScriptBlock] [-CustomRulePath ] + [-RecurseCustomRulePath] [-IncludeDefaultRules] [-ExcludeRule ] + [-IncludeRule ] [-Severity ] [-Recurse] [-SuppressedOnly] [-EnableExit] + [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] [-Confirm] + [] +``` + ## DESCRIPTION `Invoke-ScriptAnalyzer` evaluates scripts or module files (`.ps1`, `.psm1`, and `.psd1` files) based @@ -156,9 +175,9 @@ Invoke-ScriptAnalyzer -Path .\Get-Widgets.ps1 ``` ```Output -RuleName Severity FileName Line Message --------- -------- -------- ---- ------- -PSProvideCommentHelp Information ManageProf 14 The cmdlet 'Get-Widget' does not have a help comment. +RuleName Severity ScriptName Line Message +-------- -------- -------- ---- ------- +PSProvideCommentHelp Information ManageProf 14 The cmdlet 'Get-Widget' does not have a help comment. iles.psm1 ``` @@ -167,11 +186,11 @@ Invoke-ScriptAnalyzer -Path .\Get-Widgets.ps1 -SuppressedOnly ``` ```Output -Rule Name Severity File Name Line Justification ---------- -------- --------- ---- ------------- -PSAvoidUsingCmdletAliases Warning ManageProf 21 Resolution in progress. +Rule Name Severity ScriptName Line Justification +--------- -------- --------- ---- ------------- +PSAvoidUsingCmdletAliases Warning ManageProf 21 Resolution in progress. iles.psm1 -PSUseSingularNouns Warning ManageProf 14 +PSUseSingularNouns Warning ManageProf 14 iles.psm1 ``` @@ -208,20 +227,51 @@ Invoke-ScriptAnalyzer -ScriptDefinition "function Get-Widgets {Write-Host 'Hello ``` ```Output -RuleName Severity FileName Line Message --------- -------- -------- ---- ------- -PSAvoidUsingWriteHost Warning 1 Script - because - there i - suppres - Write-O -PSUseSingularNouns Warning 1 The cmd - noun sh +RuleName Severity ScriptName Line Message +-------- -------- -------- ---- ------- +PSAvoidUsingWriteHost Warning 1 Script + because + there i + suppres + Write-O +PSUseSingularNouns Warning 1 The cmd + noun sh ``` When you use the **ScriptDefinition** parameter, the **FileName** property of the **DiagnosticRecord** object is `$null`. +### EXAMPLE 10 - Analyze a script block + +This example uses the **ScriptBlock** parameter to analyze a function at the command line. The +function is defined within a script block. + +```powershell +Invoke-ScriptAnalyzer -ScriptBlock {function Get-Widgets {Write-Host 'Hello'}} +``` + +```Output + +RuleName Severity ScriptName Line Message +-------- -------- ---------- ---- ------- +PSUseSingularNouns Warning 1 The cmdlet 'Get-Widgets' uses a plural + noun. A singular noun should be used + instead. +PSAvoidUsingWriteHost Warning 1 Script definition uses Write-Host. + Avoid using Write-Host because it might + not work in all hosts, does not work + when there is no host, and (prior to PS + 5.0) cannot be suppressed, captured, or + redirected. Instead, use Write-Output, + Write-Verbose, or Write-Information. + + + +``` + +When you use the **ScriptBlock** parameter, the **ScriptName** property of the +**DiagnosticRecord** object is `$null`. + ## PARAMETERS ### -CustomRulePath @@ -362,7 +412,7 @@ Include suppressed diagnostics in output. ```yaml Type: SwitchParameter -Parameter Sets: Path_IncludeSuppressed, ScriptDefinition_IncludeSuppressed +Parameter Sets: Path_IncludeSuppressed, ScriptBlock_IncludeSuppressed, ScriptDefinition_IncludeSuppressed Aliases: Required: True @@ -472,6 +522,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ScriptBlock + +Runs the analysis on commands, functions, or expressions on a scriptblock. You can use this feature to +analyze statements, expressions, and functions, independent of their script context. + +```yaml +Type: ScriptBlock +Parameter Sets: ScriptBlock_IncludeSuppressed, ScriptBlock_SuppressedOnly +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + ### -ScriptDefinition Runs the analysis on commands, functions, or expressions in a string. You can use this feature to @@ -483,9 +550,9 @@ Parameter Sets: ScriptDefinition_IncludeSuppressed, ScriptDefinition_SuppressedO Aliases: Required: True -Position: 0 +Position: Named Default value: None -Accept pipeline input: True (ByPropertyName, ByValue) +Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` @@ -569,7 +636,7 @@ To suppress a rule, use the **SuppressMessageAttribute**. For help, see the exam ```yaml Type: SwitchParameter -Parameter Sets: Path_SuppressedOnly, ScriptDefinition_SuppressedOnly +Parameter Sets: Path_SuppressedOnly, ScriptBlock_SuppressedOnly, ScriptDefinition_SuppressedOnly Aliases: Required: False @@ -620,9 +687,13 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## INPUTS -### None +### String + +You can pipe a string representing a script path to this cmdlet. The string is bound to the `-Path` parameter by value. + +### ScriptBlock -You cannot pipe input to this cmdlet. +You can pipe a script block to this cmdlet. The script block is bound to the `-ScriptBlock` parameter by value. ## OUTPUTS