refactor(parser): extract fill_heredoc_contents visitor helpers#68
Merged
Conversation
fill_heredoc_contents in src/parser/helpers.rs was a ~100-line recursive
AST visitor with ~12 arms, gated by #[allow(clippy::too_many_lines,
clippy::match_same_arms)]. Many arms shared boilerplate:
- The `for r in redirects { fill_heredoc_contents(r, lexer); }` loop
appeared in 7 arms.
- Subshell, BraceGroup, For, and Select all follow the body+redirects
shape but were split across two match arms.
The refactor introduces six module-private helpers:
- fill_each: generic walk over a node slice (absorbs redirect-walks and
Pipeline's command-walk)
- fill_command: Command arm
- fill_if: If arm (takes Option<&mut Node> for else_body via
as_deref_mut())
- fill_body_and_redirects: Subshell / BraceGroup / For / Select (one
OR-pattern arm)
- fill_cond_body_redirects: While / Until
- fill_case: Case arm
Dispatcher shrinks to ~60 lines. Both #[allow] attributes are dropped —
the remaining arms are textually distinct and the function now fits
under the 60-line clippy cap.
Also adds 3 parable test cases in tests/parable/14_here_documents.tests
to pin behavior against future visitor edits:
- heredoc in if else branch (exercises else_body.as_deref_mut())
- heredoc in case pattern body (exercises fill_case)
- heredoc nested while inside if else (exercises multi-level recursion
through the lexer heredoc queue)
AST output is byte-identical; all 252 unit/integration + 12 oracle tests
pass. Part of #61 (v0.2.0 cycle).
Closes #57
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mpecan
pushed a commit
that referenced
this pull request
Apr 19, 2026
🤖 I have created a release *beep* *boop* --- ## [0.2.0](rable-v0.1.15...rable-v0.2.0) (2026-04-18) ### ⚠ BREAKING CHANGES * tighten lexer API surface and relocate WordSpan to ast ([#70](#70)) ### Bug Fixes * **format:** align cmdsub reformatter with bash canonical form ([#49](#49)) ([c7a4411](c7a4411)) * **lexer:** accept sloppy heredoc terminator in cmdsub mode ([#50](#50)) ([40f394f](40f394f)) * **lexer:** backticks opaque when content is invalid ([#71](#71)) ([e72166f](e72166f)), closes [#38](#38) * **lexer:** disable reserved-word recognition after assignment words ([#44](#44)) ([42e1fc0](42e1fc0)) * **lexer:** stop treating ]] and unbalanced [...] as special outside conditionals ([#45](#45)) ([4bf5a5c](4bf5a5c)) * **parser:** fall back from (( … )) arith to nested subshells ([#48](#48)) ([1437f00](1437f00)) ### Code Refactoring * **format:** introduce Formatter struct ([#65](#65)) ([d965a8f](d965a8f)) * **lexer:** drop Result<Token> wrapper from operator readers ([#62](#62)) ([d52a841](d52a841)) * **lexer:** split read_word_token into classify + advance + dispatch helpers ([#63](#63)) ([3ba09f5](3ba09f5)) * **parser:** extract fill_heredoc_contents visitor helpers ([#68](#68)) ([40e6165](40e6165)) * **parser:** extract helpers from three oversize parsers ([#69](#69)) ([25d0762](25d0762)) * **sexp:** dispatch NodeKind Display to per-category helpers ([#66](#66)) ([44b0330](44b0330)) * **sexp:** table-drive ANSI-C escape dispatch ([#67](#67)) ([91a5267](91a5267)) * tighten lexer API surface and relocate WordSpan to ast ([#70](#70)) ([5171d01](5171d01)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: repository-butler[bot] <166800726+repository-butler[bot]@users.noreply.github.com>
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
Refactors
fill_heredoc_contentsinsrc/parser/helpers.rs— the ~100-line recursive AST visitor with 12 arms (gated by#[allow(clippy::too_many_lines, clippy::match_same_arms)]) becomes a ~60-line dispatcher that delegates to per-category helpers:fill_each— generic walk over a&mut [Node]slice; absorbs every redirect loop AND Pipeline's command-walk.fill_command— Command (assignments + words + redirects).fill_if— If (condition + then + optional else + redirects).else_body: Option<&mut Node>viaas_deref_mut().fill_body_and_redirects— Subshell / BraceGroup / For / Select (fused into one OR-pattern arm — all four havebody + redirects).fill_cond_body_redirects— While / Until (condition + body + redirects).fill_case— Case (walk each pattern's optional body + redirects).Both
#[allow]attributes dropped; no remaining arms are textually identical and the function fits under the 60-line clippy cap.Test coverage
Added 3 parable test cases in
tests/parable/14_here_documents.teststo pin future regression prevention — these paths weren't directly exercised before:heredoc in if else branch— coverselse_body.as_deref_mut().heredoc in case pattern body— coversfill_case's inner loop.heredoc nested while inside if else— covers multi-level recursion through the lexer's heredoc queue.Test plan
cargo fmtcargo clippy --all-targets -- -D warnings— no warningscargo test— 252 passed (56 heredoc cases incl. 3 new ones)oracle_*tests pass (cargo test --test integration oracle_)Stack
Part of the v0.2.0 refactoring cycle (#61). This is PR 7 of 10.
Closes #57
🤖 Generated with Claude Code