diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index cdf053dcb2e..cf7527069db 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -2278,26 +2278,25 @@ private Expression CaptureAstResults( switch (context) { case CaptureAstContext.AssignmentWithResultPreservation: + result = Expression.Call(CachedReflectionInfo.PipelineOps_PipelineResult, resultList); + + // PipelineResult might get skipped in some circumstances due to an early return or a FlowControlException thrown out, in which case + // we write to the oldPipe. This can happen in cases like: + // $(1;2;return 3) + finallyExprs.Add(Expression.Call(CachedReflectionInfo.PipelineOps_FlushPipe, oldPipe, resultList)); + break; case CaptureAstContext.AssignmentWithoutResultPreservation: result = Expression.Call(CachedReflectionInfo.PipelineOps_PipelineResult, resultList); // Clear the temporary pipe in case of exception, if we are not required to preserve the results - if (context == CaptureAstContext.AssignmentWithoutResultPreservation) + var catchExprs = new List { - var catchExprs = new List - { - Expression.Call(CachedReflectionInfo.PipelineOps_ClearPipe, resultList), - Expression.Rethrow(), - Expression.Constant(null, typeof(object)) - }; - - catches.Add(Expression.Catch(typeof(RuntimeException), Expression.Block(typeof(object), catchExprs))); - } + Expression.Call(CachedReflectionInfo.PipelineOps_ClearPipe, resultList), + Expression.Rethrow(), + Expression.Constant(null, typeof(object)) + }; - // PipelineResult might get skipped in some circumstances due to a FlowControlException thrown out, in which case - // we write to the oldPipe. This can happen in cases like: - // $(1;2;return 3) - finallyExprs.Add(Expression.Call(CachedReflectionInfo.PipelineOps_FlushPipe, oldPipe, resultList)); + catches.Add(Expression.Catch(typeof(RuntimeException), Expression.Block(typeof(object), catchExprs))); break; case CaptureAstContext.Condition: result = DynamicExpression.Dynamic(PSPipelineResultToBoolBinder.Get(), typeof(bool), resultList); diff --git a/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 b/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 index 933c53de6de..0d4e8245e2c 100644 --- a/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 +++ b/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 @@ -68,4 +68,26 @@ Describe "Scripting.Followup.Tests" -Tags "CI" { $result = [ordered]@{ key = 1 } $result | Should -BeOfType 'System.Collections.Specialized.OrderedDictionary' } + + It "Don't preserve result when no need to do so in case of flow-control exception" { + function TestFunc1([switch]$p) { + ## No need to preserve and flush the results from the IF statement to the outer + ## pipeline, because the results are supposed to be assigned to a variable. + if ($p) { + $null = if ($true) { "one"; return "two" } + } else { + $a = foreach ($a in 1) { "one"; return; } + } + } + + function TestFunc2 { + ## The results from the sub-expression need to be preserved and flushed to the outer pipeline. + $("1";return "2") + } + + TestFunc1 | Should -Be $null + TestFunc1 -p | Should -Be $null + + TestFunc2 | Should -Be @("1", "2") + } }