Skip to content

parser: a case pattern may be produced by a substitution (#744) - #746

Merged
berrym merged 1 commit into
masterfrom
fix/744-case-pattern-backtick
Aug 16, 2026
Merged

parser: a case pattern may be produced by a substitution (#744)#746
berrym merged 1 commit into
masterfrom
fix/744-case-pattern-backtick

Conversation

@berrym

@berrym berrym commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Closes #744.

The defect

A case pattern is a word, so a substitution is a legal part of one. None of the three spellings parsed:

$ lush -c 'case a in $(printf a)) echo match;; esac'
error[E1001]: expected pattern in case statement

Same for the backtick form and $((1+2)). bash, zsh and dash accept all three.

Cause

The case-pattern parser carries a hand-written list of accepted token types. It included TOK_VARIABLE — which is why $var and ${p} worked, and what made the gap look narrower than it was — but none of TOK_COMMAND_SUB, TOK_BACKQUOTE, TOK_ARITH_EXP.

The two canonical predicates next door in tokenizer.c, token_is_argument_word_token and token_is_assignment_value_token, both already list all three. Only this site missed them. Added here rather than by adopting a predicate, because this list is genuinely its own: it also accepts *, [[, ]] and excludes keywords.

Not the #494 shape, and the issue guessed wrong

I filed this pointing at the pattern scanner, reasoning it failed to treat a backtick body as opaque. The scanner was never involved. #494's )-boundary work is intact — once the tokens are accepted, a ) inside a substitution correctly does not end the pattern:

$ lush -c 'case ")" in $(printf ")")) echo match;; esac'   -> match
$ lush -c 'case ")" in `printf ")"`) echo match;; esac'    -> match

Both are pinned in the test so the two defects cannot be confused again.

The issue also claimed the $( ) spelling "parses and matches" — a control I asserted without running. Every substitution form failed identically. Scope corrected on the issue.

Verification

  • tests/integration/test_case_pattern_substitution.c — 21 checks: all three spellings as a whole pattern and as part of one, alternation, nesting, a non-matching pattern that must still fall through (so the fix is not "accept and always match"), glob metacharacters on the produced pattern, the two Command substitution mis-terminates at a case-pattern ) #494 boundary cases, and the eight forms that already worked.
  • 13 of 21 fail against the parent build, with a control proving the binaries differ.
  • Full suite 195/195. ASan 65/65; the new test 1/1 under ASan.

A case pattern is a word, so a substitution is a legal part of one. None of
the three spellings parsed:

    case a in $(printf a)) echo match;; esac
      error[E1001]: expected pattern in case statement

The same for the backtick form and for `$((1+2))`. bash, zsh and dash accept
all three.

The case-pattern parser carries a hand-written list of accepted token types.
It included TOK_VARIABLE -- which is why `$var` and `${p}` worked and made the
gap look narrower than it was -- but none of TOK_COMMAND_SUB, TOK_BACKQUOTE or
TOK_ARITH_EXP. The two canonical predicates next door in tokenizer.c,
token_is_argument_word_token and token_is_assignment_value_token, both already
list all three; only this site missed them. Added here rather than by adopting
a predicate, because this list is genuinely its own: it also accepts `*`, `[[`
and `]]`, and excludes keywords.

Not the #494 shape. That issue was a `)` inside a substitution being mistaken
for the pattern terminator; that work is intact, and once these tokens are
accepted such a `)` correctly does not end the pattern:

    case ")" in $(printf ')')) echo match;; esac    -> match
    case ")" in `printf ')'`) echo match;; esac     -> match

Both are pinned in the test so the two defects cannot be confused again -- the
issue as filed guessed at the scanner, and the scanner was never involved.

The issue also claimed the `$( )` spelling already worked, which was a control
asserted without being run: every substitution form failed identically. The
scope is corrected on the issue.

tests/integration/test_case_pattern_substitution.c: 21 checks -- all three
spellings as a whole pattern and as part of one, alternation, nesting, a
non-matching pattern that must still fall through, glob metacharacters on the
produced pattern, the two #494 boundary cases, and the eight forms that
already worked and must be untouched. 13 fail against the parent build.
@codecov

codecov Bot commented Aug 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@berrym
berrym merged commit 405484e into master Aug 16, 2026
7 checks passed
@berrym
berrym deleted the fix/744-case-pattern-backtick branch August 16, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

a backtick substitution in a case pattern is a parse error

1 participant