Description
For some cases the formatter is changing code where it cannot run. Rules that trigger this are:
PSUseConsistentWhitespace.CheckParameter
when$True
PSAvoidUsingCmdletAliases
when$True
The source script triggering this bug is not valid, however
- the
PSAvoidUsingCmdletAliases
:pwsh
will execute this with no errors (It runs normally, it never assigns$x
) - the
CheckParameter
: This code is broken,pwsh
will not execute this
Possible Cause of PSAvoidUsingCmdletAliases
From the docs: If your function defines a
Begin
,Process
orEnd
block, all of your code must reside inside those blocks. No code will be recognized outside the blocks if any of the blocks are defined.
I think the cause is the script reaches a statement that is not inside a begin/process/end
block -- so it assumes the function is a non-pipeline function. In that context, process
is not a keyword but an identifier. It replaces process
with Get-Process
(which would the correct behavior if it was a normal function)
Invoke-ScriptAnalyzer
has the same behavior.
Potential fix for PSAvoidUsingCmdletAliases
?
- PSSA should have a rule to check for functions that have code outside
begin/process/end
blocks. - Can the parser test if there are any
begin/process/end
statements, then treat it as a pipeline function? - Can the formatter abort if errors occur? (Right now there's no errors on mutation)
Steps to reproduce
$script = @'
function Do-Stuff {
$x = 2
Process { }
}
'@
$scriptFixed = @'
function Do-Stuff {
#$x = 2
Process { }
}
'@
$settings = @{
IncludeRules = @('PSUseCorrectCasing', 'PSUseConsistentIndentation')
Rules = @{
PSAvoidUsingCmdletAliases = @{
Enable = $false
}
PSUseConsistentIndentation = @{
Enable = $true
Kind = 'space'
PipelineIndentation = 'IncreaseIndentationAfterEveryPipeline'
IndentationSize = 4
}
}
}
Invoke-Formatter -ScriptDefinition $script -Settings $settings
Invoke-Formatter -ScriptDefinition $scriptFixed -Settings $settings
Expected behavior
Indent and replace aliases.
function Do-Stuff {
$x = 2
Process { }
}
function Do-Stuff {
#$x = 2
Process { }
}
Actual behavior
Indent, and replaces keyword as if it was not in the keyword context.
function Do-Stuff {
$x = 2
Get-Process { }
}
function Do-Stuff {
#$x = 2
Process { }
}
Another example not using pipeline functions
Steps to reproduce
$Original = @'
Function BadCode {
"stuff" | Format-CodeColor" 'ps1'
$InputList | ForEach-Object {
} | Select-Object -First 2
| Join-String -sep ", " -OutputPrefix 'Results: '
}
'@
$settings = @{
IncludeRules = @(
"PSUseConsistentWhitespace"
)
Rules = @{
PSUseConsistentWhitespace = @{
Enable = $True
CheckParameter = $false
}
}
}
$out1 = Invoke-Formatter -ScriptDefinition $Original -Settings $settings
$settings.rules.PSUseConsistentWhitespace.CheckParameter = $True
$out2 = Invoke-Formatter -ScriptDefinition $Original -Settings $settings
$out1
'+' * 30
$out2
if ($out1 -ne $out2) {
Write-Error 'formatting does not match'
}
Expected behavior 2
Give an error, or don't mutate code
Actual behavior 2
Function BadCode {
"`nPS> Top | Bot | Se" -OutputPrefix 'Results: '
}
Environment data
>> $PSVersionTable | ft
Name Value
---- -----
PSVersion 7.0.3
PSEdition Core
GitCommitId 7.0.3
OS Microsoft Windows 10.0.19041
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
>> (Get-Module -ListAvailable PSScriptAnalyzer).Version | ForEach-Object { $_.ToString() }
1.19.1
>> $psEditor | select EditorServicesVersion
EditorServicesVersion
---------------------
2.3.0.0