Skip to content

(x86asm, mipsasm, llvm) Label and directive matches swallow the preceding blank line #4525

Description

@mattgodbolt

Describe the issue

In the assembly grammars, the label and directive rules anchor with ^\s*. Because
\s matches \n, a match that starts at the beginning of a preceding blank or
whitespace-only line
can run through the newline(s) and into the label on the next
line. Since JS regexes prefer the leftmost match, that earlier start wins over the
correct one, and the emitted <span> begins one line too early:

<span class="hljs-symbol">
.exit:</span>

The blank line is now inside the symbol span, so it picks up the label's styling
(visible in any theme that gives .hljs-symbol a background, and it also means the
highlighted region simply doesn't correspond to the token). It also breaks anything
that splits the highlighted HTML per line.

This only triggers when the label or directive is preceded by a blank (or
whitespace-only) line, which is why it has gone unnoticed. That happens to be the
normal shape of compiler-generated assembly, where functions are separated by blank
lines.

It's already baked into one of your own fixtures: test/markup/mipsasm/default.expect.txt
currently expects

<span class="hljs-symbol">
AckermannFunc:</span>

Affected rules:

  • src/languages/x86asm.js:127'^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)'
  • src/languages/x86asm.js:129'^\\s*%%[A-Za-z0-9_$#@~.?]*:'
  • src/languages/x86asm.js:147/^\s*\.[\w_-]+/
  • src/languages/mipsasm.js:90,92'^\\s*[a-z_\\.\\$][a-z0-9_\\.\\$]+:', '^\\s*[0-9]+:'
  • src/languages/llvm.js:38/^\s*[a-z]+:/

The trailing \s+label in the x86asm rule has the same defect independently: it can
join a bare identifier to a label keyword on the following line.

Which language seems to have the issue?

x86asm, mipsasm and llvm (explicit language, not auto-detection).

Are you using highlight or highlightAuto?

highlight.

Sample Code to Reproduce

hljs.highlight("main:\n    mov eax, 1\n\n.exit:\n    ret\n", { language: "x86asm" }).value

Actual:

<span class="hljs-symbol">main:</span>
    <span class="hljs-keyword">mov</span> <span class="hljs-built_in">eax</span>, <span class="hljs-number">1</span>
<span class="hljs-symbol">
.exit:</span>
    <span class="hljs-keyword">ret</span>

Same for directives (\n\n .section .text puts the newline inside hljs-meta), for
mipsasm and llvm labels, and for the x86asm label keyword form across two lines.

Expected behavior

The blank line stays outside the span:

<span class="hljs-symbol">main:</span>
    <span class="hljs-keyword">mov</span> <span class="hljs-built_in">eax</span>, <span class="hljs-number">1</span>

<span class="hljs-symbol">.exit:</span>
    <span class="hljs-keyword">ret</span>

Additional context

The fix is to use [ \t] rather than \s for the leading indentation (and for the
x86asm label separator): indentation before a label or directive is by definition
same-line. I have a patch with markup tests for all three grammars; PR to follow.
Verified against main (fc3f063): with the fix the only existing expectation that
changes is the mipsasm/default one quoted above, and highlighting relevance is
unchanged for every case I tested, so auto-detection is unaffected.

Two related things I noticed while in here but deliberately left alone, as they're
separate defects with less obvious fixes:

  • src/languages/mipsasm.js:92 — the numbered-local-label rule ^\s*[0-9]+: is
    effectively dead: the number mode appears earlier in contains, so at a true
    line start it always wins and 1: highlights as a number followed by a bare colon.
    Before this fix, the symbol rule only ever won when it could start on an earlier
    blank line.
  • src/languages/mipsasm.js:57 — the keyword mode's end: '\\s' consumes the
    terminating character, so a mnemonic at the end of a line emits
    <span class="hljs-keyword">nop\n</span>. Same class of problem, but tightening it
    would change existing expectations for the trailing space in addi .

Investigated and drafted by Claude Code (Claude Opus 5) working with me; posted
from my account with my permission. I have reviewed the analysis and the patch and
can speak to them in review. Assisted-by: Claude Opus 5 (high)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions