Closed
Description
Summary of the new feature
In response to questions seen on Twitter, like this one: https://twitter.com/_bateskevin/status/1225536662769405952?s=20
As a PowerShell 7 user, I would like to be warned when I use variables incorrectly with ForEach-Object -Parallel
, so I don't have to wonder why I get an error related to a non-initialized variable.
Example with incorrect variable usage:
$myVar = 10
1..2 | ForEach-Object -Parallel {
$myVar | Get-Member
}
I would like PSScriptAnalyzer to warn me that $myVar
is not going to work. (I need to use $using:myVar)
Proposed technical implementation details (optional)
A quick proof of context:
function Test-ForeachParallelWarningForNonAssignedNonUsingVars {
param(
$script
)
if ($script -is [scriptblock]) {
$ScriptBlocks = $script.Ast.FindAll( {
$args[0] -is [System.Management.Automation.Language.ScriptBlockAst]
}, $true)
}
else {
$ScriptBlocks = [scriptblock]::Create($script).Ast.FindAll( {
$args[0] -is [System.Management.Automation.Language.ScriptBlockAst]
}, $true)
}
foreach ($ScriptBlockAst in $ScriptBlocks) {
$foreachObjectParallelCommandAst = $ScriptBlockAst.FindAll( {
$args[0] -is [System.Management.Automation.Language.CommandAst] -and
$args[0].GetCommandName() -match "^foreach$|^%$|^foreach-object$" -and
$args[0].CommandElements.ParameterName -like "pa*"
}, $false)
foreach ($parallelCommandAst in $foreachObjectParallelCommandAst) {
$assignments = $parallelCommandAst.CommandElements.FindAll({$args[0] -is [System.Management.Automation.Language.AssignmentStatementAst]}, $true)
$varsInAssignments = $assignments | ForEach-Object { $_.Left.FindAll( { $args[0] -is [System.Management.Automation.Language.VariableExpressionAst]}, $true)}
$nonAssignedNonUsingVars = $parallelCommandAst.CommandElements.FindAll({
$args[0] -is [System.Management.Automation.Language.VariableExpressionAst] -and
-not ($args[0].Parent -is [System.Management.Automation.Language.UsingExpressionAst]) -and
$varsInAssignments -notcontains $args[0]
}, $true)
return $nonAssignedNonUsingVars
}
}
}
## tests
Describe "Test-ForeachParallelWarningForNonAssignedNonUsingVars" {
Context "Should detect something" {
$testcases = @(
@{
Description = "Foreach-Object -Parallel with undeclared var"
ScriptBlock = {
1..2 | ForEach-Object -Parallel { $var }
}
}
@{
Description = "alias foreach -parallel with undeclared var"
ScriptBlock = {
1..2 | ForEach -Parallel { $var }
}
}
@{
Description = "alias % -parallel with undeclared var"
ScriptBlock = {
1..2 | % -Parallel { $var }
}
}
@{
Description = "abbreviated param Foreach-Object -pa with undeclared var"
ScriptBlock = {
1..2 | foreach-object -pa { $var }
}
}
@{
Description = "Nested Foreach-Object -Parallel with undeclared var"
ScriptBlock = {
$myNestedScriptBlock = {
1..2 | ForEach-Object -Parallel { $var }
}
}
}
)
it "should emit for: <Description>" -TestCases $testcases {
param($Description, $ScriptBlock)
Test-ForeachParallelWarningForNonAssignedNonUsingVars $ScriptBlock | Should -Be "`$var"
}
}
Context "Should not detect anything" {
$testcases = @(
@{
Description = "Foreach-Object with uninitialized var inside"
ScriptBlock = {
1..2 | ForEach-Object { $var }
}
}
@{
Description = "Foreach-Object -Parallel with uninitialized `$using: var"
ScriptBlock = {
1..2 | foreach-object -Parallel { $using:var }
}
}
@{
Description = "Foreach-Object -Parallel with var assigned locally"
ScriptBlock = {
1..2 | ForEach-Object -Parallel { $var="somevalue" }
}
}
@{
<# Not implemented #>
Description = "Foreach-Object -Parallel with built-in var '`$Args' inside"
ScriptBlock = {
1..2 | ForEach-Object { $Args[0] } -ArgumentList "a" -Parallel
}
}
)
it "should not emit anything for: <Description>" -TestCases $testcases {
param($Description, $ScriptBlock)
Test-ForeachParallelWarningForNonAssignedNonUsingVars $ScriptBlock | Should -BeNullOrEmpty
}
}
}
What is the latest version of PSScriptAnalyzer at the point of writing
1.18.3