fix(workflows): reject a switch expression that is never evaluated - #4295
Open
ntdatt812 wants to merge 1 commit into
Open
fix(workflows): reject a switch expression that is never evaluated#4295ntdatt812 wants to merge 1 commit into
ntdatt812 wants to merge 1 commit into
Conversation
`SwitchStep.validate` checked only that `expression` is present. It goes through
the same `evaluate_expression` as a condition, so one written without braces
comes back as its own source text:
expression: inputs.mode -> expression_value: "inputs.mode"
matched_case: "__default__"
status: COMPLETED
It matches no case key, falls through to `default` on every run — or dispatches
nothing at all when there is no default — and still reports COMPLETED. That is
the "silent empty result + COMPLETED" wiring bug this file's own `cases:` guard
was written to prevent, on the field one line above it.
`if`, `while` and `do-while` already run these two predicates on their
`condition`. This reuses them rather than writing a third scan.
Only those two apply. A switch matches on strings, so a composite key such as
`{{ inputs.a }}-{{ inputs.b }}` is legitimate here even though the same shape
would be a fault in a boolean condition — there is a test pinning that, and a
literal `true` and the empty string stay accepted as ordinary case keys for the
same reason.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The defect
SwitchStep.validatechecks only thatexpressionis present (steps/switch/__init__.py:105). The field goes through the sameevaluate_expressionas a condition, so one written without braces comes back as its own source text.Measured on
main(27f50f7),inputs.mode = "review", casesreview/build, plus adefault:It matches no case key, falls through to
defaulton every run — or, with nodefault:, dispatches nothing at all — and still reports COMPLETED. That is exactly the "silent empty result + COMPLETED" wiring bug this file's owncases:guard was written to prevent, quoting its comment, on the field one line above it.if,whileanddo-whileall runcondition_is_never_evaluatedandcondition_has_malformed_expression_blockon theircondition.switchran neither on itsexpression.The fix
Reuse those two predicates rather than write a third scan, with switch-appropriate wording.
Only those two apply, deliberately. A switch matches on strings, so a composite key is legitimate here even though the same shape is a fault in a boolean condition:
inputs.mode{{ inputs.x{{ inputs.missing | default('oops }}{{ inputs.mode }}{{ inputs.a }}-{{ inputs.b }}true,""The last two rows are the condition-specific exemptions
condition_is_never_evaluatedalready carries. They are harmless on a condition and actively correct here, which is what makes these two predicates safe to reuse on a non-boolean field. There is a test pinning that boundary so a later narrowing cannot quietly reject composite keys.This is independent of #4292, deliberately: that PR adds a predicate for a condition holding more than one block, which is precisely the shape a switch is allowed to have. It is not applied here.
Verification
Windows, Python 3.11.
Identical 22 failures on both sides,
diffclean — the pre-existing symlink and bash-parity classes on unelevated Windows. 1728 → 1731 is exactly the three cases added.Mutation-checked, after confirming the edit applied: disabling the never-evaluated branch fails 2 of the 3 new cases, and the composite-key case stays green under it — which is the point, since it does not depend on the branch and still guards the other side.
Tests
tests/test_workflows.py::TestSwitchStep— three cases: the braceless form (asserting the current runtime behaviour first, then the validator), both unclosable shapes, and the composite-key/literal boundary that must stay accepted.