Skip to content
Merged
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
17 changes: 14 additions & 3 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,12 @@ public object VisitPipeline(PipelineAst pipelineAst)
input = GetRangeEnumerator(firstCommandExpr.Expression) ??
Compile(firstCommandExpr.Expression);
}

if (input.Type == typeof(void))
{
input = Expression.Block(input, ExpressionCache.AutomationNullConstant);
}

i = 1;
commandsInPipe = pipeElements.Count - 1;
}
Expand Down Expand Up @@ -5525,16 +5531,21 @@ public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst)
if (values.Type == typeof(void))
{
// A dynamic site can't take void - but a void value is just an empty array.
return Expression.NewArrayInit(typeof(object));
return Expression.Block(values, Expression.NewArrayInit(typeof(object)));
}

return DynamicExpression.Dynamic(PSToObjectArrayBinder.Get(), typeof(object[]), values);
}

public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst)
{
return Expression.NewArrayInit(typeof(object),
arrayLiteralAst.Elements.Select(elem => Compile(elem).Cast(typeof(object))));
List<Expression> elementValues = new List<Expression>(arrayLiteralAst.Elements.Count);
foreach (var element in arrayLiteralAst.Elements)
{
var eValue = Compile(element);
elementValues.Add(eValue.Type != typeof(void) ? eValue.Cast(typeof(object)) : Expression.Block(eValue, ExpressionCache.AutomationNullConstant));
}
return Expression.NewArrayInit(typeof(object), elementValues);
}

private IEnumerable<Expression> BuildHashtable(ReadOnlyCollection<KeyValuePair> keyValuePairs, ParameterExpression temp, bool ordered)
Expand Down
2 changes: 1 addition & 1 deletion src/System.Management.Automation/engine/parser/ast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9301,7 +9301,7 @@ public ArrayLiteralAst(IScriptExtent extent, IList<ExpressionAst> elements)
}

/// <summary>
/// The non-empty collection of asts of the elements of the array, or null if no elements were specified (e.g. <c>@()</c>).
/// The non-empty collection of asts of the elements of the array.
/// </summary>
public ReadOnlyCollection<ExpressionAst> Elements { get; private set; }

Expand Down
67 changes: 67 additions & 0 deletions test/powershell/Language/Scripting/Array.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,71 @@ Describe "ArrayExpression Tests" -Tags "CI" {
$result.Length | Should Be 1
$result[0] | Should Be $null
}

It "@([void](New-Item)) should create file" {
try {
$testFile = Join-Path $TestDrive (New-Guid)
$result = @([void](New-Item $testFile -ItemType File))
## file should be created
$testFile | Should Exist
## the array should be empty
$result.Count | Should Be 0
} finally {
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
}
}
}

Describe "ArrayLiteral Tests" -Tags "CI" {
It "'[void](New-Item),2,3' should return a 3-element array and first element is AutomationNull" {
try {
$testFile = Join-Path $TestDrive (New-Guid)
$result = [void](New-Item $testFile -ItemType File), 2, 3
## file should be created
$testFile | Should Exist
## the array should contain 3 items
$result.Count | Should Be 3

## the first item should be AutomationNull
$result[0] | ForEach-Object { "YES" } | Should Be $null
$result | Measure-Object | ForEach-Object -MemberName Count | Should Be 2
} finally{
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
}
}

It "'[void]1, [void](New-Item), [void]2' should return a 3-AutomationNull-element array" {
try {
$testFile = Join-Path $TestDrive (New-Guid)
$result = [void]1, [void](New-Item $testFile -ItemType File), [void]2
## file should be created
$testFile | Should Exist
## the array should contain 3 items
$result.Count | Should Be 3

## all items should be AutomationNull
$result | ForEach-Object { "YES" } | Should Be $null
} finally {
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
}
}

It "'[void]`$arraylist1.Add(1), `$arraylist2.Clear()' should return a 2-AutomationNull-element array" {
$arraylist1 = [System.Collections.ArrayList]::new()
$arraylist2 = [System.Collections.ArrayList]::new()

$arraylist2.Add(2) > $null
$arraylist2.Count | Should Be 1

## first item is a non-void method call
## second item is a void method call
$result = [void]$arraylist1.Add(1), $arraylist2.Clear()
$result.Count | Should Be 2
$result | ForEach-Object { "YES" } | Should Be $null

$arraylist1.Count | Should Be 1
$arraylist1[0] | Should Be 1

$arraylist2.Count | Should Be 0
}
}
30 changes: 30 additions & 0 deletions test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Describe "Scripting.Followup.Tests" -Tags "CI" {
It "'[void](New-Item) | <Cmdlet>' should work and behave like passing AutomationNull to the pipe" {
try {
$testFile = Join-Path $TestDrive (New-Guid)
[void](New-Item $testFile -ItemType File) | ForEach-Object { "YES" } | Should Be $null
## file should be created
$testFile | Should Exist
} finally {
Remove-Item $testFile -Force -ErrorAction SilentlyContinue
}
}

## cast non-void method call to [void]
It "'[void]`$arraylist.Add(1) | <Cmdlet>' should work and behave like passing AutomationNull to the pipe" {
$arraylist = [System.Collections.ArrayList]::new()
[void]$arraylist.Add(1) | ForEach-Object { "YES" } | Should Be $null
## $arraylist.Add(1) should be executed
$arraylist.Count | Should Be 1
$arraylist[0] | Should Be 1
}

## void method call
It "'`$arraylist2.Clear() | <Cmdlet>' should work and behave like passing AutomationNull to the pipe" {
$arraylist = [System.Collections.ArrayList]::new()
$arraylist.Add(1) > $null
$arraylist.Clear() | ForEach-Object { "YES" } | Should Be $null
## $arraylist.Clear() should be executed
$arraylist.Count | Should Be 0
}
}