-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Stop setting $? to true for subpipelines and subexpressions #11040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
daxian-dbw
merged 14 commits into
PowerShell:master
from
rjmholt:dollarhook-subpipelines
Nov 15, 2019
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
8420b0f
Make $() and () not reset $?
rjmholt 982d3f0
Add tests
rjmholt 617c595
Fix iex pipeline behaviour
rjmholt 58a5652
Add native $? tests
rjmholt d489f49
Add new pipeline chain tests
rjmholt 5d12a0e
Move error semantics cases down
rjmholt 8d5e158
Add array and empty expression tests
18d3d41
Add final fixes plus tests
rjmholt 772c690
Add comments to methods
rjmholt 19cd54b
Update test/powershell/Language/Operators/PipelineChainOperator.Tests…
rjmholt f3ecc98
Add comment to describe logic
9b7127f
Add string expression tests
5923439
Add trap test for optimisation
c420aa5
Add example comment
rjmholt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3217,37 +3217,133 @@ private void CompileTrappableExpression(List<Expression> exprList, StatementAst | |
| var expr = Compile(stmt); | ||
rjmholt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| exprList.Add(expr); | ||
|
|
||
| var pipeAst = stmt as PipelineAst; | ||
| if (pipeAst != null) | ||
| if (ShouldSetExecutionStatusToSuccess(stmt)) | ||
| { | ||
| if (pipeAst.PipelineElements.Count == 1 && pipeAst.PipelineElements[0] is CommandExpressionAst) | ||
| { | ||
| // A single expression - must set $? after the expression. | ||
| exprList.Add(s_setDollarQuestionToTrue); | ||
| } | ||
| exprList.Add(s_setDollarQuestionToTrue); | ||
| } | ||
| else | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Determines whether a statement must have an explicit setting | ||
| /// for $? = $true after it by the compiler. | ||
| /// </summary> | ||
| /// <param name="statementAst">The statement to examine.</param> | ||
| /// <returns>True is the compiler should add the success setting, false otherwise.</returns> | ||
| private bool ShouldSetExecutionStatusToSuccess(StatementAst statementAst) | ||
| { | ||
| // Simple overload fan out | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd remove the comment.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wanted to explain why this method really exists here -- it doesn't do anything itself, it's just for type resolution |
||
| switch (statementAst) | ||
| { | ||
| var assignmentStatementAst = stmt as AssignmentStatementAst; | ||
| if (assignmentStatementAst != null) | ||
| { | ||
| Ast right = null; | ||
| var assignAst = assignmentStatementAst; | ||
| while (assignAst != null) | ||
| { | ||
| right = assignAst.Right; | ||
| assignAst = right as AssignmentStatementAst; | ||
| } | ||
| case PipelineAst pipelineAst: | ||
| return ShouldSetExecutionStatusToSuccess(pipelineAst); | ||
| case AssignmentStatementAst assignmentStatementAst: | ||
| return ShouldSetExecutionStatusToSuccess(assignmentStatementAst); | ||
| default: | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Determines whether a pipeline must have an explicit setting | ||
| /// for $? = $true after it by the compiler. | ||
| /// </summary> | ||
| /// <param name="pipelineAst">The pipeline to examine.</param> | ||
| /// <returns>True is the compiler should add the success setting, false otherwise.</returns> | ||
| private bool ShouldSetExecutionStatusToSuccess(PipelineAst pipelineAst) | ||
| { | ||
| ExpressionAst expressionAst = pipelineAst.GetPureExpression(); | ||
|
|
||
| // If the pipeline is not a simple expression, it will set $? | ||
| if (expressionAst == null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // Expressions may still set $? themselves, so dig deeper | ||
| return ShouldSetExecutionStatusToSuccess(expressionAst); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Determines whether an assignment statement must have an explicit setting | ||
| /// for $? = $true after it by the compiler. | ||
| /// </summary> | ||
| /// <param name="assignmentStatementAst">The assignment statement to examine.</param> | ||
| /// <returns>True is the compiler should add the success setting, false otherwise.</returns> | ||
| private bool ShouldSetExecutionStatusToSuccess(AssignmentStatementAst assignmentStatementAst) | ||
| { | ||
| // Get right-most RHS in cases like $x = $y = <expr> | ||
| StatementAst innerRhsStatementAst = assignmentStatementAst.Right; | ||
| while (innerRhsStatementAst is AssignmentStatementAst rhsAssignmentAst) | ||
| { | ||
| innerRhsStatementAst = rhsAssignmentAst.Right; | ||
| } | ||
|
|
||
| // Simple assignments to pure expressions may need $? set, so examine the RHS statement for pure expressions | ||
| switch (innerRhsStatementAst) | ||
| { | ||
| case CommandExpressionAst commandExpression: | ||
| return ShouldSetExecutionStatusToSuccess(commandExpression.Expression); | ||
|
|
||
| pipeAst = right as PipelineAst; | ||
| if (right is CommandExpressionAst || | ||
| (pipeAst != null && pipeAst.PipelineElements.Count == 1 && | ||
| pipeAst.PipelineElements[0] is CommandExpressionAst)) | ||
| case PipelineAst rhsPipelineAst: | ||
| return ShouldSetExecutionStatusToSuccess(rhsPipelineAst); | ||
|
|
||
| default: | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Determines whether an expression in a statement must have an explicit setting | ||
| /// for $? = $true after it by the compiler. | ||
| /// </summary> | ||
| /// <param name="expressionAst">The expression to examine.</param> | ||
| /// <returns>True is the compiler should add the success setting, false otherwise.</returns> | ||
| private bool ShouldSetExecutionStatusToSuccess(ExpressionAst expressionAst) | ||
| { | ||
| switch (expressionAst) | ||
| { | ||
| case ParenExpressionAst parenExpression: | ||
| // Pipelines in paren expressions that are just pure expressions will need $? set | ||
| // e.g. ("Hi"), vs (Test-Path ./here.txt) | ||
| return ShouldSetExecutionStatusToSuccess(parenExpression.Pipeline); | ||
|
|
||
| case SubExpressionAst subExpressionAst: | ||
| // Subexpressions generally set $? since they encapsulate a statement block | ||
| // But $() requires an explicit setting | ||
| return subExpressionAst.SubExpression.Statements.Count == 0; | ||
|
|
||
| case ArrayExpressionAst arrayExpressionAst: | ||
| // ArrayExpressionAsts and SubExpressionAsts must be treated differently, | ||
| // since they are optimised for a single expression differently. | ||
| // A SubExpressionAst with a single expression in it has the $? = $true added, | ||
| // but the optimisation drills deeper for ArrayExpressionAsts, | ||
| // meaning we must inspect the expression itself in these cases | ||
|
|
||
| switch (arrayExpressionAst.SubExpression.Statements.Count) | ||
rjmholt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| // If the RHS of the assign was an expression, | ||
| exprList.Add(s_setDollarQuestionToTrue); | ||
| case 0: | ||
| // @() needs $? set | ||
| return true; | ||
|
|
||
| case 1: | ||
| // Single expressions with a trap are handled as statements | ||
iSazonov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // For example: @(trap { continue } "Value") | ||
| if (arrayExpressionAst.SubExpression.Traps != null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // Pure, single statement expressions need $? set | ||
| // For example @("One") and @("One", "Two") | ||
| return ShouldSetExecutionStatusToSuccess(arrayExpressionAst.SubExpression.Statements[0]); | ||
|
|
||
| default: | ||
| // Arrays with multiple statements in them will have $? set | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| default: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -3558,9 +3654,8 @@ public object VisitPipelineChain(PipelineChainAst pipelineChainAst) | |
| /// <returns>The compiled expression to execute the pipeline.</returns> | ||
| private Expression CompilePipelineChainElement(PipelineAst pipelineAst) | ||
| { | ||
| if (pipelineAst.PipelineElements.Count == 1 && pipelineAst.PipelineElements[0] is CommandExpressionAst) | ||
| if (ShouldSetExecutionStatusToSuccess(pipelineAst)) | ||
| { | ||
| // A single expression - must set $? after the expression. | ||
| return Expression.Block(Compile(pipelineAst), s_setDollarQuestionToTrue); | ||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.