Skip to content

fix(compiler-core): error on unexpected first character of an attribute name - #15284

Open
ValentinYoushkevich wants to merge 1 commit into
vuejs:mainfrom
ValentinYoushkevich:fix/error-on-lt-in-attr-name
Open

fix(compiler-core): error on unexpected first character of an attribute name#15284
ValentinYoushkevich wants to merge 1 commit into
vuejs:mainfrom
ValentinYoushkevich:fix/error-on-lt-in-attr-name

Conversation

@ValentinYoushkevich

@ValentinYoushkevich ValentinYoushkevich commented Aug 12, 2026

Copy link
Copy Markdown

close #13319

The problem

When a new tag is opened while the previous one is still unclosed, the opening < is silently swallowed as the first character of an attribute name, and the only diagnostic is a misleading Invalid end tag. pointing at the closing tag:

<div
  <span>hi</span>
</div>
Invalid end tag.  (2:11)

<span becomes an attribute of div, so to the parser </span> is indeed unmatched — but the reported location is the symptom, not the mistake, and the closing tag it points at visibly does have a matching open tag right before it.

Root cause

The tokenizer implements the attribute name state parse error for ", ' and < — but only for characters consumed while already in State.InAttrName. Per the spec, unexpected-character-in-attribute-name also fires when one of these characters is reconsumed as the first character of the name from the before attribute name state. That reconsume path goes through handleAttrStart, which entered State.InAttrName without the check:

<div a<b=''>   <!-- errors today -->
<div <b=''>    <!-- silently parsed as an attribute named `<b` -->

The fix

handleAttrStart now reports UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME for a leading ", ' or <, under the same __DEV__ || !__BROWSER__ guard as the existing in-name check. Error recovery is untouched: the character still becomes part of the attribute name and the resulting AST is unchanged, exactly as in the non-leading case. The snippet above now reports

Attribute name cannot contain U+0022 ("), U+0027 ('), and U+003C (<).  (2:3)
Invalid end tag.  (2:11)

with the first error sitting exactly on the < of <span>.

Regarding the concern about compile-time failures on browser-tolerated input: this doesn't introduce a new failure mode. This input already failed to compile (Invalid end tag.), the HTML spec itself classifies the leading character as a (recoverable) parse error, and the compiler already reports the identical error for the identical characters one position later. The change only removes the first-character blind spot so the error carries a useful location.

Tests

Four new cases in the parse error table: the three leading characters mirroring the existing a"bc='' / a'bc='' / a<bc='' cases, plus the scenario from the issue asserting both errors with exact locations.

Summary by CodeRabbit

  • Bug Fixes
    • Improved template parsing to report clearer errors when unsupported characters appear in attribute names.
    • Corrected error detection for newly opened tags inside unclosed tags.
    • Error messages now include more accurate codes and source locations.

…te name

Per the HTML spec, `"`, `'` and `<` hitting the attribute name state
trigger the unexpected-character-in-attribute-name parse error, including
when reconsumed as the FIRST character from the before attribute name
state. The tokenizer only reported the error for subsequent characters,
so `<div a<b>` errored while `<div <b>` was silently swallowed as an
attribute named `<b`.

The silent case is how an unclosed tag manifests: in

    <div
      <span>hi</span>
    </div>

the `<span` became an attribute of `div` and the only diagnostic was a
misleading "Invalid end tag." pointing at `</span>`. Now the error is
also reported at the exact position where the new tag opens before the
previous one is closed. Error recovery is unchanged — the character
still becomes part of the attribute name, exactly as the spec and the
existing non-leading case do.

close vuejs#13319
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9044391-a9e2-4416-97f8-d37ea3abab59

📥 Commits

Reviewing files that changed from the base of the PR and between a2b40db and a656ebd.

⛔ Files ignored due to path filters (1)
  • packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • packages/compiler-core/__tests__/parse.spec.ts
  • packages/compiler-core/src/tokenizer.ts

📝 Walkthrough

Walkthrough

The tokenizer now reports unexpected quotes and < characters at attribute-name starts. Parser tests verify error codes, source locations, and the nested-tag regression that also produces an invalid end-tag error.

Changes

Attribute name error handling

Layer / File(s) Summary
Tokenizer validation and regression tests
packages/compiler-core/src/tokenizer.ts, packages/compiler-core/__tests__/parse.spec.ts
handleAttrStart reports UNEXPECTED_CHARACTER_IN_ATTRIBUTE_NAME for invalid starting characters in development or non-browser builds. Tests cover error locations and the nested-tag X_INVALID_END_TAG regression.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the tokenizer change for unexpected first characters in attribute names.
Linked Issues check ✅ Passed The tokenizer now reports the error at the new tag’s opening character, with regression tests covering the linked issue.
Out of Scope Changes check ✅ Passed All reviewed changes support the tokenizer diagnostic fix and related parser regression coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@edison1105 edison1105 added the 🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. label Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compiler should error on the start of a new element with the current element unclosed, instead of onClose

2 participants