fix(linter): CSS noUselessEscapeInString now recognizes hex unicode escapes#9418
fix(linter): CSS noUselessEscapeInString now recognizes hex unicode escapes#9418CloveSVG wants to merge 1 commit intobiomejs:mainfrom
Conversation
…scapes The CSS `noUselessEscapeInString` rule was using JavaScript escape rules instead of CSS-specific ones. In CSS, `\` followed by 1-6 hex digits (0-9, a-f, A-F) is a valid unicode escape, but the rule only recognized `\0`-`\7` (JS octal escapes) as meaningful. This caused valid CSS hex escapes like `\e7bb` or `\e644` (commonly used in icon fonts) to be incorrectly flagged as useless and removed by the auto-fix, breaking iconfont rendering. Fixes #9385. Co-Authored-By: Claude Opus 4.6 <[email protected]>
🦋 Changeset detectedLatest commit: 1de69ed The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughThis PR corrects the CSS linter rule Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
.changeset/fix-css-unicode-escape.md (1)
5-5: Make the changeset a touch more release-note shaped.This still leans a bit into root-cause territory. I’d keep the rule name, but swap the JavaScript/CSS implementation detail for the user-visible bit: valid icon-font escapes are preserved again.
Possible wording
-Fixed [`#9385`](https://github.com/biomejs/biome/issues/9385): the CSS [`noUselessEscapeInString`](https://biomejs.dev/linter/rules/no-useless-escape-in-string/) rule now correctly recognizes CSS unicode escapes (`\` followed by 1-6 hex digits). Previously, the rule used JavaScript escape rules and only treated `\0`-`\7` as meaningful, causing valid CSS hex escapes like `\e7bb` or `\e644` (used in icon fonts) to be incorrectly flagged and removed by the auto-fix. +Fixed [`#9385`](https://github.com/biomejs/biome/issues/9385): the CSS [`noUselessEscapeInString`](https://biomejs.dev/linter/rules/no-useless-escape-in-string/) rule no longer strips valid unicode escapes from string values such as icon-font `content`. Auto-fixes now preserve escapes like `\e7bb` and `\e644` instead of removing the leading backslash.As per coding guidelines, "Changeset descriptions must be written for end users (explain impact and what changed), not developers (avoid implementation details) in 1-3 sentences".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.changeset/fix-css-unicode-escape.md at line 5, Update the changeset text in .changeset/fix-css-unicode-escape.md to be a short, user-facing release note: keep the rule name "noUselessEscapeInString" and state the user-visible outcome (valid CSS icon-font escapes are preserved again), remove the JS vs CSS implementation details and root-cause discussion, and keep it to 1–3 concise sentences that explain the impact (what changed and why users care).crates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/valid.css (1)
21-23: This fixture promises a newline and only tests\\.The continuation arm in
next_useless_escapestill isn’t covered here;.g::afteronly exercises an escaped backslash. Either add a real backslash-newline case or trim the comment so it says what it does on the tin.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/valid.css` around lines 21 - 23, The test fixture comment claims to test a backslash-newline continuation but the rule `.g::after { content: "\\\" }` only tests an escaped backslash; update the fixture so the continuation branch in next_useless_escape is exercised by either adding a real backslash+newline case in the CSS content (a backslash character immediately followed by a literal newline inside the content value) or, if you prefer not to add that case, change the comment to accurately describe that the fixture only tests an escaped backslash; reference `.g::after` and the parser branch next_useless_escape when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rs`:
- Around line 131-132: The match arm in no_useless_escape_in_string.rs that
handles line continuations currently only matches b'\r' and b'\n'; add the
form-feed byte (b'\x0C' or b'\f') to that arm so that `\` + form feed is treated
as a valid line continuation rather than falling through to the default
useless-escape case—update the line-continuation match in the function handling
escapes in the suspicious lint to include b'\x0C'.
---
Nitpick comments:
In @.changeset/fix-css-unicode-escape.md:
- Line 5: Update the changeset text in .changeset/fix-css-unicode-escape.md to
be a short, user-facing release note: keep the rule name
"noUselessEscapeInString" and state the user-visible outcome (valid CSS
icon-font escapes are preserved again), remove the JS vs CSS implementation
details and root-cause discussion, and keep it to 1–3 concise sentences that
explain the impact (what changed and why users care).
In
`@crates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/valid.css`:
- Around line 21-23: The test fixture comment claims to test a backslash-newline
continuation but the rule `.g::after { content: "\\\" }` only tests an escaped
backslash; update the fixture so the continuation branch in next_useless_escape
is exercised by either adding a real backslash+newline case in the CSS content
(a backslash character immediately followed by a literal newline inside the
content value) or, if you prefer not to add that case, change the comment to
accurately describe that the fixture only tests an escaped backslash; reference
`.g::after` and the parser branch next_useless_escape when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ff356cde-b699-4939-80b4-7e32ae821fe1
⛔ Files ignored due to path filters (2)
crates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/invalid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/valid.css.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (4)
.changeset/fix-css-unicode-escape.mdcrates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rscrates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/invalid.csscrates/biome_css_analyze/tests/specs/suspicious/noUselessEscapeInString/valid.css
| // Line continuation: \ followed by newline or carriage return | ||
| b'\r' | b'\n' => {} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In CSS syntax, is a backslash followed by form feed (U+000C) treated as a string continuation/newline escape, alongside LF and CR?
💡 Result:
Yes—effectively.
In CSS Syntax, the input is preprocessed so that U+000C FORM FEED (and U+000D CR, and CRLF) are first converted to a single U+000A LF. [1] The tokenizer then defines newline as U+000A only (explicitly excluding CR and FF because they were converted). [1]
When consuming a string token, if a backslash is followed by a newline, that newline is consumed/ignored (a line-continuation). [2] Since FF becomes LF during preprocessing, a source sequence \ + U+000C behaves the same as \ + U+000A. [1][2]
Sources:
[1] CSS Syntax Module Level 3 — preprocessing + newline definition https://www.w3.org/TR/css-syntax-3/
[2] CSS Syntax Module Level 3 — “Consume a string token” (reverse-solidus + newline) https://www.w3.org/TR/css-syntax-3/
🏁 Script executed:
head -140 crates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rs | tail -20Repository: biomejs/biome
Length of output: 819
🏁 Script executed:
wc -l crates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rsRepository: biomejs/biome
Length of output: 136
🏁 Script executed:
cat -n crates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rsRepository: biomejs/biome
Length of output: 6103
Add form-feed character to line-continuation match.
CSS preprocesses form feed (U+000C) to LF, making \ + form feed a valid string continuation. Lines 131–132 only match CR and LF, so form feed falls through to the default case and gets flagged as a useless escape, which would incorrectly strip a meaningful continuation.
Patch
- // Line continuation: \ followed by newline or carriage return
- b'\r' | b'\n' => {}
+ // Line continuation: \ followed by newline, carriage return, or form feed
+ b'\r' | b'\n' | b'\x0C' => {}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_css_analyze/src/lint/suspicious/no_useless_escape_in_string.rs`
around lines 131 - 132, The match arm in no_useless_escape_in_string.rs that
handles line continuations currently only matches b'\r' and b'\n'; add the
form-feed byte (b'\x0C' or b'\f') to that arm so that `\` + form feed is treated
as a valid line continuation rather than falling through to the default
useless-escape case—update the line-continuation match in the function handling
escapes in the suspicious lint to include b'\x0C'.
|
No bots |
Summary
noUselessEscapeInStringrule to correctly handle CSS unicode escapes (\followed by 1-6 hex digits).\0-\7octal escapes), causing valid CSS hex escapes like\e7bband\e644to be incorrectly flagged and removed by the auto-fix.Root Cause
The
next_useless_escapefunction in the CSS version of the rule was copied from the JavaScript version without adapting it to CSS escape syntax. In CSS:\followed by 1-6 hex digits (0-9,a-f,A-F) is a valid unicode escape (e.g.,\e7bb= U+E7BB)\followed by a newline is a line continuation\followed by\\is an escaped backslash\followed by the matching quote is an escaped quoteThe old code only recognized
\0-\7as valid (JavaScript octal escapes), and included JS-specific sequences like\b,\f,\n(the letter),\t,\u,\v,\xas meaningful - none of which are special CSS escape sequences.Before/After
Before (broken):
After (fixed):
Test plan
\ato\zsince\ais now correctly recognized as a hex escape (U+000A)\e7bb,\e644,\a,\abcdef,\0,\\cargo test -p biome_css_analyze -- no_useless_escape_in_string)just gen-rulesfor code generationCloses #9385.
🤖 Generated with Claude Code