Skip to content

fix(rust) reuse the string rules inside attributes - #4461

Open
MaksZhukov wants to merge 2 commits into
highlightjs:mainfrom
MaksZhukov:fix/issue-3817-rust-attribute-strings
Open

fix(rust) reuse the string rules inside attributes#4461
MaksZhukov wants to merge 2 commits into
highlightjs:mainfrom
MaksZhukov:fix/issue-3817-rust-attribute-strings

Conversation

@MaksZhukov

Copy link
Copy Markdown

Resolves #3817

Changes

The meta mode for Rust attributes declared its own minimal string rule ("" + BACKSLASH_ESCAPE), so only plain double-quoted literals were recognised inside #[...]. Raw strings and byte strings were mis-highlighted:

#[doc = r#"a "quoted" word"#]

was cut at the first inner quote, so quoted fell out of the string and the rest of the attribute was wrongly scoped.

Attributes hold arbitrary token trees, so any string literal that is valid elsewhere is valid there too — which answers the question raised in the issue. This extracts the existing string modes into named constants (QUOTE_STRING, RAW_STRING, CHARACTER) and reuses QUOTE_STRING / RAW_STRING inside meta instead of duplicating a weaker version. The top-level contains order is unchanged.

The character-literal mode is deliberately not added to meta, so lifetimes in attribute token trees (#[foo(Bar<'a>)]) are not treated as an unterminated char literal.

Note on the original report: the exact snippet in the issue (#[error("\" appears in a string")]) has been highlighted correctly since 11.10.0, but the underlying cause it pointed at — meta not reusing the string rules — was still present. Before/after for the remaining cases:

input before after
#[doc = r#"a "quoted" word"#] r# outside the string, literal split in two whole r#"…"# is one string
#[doc = b"bytes"] b outside the string b"bytes" is one string
multi-line r##"…"## in an attribute broken one string

How to test

npm run build && npm test

Or in the demo/playground, paste the Rust snippets above.

Checklist

  • Added markup tests (test/markup/rust/strings.txt / .expect.txt — raw, byte, multi-line raw strings in attributes, plus a lifetime-in-attribute regression case)
  • Updated the changelog at CHANGES.md

🤖 Generated with Claude Code

The `meta` mode for Rust attributes declared its own minimal string
rule, so only plain `"..."` literals were recognised inside `#[...]`.
Raw strings and byte strings were mis-highlighted: in
`#[doc = r#"a "quoted" word"#]` the literal was cut at the first inner
quote, leaving the rest of the attribute wrongly scoped.

Attributes hold arbitrary token trees, so any string literal that is
valid elsewhere is valid there too. Extract the existing string modes
into named constants and reuse them in `meta` instead of duplicating a
weaker version.

The character-literal mode is deliberately left out of `meta` so that
lifetimes in attribute token trees (`#[foo(Bar<'a>)]`) are not treated
as an unterminated char literal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread src/languages/rust.js
"assert_ne!",
"debug_assert_ne!"
];
const QUOTE_STRING = hljs.inherit(hljs.QUOTE_STRING_MODE, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are we still getting from QUOTE_STRING_MODE? escapes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — the end: /"/ and the BACKSLASH_ESCAPE handling. illegal: null clears QUOTE_STRING_MODE's illegal: '\n' so multiline strings still highlight. (This rule is unchanged from before — the PR just extracts it into a named constant so the attribute rule can reuse it.)

Comment thread src/languages/rust.js Outdated
});
const RAW_STRING = {
scope: 'string',
begin: /b?r(#*)"(.|\n)*?"\1(?!#)/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the run of # really be infinite?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so lets add a ################ sample to the specs, just cause. If not lets add the max size (if it's reasonable) and tighten this rule.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No — rustc caps raw string delimiters at 255 # symbols ("too many # symbols: raw strings may be delimited by up to 255 # symbols"). Tightened the rule to #{0,255} and added a many-hash sample to the specs. Verified the boundary: 255 hashes highlights as one raw string, while 256 hashes (invalid Rust) no longer matches the raw-string rule.

rustc rejects raw string literals delimited by more than 255 # symbols,
so tighten the rule from #* to #{0,255} and add a many-hash spec sample.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

(Rust) Escaped double quotations in string at attribute are not highlighted properly

3 participants