fix: attach trailing redirects to compound commands - #20
Draft
378-kaiabot[bot] wants to merge 2 commits into
Draft
fix: attach trailing redirects to compound commands#20378-kaiabot[bot] wants to merge 2 commits into
378-kaiabot[bot] wants to merge 2 commits into
Conversation
Redirects following fi/done/}/)/esac currently land on a phantom word-less SimpleCommand statement instead of the compound node itself; bash and mvdan/sh attach them to the compound (WhileClause, IfClause, Block, Subshell, ...). Adds optional redirects fields to the compound AST node types as the contract for the fix, plus failing tests asserting the correct shapes, including heredoc and pipeline interactions.
Redirects written after a compound command's closing keyword (done, fi, }, ), esac, ]]) used to be captured into a phantom word-less SimpleCommand statement, silently disconnecting them from the loop/if/block they apply to (e.g. 'while read x; do ...; done < files | sort' produced a redirect-only statement as the pipeline left instead of redirecting the loop). A shared parseTrailingRedirects step now consumes redirects following each closer's compound node, mirroring bash and mvdan/sh. Function definitions and coproc bodies cause redirects after their blocks to bind to the FunctionDecl/CoprocClause instead, so capture is suppressed in the body scope and re-enabled for nested statement lists. Also routes the cStyleLoop test builder through the real arithmetic parser so fixtures match parser output.
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.
Note
Automated pull request by Pi, model
aperture/synthetic/hf:zai-org/GLM-5.3-Flash. Implemented and validated empirically againstaliou/shon branchfix/heredoc-body-queue. See the local run log for the full investigation.Summary
Third layer of the heredoc/redirect work, stacked on #18. PR #18's review surfaced a worse-than-throwing edge: redirects written after a compound command's closing keyword (
done,fi,},),esac,]]) were silently captured into a phantom word-lessSimpleCommandstatement instead of attaching to the compound node. Injury, error, and now insult:while read x; do echo $x; done < in | sortparsed< inas a redirect-only statement at the left of a new pipeline — the loop lost its stdin and the pipeline shape broke, with no error signal.Bash and mvdan/sh attach these redirects to the compound node itself.
Changes
src/ast.ts— optionalredirects?: Redirect[]onSubshell,Block,IfClause,WhileClause,ForClause,SelectClause,FunctionDecl,CaseClause,TestClause,CoprocClause,CStyleLoop. Field stays absent when there are no redirects, so every existing fixture is untouched.src/parser/compound-redirects.test.ts— commit 1/2: fifteen failing tests (loops,if, block, subshell, case, select, function,[[ ]], coproc, heredoc-on-compound, multiple redirects, and the pipeline case). Do not modify these to make the fix pass.src/parser/parser.ts— commit 2/2: a sharedparseTrailingRedirectsruns after each compound's closing keyword and attaches redirects to that node. Two subtleties it handles: redirects after a function's block bind to theFunctionDecl(and coproc bodies to theCoprocClause), so capture is suppressed in the body scope and re-enabled for nested statement lists; and heredoc openers flow through the existingpendingHeredocsqueue from fix: queue heredoc bodies per command line and drain in opener order #18, sodone <<INbodies still attach in order.time { a; } > outintentionally keeps binding to the timed block —TimeClausehas noredirectsfield.src/test-helpers/ast-builders.ts—cStyleLoopnow routes clause strings through the real arithmetic parser (theArithLitplaceholders never matched parser output)..changeset/fuzzy-hounds-tease.md— patch changeset.Verification
pnpm typecheck✅pnpm lint✅pnpm test✅ (305 passed; the 15 new tests fail on fix: queue heredoc bodies per command line and drain in opener order #18's tip and pass here)&&/background ordering, heredoc on a function,elifchains (redirect attaches once, to the outerif), unnamed coproc, dup-fd redirects (>&2), redirect-then-comment, position extents — all verified empirically.Known limit (pre-existing, unchanged):
time false > t2still binds to the timed command, sinceTimeClausecarries no redirects.Stacked on #18
Run host:
sh-d9e31b· Session:01a062c1-c9e1-71cb-b132-aafe96837313· Model:aperture/synthetic/hf:zai-org/GLM-5.3-Flash(validation),aperture/neuralwatt/kimi-k3(implementation)