Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4123,14 +4123,31 @@ public object VisitCommand(CommandAst commandAst)
var splatTest = element;
bool splatted = false;

UsingExpressionAst usingExpression = element as UsingExpressionAst;
if (usingExpression != null)
if (splatTest is UsingExpressionAst usingExpression)
{
splatTest = usingExpression.SubExpression;
}

VariableExpressionAst variableExpression = splatTest as VariableExpressionAst;
if (variableExpression != null)
var done = false;
do
{
switch (splatTest)
{
case MemberExpressionAst memberExpression:
splatTest = memberExpression.Expression;
break;

case IndexExpressionAst indexExpression:
splatTest = indexExpression.Target;
break;

default:
done = true;
break;
}
} while (!done);

if (splatTest is VariableExpressionAst variableExpression)
{
splatted = variableExpression.Splatted;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,11 @@ private ExpressionAst CheckUsingExpression(ExpressionAst exprAst)

public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
{
if (variableExpressionAst.Splatted && !(variableExpressionAst.Parent is CommandAst) && !(variableExpressionAst.Parent is UsingExpressionAst))
if (variableExpressionAst.Splatted &&
!(variableExpressionAst.Parent is CommandAst) &&
!(variableExpressionAst.Parent is MemberExpressionAst) &&
!(variableExpressionAst.Parent is IndexExpressionAst) &&
!(variableExpressionAst.Parent is UsingExpressionAst))
{
if (variableExpressionAst.Parent is ArrayLiteralAst && variableExpressionAst.Parent.Parent is CommandAst)
{
Expand Down
176 changes: 176 additions & 0 deletions test/powershell/Language/Scripting/Splatting.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

Describe 'Tests for splatting' -Tags 'CI' {
Context 'Basic splatting' {
BeforeAll {
function Test-Splat {
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(Position = 0, ParameterSetName = 'Value')]
[ValidateNotNullOrEmpty()]
[string]
$Value,

[Parameter(Position = 0, ParameterSetName = 'Value2')]
[ValidateNotNullOrEmpty()]
[int]
$Value2
)
$PSCmdlet.ParameterSetName
}
}

It 'Should splat hashtables properly' {
$testObject = @{
Value2 = 42
}
Test-Splat @testObject | Should -BeExactly 'Value2'
}

It 'Should splat arrays properly' {
$testObject = @(
'Does it splat?'
)
Test-Splat @testObject | Should -BeExactly 'Value'
}

It 'Should splat values properly' {
$testObject = 42
Test-Splat @testObject | Should -BeExactly 'Value2'
}
}

Context 'Splatting members and indexed data' {
BeforeAll {
function Test-Splat {
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
[Parameter(ParameterSetName = 'Value')]
[ValidateNotNullOrEmpty()]
[string]
$Value,

[Parameter(ParameterSetName = 'Value2')]
[ValidateNotNullOrEmpty()]
[string]
$Value2
)
$PSCmdlet.ParameterSetName
}
}

It 'Should splat property values properly' {
$testObject = [pscustomobject]@{
Parameters = @{
Value = 'Does it splat?'
}
}
Test-Splat @testObject.Parameters | Should -BeExactly 'Value'
}

It 'Should splat method results properly' {
$testObject = [pscustomobject]@{
Parameters = @{
Value = 'Does it splat?'
}
}
Add-Member -InputObject $testObject -Name GetParams -MemberType ScriptMethod -Value { $this.Parameters }
Test-Splat @testObject.GetParams() | Should -BeExactly 'Value'
}

It 'Should splat indexed method results properly' {
$testObject = [pscustomobject]@{
Parameters = @(
@{
Value = 'Will this splat?'
}
@{
Value2 = 'Or this?'
}
)
}
Add-Member -InputObject $testObject -Name GetParams -MemberType ScriptMethod -Value { $this.Parameters }
Test-Splat @testObject.GetParams()[1] | Should -BeExactly 'Value2'
}

It 'Should splat indexed data properly' {
$testObject = @(
@{
Value = 'Will this splat?'
}
@{
Value2 = 'Or this?'
}
)
Test-Splat @testObject[1] | Should -BeExactly 'Value2'
}

It 'Should splat multiple members properly' {
$testObject = [pscustomobject]@{
Parameters = @{
Nested = @{
Value = 'Does it splat?'
}
}
}
Add-Member -InputObject $testObject -Name GetParams -MemberType ScriptMethod -Value { $this.Parameters }
Test-Splat @testObject.GetParams().Nested | Should -BeExactly 'Value'
}

It 'Should splat $PSCmdlet.MyInvocation.BoundParameters properly' {
& {
[CmdletBinding()]
param(
$Value
)
Test-Splat @PSCmdlet.MyInvocation.BoundParameters
} -Value 'Hello' | Should -BeExactly 'Value'
}
}

Context 'Splatting members and indexed data with the using statement' {
BeforeAll {
$skipTest = -not $EnabledExperimentalFeatures.Contains('PSForEachObjectParallel')
if ($skipTest) {
Write-Verbose "Tests Skipped. These tests require the experimental feature 'PSForEachObjectParallel' to be enabled." -Verbose
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:skip"] = $true
}
}

AfterAll {
if ($skipTest) {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
}
}

It 'Should splat indexed data and ''using:'' properly' {
$testObject = @(
@{
InputObject = 'First'
}
@{
InputObject = 'Second'
}
)
Add-Member -InputObject $testObject -Name GetParams -MemberType ScriptMethod -Value { $this.Parameters[$args[0]] }
1..1 | ForEach-Object -Parallel { Write-Output @using:testObject[1] } | Should -BeExactly 'Second'
}

It 'Should splat with properties and indexed data and ''using:'' properly' {
$testObject = [pscustomobject]@{
Parameters = @(
@{
InputObject = 'First'
}
@{
InputObject = 'Second'
}
)
}
Add-Member -InputObject $testObject -Name GetParams -MemberType ScriptMethod -Value { $this.Parameters[$args[0]] }
1..1 | ForEach-Object -Parallel { Write-Output @using:testObject.Parameters[1] } | Should -BeExactly 'Second'
}
}
}