Skip to content

fix: handle leading tilde on else if chain tags - #768

Merged
sunng87 merged 2 commits into
masterfrom
fix/else-if-leading-tilde
Jul 12, 2026
Merged

fix: handle leading tilde on else if chain tags#768
sunng87 merged 2 commits into
masterfrom
fix/else-if-leading-tilde

Conversation

@sunng87

@sunng87 sunng87 commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Fixes #766.

Summary

{{~else if cond}} panicked during template compilation:

thread 'main' panicked at src/template.rs:389:22:
internal error: entered unreachable code: leading_tilde_to_omit_whitespace

Root cause

The grammar places the optional leading tilde of invert_chain_tag
before invert_tag_item:

invert_chain_tag = { !escape ~ "{{" ~ leading_tilde_to_omit_whitespace? ~ invert_tag_item
                     ~ exp_line ~ trailing_tilde_to_omit_whitespace? ~ "}}" }

But the compile code called parse_name (to consume invert_tag_item)
before parse_expression, so when a leading ~ was present it was
handed to parse_name, which only expects identifier /
invert_tag_item / reference / subexpression — hence the
unreachable! arm.

The plain {{~else}} (invert_tag) case already worked, because it
skips parse_name and goes straight to parse_expression, which
handles the leading tilde itself. Only invert_chain_tag with a
leading tilde was broken (trailing tildes worked, since
parse_expression's loop consumes them).

Fix

Consume the optional leading tilde before parse_name for
invert_chain_tag, and fold its omit_pre_ws semantics into the
parsed expression so preceding whitespace is stripped consistently
with {{~else}}.

let mut invert_omit_pre_ws = false;
if rule == Rule::invert_chain_tag {
    if it.peek().unwrap().as_rule() == Rule::leading_tilde_to_omit_whitespace {
        invert_omit_pre_ws = true;
        it.next();
    }
    let _ = Template::parse_name(source, &mut it, span.end())?;
}
let mut exp = Template::parse_expression(source, it.by_ref(), span.end())?;
if invert_omit_pre_ws {
    exp.omit_pre_ws = true;
}

Tests

  • src/grammar.rs: grammar acceptance cases for {{~else if foo}},
    {{else if foo~}}, {{~else if foo~}}.
  • src/template.rs: unit test reproducing the original panic.
  • tests/whitespace.rs: rendering tests for leading / trailing / both
    tildes and the falsy fallback, confirming behaviour matches {{~else}}.

All existing tests, cargo fmt --check, and cargo clippy --tests pass.

`{{~else if cond}}` panicked during template compilation with
`unreachable: leading_tilde_to_omit_whitespace`.

The grammar places the optional leading tilde of `invert_chain_tag`
before `invert_tag_item`, but the compile code called `parse_name`
(before `parse_expression`) to consume `invert_tag_item`, so the
leading tilde was handed to `parse_name` and hit its `unreachable!`
arm. The plain `{{~else}}` (`invert_tag`) case already worked because
it skips `parse_name` and `parse_expression` handles the tilde itself.

Consume the optional leading tilde before `parse_name` for
`invert_chain_tag`, and fold its `omit_pre_ws` semantics into the
parsed expression so preceding whitespace is stripped consistently
with `{{~else}}`.

Fixes #766.
@coveralls

coveralls commented Jul 11, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 85.052% (+0.05%) from 85.007% — fix/else-if-leading-tilde into master

@sunng87
sunng87 merged commit aa3aaa0 into master Jul 12, 2026
9 checks passed
@sunng87
sunng87 deleted the fix/else-if-leading-tilde branch July 12, 2026 06:03
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.

~ in else if enters unreachable code

2 participants