fix(expand): unwind the command list on fatal ${...} expansion errors - #657
Merged
Conversation
A fatal ${...} expansion error -- bad substitution, bad array subscript,
invalid indirection -- unwinds the whole command list in bash (the DISCARD
longjmp): the rest of the current input line is abandoned, through &&
chains, compound commands and function calls, and the reader continues at
the next line. In POSIX mode the error is fatal to a non-interactive
shell outright (exit 127). gnash reported the error and failed the
command but ran the rest of the list.
Escalate any arith_error newly raised inside expand_dollar's ${...}
branch to the existing arith_abort unwind (a scope guard covers every
error site in the branch at once). Containment falls out of the existing
machinery: subshells and pipeline elements fork, and eval strings, funsubs
and trap bodies run through run_string, which already ends the unwind.
A here-document body is bash's exception -- the error only aborts the
redirection -- so the executor's heredoc containment now saves and
restores arith_abort too, which also stops the $((...)) branch's abort
from escaping a heredoc. A redirection target whose expansion fails now
fails the redirection (and the command, status 1) instead of opening the
partially-expanded name. A fatal expansion error in a while/until
condition or a for/select word list is the construct's own status 1
rather than the last completed body's.
Closes #655
…h 127
bash treats an unknown or missing transform operator (${v@Z}, ${v@})
as fatal like ${x?}: a non-interactive shell exits with status 127
rather than the DISCARD unwind's status 1. Route the invalid-transform
report through the same exiting/127 path as ${x?} instead of
arith_error. This also stops the splat form ${arr[@]@z} from printing
the diagnostic once per element.
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.
Summary
A fatal
${...}expansion error — bad substitution, bad array subscript, invalid indirection — unwinds the whole command list in bash (the DISCARD longjmp): the rest of the current input line is abandoned, through&&chains, compound commands and function calls, and the reader continues at the next line. In POSIX mode the error is fatal to a non-interactive shell outright (exit 127). gnash reported the error and failed the command, but ran the rest of the list.Closes #655
Changes
core/src/expand.cpp: a scope guard onexpand_dollar's${...}branch escalates any arith_error newly raised inside it to the existingarith_abortunwind (the mechanism fatal assignment errors already use), covering every error site in the branch at once. Containment falls out of existing machinery: subshells and pipeline elements fork; eval strings, funsubs, in-process command substitutions and trap bodies run throughrun_string, which already ends the unwind.arith_aborttoo — which also stops the$((...))branch's abort from escaping a heredoc (cat <<E; echo Awith a bad$((1+))body now runs theecho A, as bash does).while/untilcondition or afor/selectword list is the construct's own status 1, not the last completed body's.@transform (separate commit):${v@Z}/${v@}are fatal like${x?}— a non-interactive shell exits 127, not the DISCARD's status 1. This also stops${arr[@]@Z}printing its diagnostic once per element.tests/harness/run_diff.sh: 22 new differential cases covering the unwind, every containment boundary, the heredoc exception, and the loop/transform statuses (470 total).Verification
${...}error classes,&&/;chains, functions, subshells, pipelines, cmdsubs/backticks, eval, traps, heredocs, redirect targets, assignments, loops,[[, background jobs, POSIX mode (127), and the non-fatal classes ((( )),let,${x-},${v/%%/x}) staying non-fatal.ctest23/23;run_diff470/470.errorsimproves 126 → 125 vs the pre-change binary; the remaining 125 lines are pre-existing divergences (readonly-assignment abort family,${$VAR}), to be filed separately.