fix(lexer): consume newlines between @let and the declared name#418
Open
shaharkazaz wants to merge 1 commit into
Open
fix(lexer): consume newlines between @let and the declared name#418shaharkazaz wants to merge 1 commit into
@let and the declared name#418shaharkazaz wants to merge 1 commit into
Conversation
Angular consumes all whitespace, including newlines, between `@let` and the declared variable name. The HTML lexer only skipped spaces and tabs, so a `@let` followed by a newline (as prettier commonly formats it) was misclassified as incomplete. Its value was then never consumed, leaving any object-literal braces in the value to be re-lexed as a stray block close. That corrupted block nesting and surfaced downstream as an "Unexpected closing block" / "Unexpected closing tag" parse error. Skip all whitespace after `@let` (matching Angular's lexer) so `@let\nname = expr;` parses as a complete declaration. Adds a lexer test (newline before the name tokenizes as a complete LET) and a parser regression test (object literal in a `@let` value inside a block no longer breaks block nesting).
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
The HTML lexer misclassifies a
@letdeclaration as incomplete when anewline (rather than a space/tab) separates the
@letkeyword from thedeclared name — e.g. when a formatter wraps the line:
@if (cond) { @let label = value | translate: { section: id }; <button></button> }Because the declaration is treated as incomplete, its value is never
consumed. Any object-literal braces inside the value (
{ section: id }) arethen re-lexed as ordinary block content, and the stray
}becomes aspurious block-close token. That corrupts block nesting and surfaces
downstream as an
Unexpected closing block/Unexpected closing tagparseerror, failing the build for otherwise-valid Angular templates.
The standard
@angular/compileraccepts this template, so the divergence isin the lexer's whitespace handling.
Root cause
scan_let_start(crates/oxc_angular_compiler/src/parser/html/lexer.rs)skipped only spaces and tabs after
@let:When the next character is a newline, the following name-start check fails and
the scanner emits
IncompleteLet, bailing before it reads the value.Alignment with Angular
Angular's
_consumeLetDeclaration(
packages/compiler/src/ml_parser/lexer.ts, v22.1.0-rc.0) requires at leastone whitespace after
@letand then consumes all whitespace via_attemptCharCodeUntilFn(isNotWhitespace):isWhitespaceis(code >= $TAB && code <= $SPACE) || code == $NBSP, whichincludes
\nand\r. The "require at least one whitespace" half is alreadyenforced by this crate's block dispatch (
@letFoo→IncompleteLet); onlythe "skip all whitespace" half was too strict.
Fix
Skip all whitespace after
@let, matching Angular:chars::is_whitespacehere ismatches!(ch, TAB..=SPACE | NBSP)— the samerange as Angular's
isWhitespace.EOF('\0') is outside that range, sothe loop still terminates at end-of-input, matching Angular's
isNotWhitespaceEOF guard.Tests
html_lexer_test.rs) —@let\nfoo = 123;tokenizes as acomplete
LetStart/LetValue/LetEnd(notIncompleteLet).html_parser_test.rs) — a@letwith an object-literal valueinside a block no longer corrupts block nesting (regression for the
Unexpected closing blockerror).Both fail before the change and pass after. Full suite green:
cargo test -p oxc_angular_compiler— 0 failures.cargo fmtclean.