Skip to content

feat(css): improve SCSS expression parsing#9396

Merged
denbezrukov merged 6 commits intomainfrom
db/exp-impovments
Mar 8, 2026
Merged

feat(css): improve SCSS expression parsing#9396
denbezrukov merged 6 commits intomainfrom
db/exp-impovments

Conversation

@denbezrukov
Copy link
Contributor

This PR was created with AI assistance (Codex).

Summary

Improves SCSS expression parsing and recovery. Its more about splitting and refactorning.

Biome now parses SCSS lists, maps, module-qualified function calls, and mixed expression forms
more consistently, including sparse comma-separated lists like 1,,3. It also improves SCSS
variable declaration recovery for malformed identifiers and modifier tails, and adds formatter
support for !important when it appears as an SCSS expression item.

Test Plan

New tests.

cargo test -p biome_css_parser/formatter

@changeset-bot
Copy link

changeset-bot bot commented Mar 8, 2026

⚠️ No Changeset found

Latest commit: 7041422

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions bot added A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools L-CSS Language: CSS and super languages labels Mar 8, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53009 53009 0
Passed 51789 51789 0
Failed 1178 1178 0
Panics 42 42 0
Coverage 97.70% 97.70% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

markdown/commonmark

Test result main count This PR count Difference
Total 652 652 0
Passed 652 652 0
Failed 0 0 0
Panics 0 0 0
Coverage 100.00% 100.00% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5466 5466 0
Passed 1915 1915 0
Failed 3551 3551 0
Panics 0 0 0
Coverage 35.03% 35.03% 0.00%

ts/babel

Test result main count This PR count Difference
Total 636 636 0
Passed 568 568 0
Failed 68 68 0
Panics 0 0 0
Coverage 89.31% 89.31% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18875 18875 0
Passed 13014 13014 0
Failed 5860 5860 0
Panics 1 1 0
Coverage 68.95% 68.95% 0.00%

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 8, 2026

Merging this PR will not alter performance

✅ 29 untouched benchmarks
⏩ 187 skipped benchmarks1


Comparing db/exp-impovments (7041422) with main (776cb64)2

Open in CodSpeed

Footnotes

  1. 187 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (1bb9edc) during the generation of this report, so 776cb64 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR modularises and extends SCSS parsing and formatting across the css crates: it splits SCSS expression parsing into submodules (list, map, precedence, primary), adds SCSS identifier/qualified-name and function-name parsing, introduces precedence-based unary/binary parsing, implements dedicated SCSS variable‑modifier parsing and recovery, centralises SCSS token sets and diagnostics, wires CssDeclarationImportant into the SCSS expression grammar and formatter, updates a few import/signature paths, and adds/updates many SCSS tests and fixtures.

Possibly related PRs

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main improvement: SCSS expression parsing enhancements, which aligns with the substantial refactoring and feature additions across expression, list, map, and identifier parsing modules.
Description check ✅ Passed The description provides relevant context about the PR's improvements to SCSS parsing (lists, maps, function calls, variable declarations, formatter support) and includes testing instructions, adequately covering the changeset's scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch db/exp-impovments

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (3)
crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-call.scss (1)

22-24: Minor: trailing blank lines.

There are a few empty lines at the end of the file. Not a blocker, but you might tidy these up if you're touching the file anyway.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-call.scss`
around lines 22 - 24, Remove the trailing blank lines at the end of
function-call.scss (the test file for scss value function-call) so the file ends
with a single newline only; open function-call.scss, delete any extra empty
lines at EOF and ensure there is exactly one terminating newline character, then
save and commit the tidy change.
crates/biome_css_parser/src/syntax/scss/expression/primary.rs (1)

11-19: Docstring example could be more illustrative.

The example foo($x: 1, $y: 2) demonstrates keyword arguments rather than the primary expression constructs this function handles (parenthesised values, maps, !important, fallback values). Consider updating to something like (key: value) or !important.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_parser/src/syntax/scss/expression/primary.rs` around lines
11 - 19, Update the docstring in primary.rs so the illustrative example matches
the kinds of SCSS primaries this parser handles: replace the current foo($x: 1,
$y: 2) example with one or more examples showing a parenthesized value and a map
and an !important/fallback usage (e.g. a parenthesized map like (key: value),
and a declaration using !important or the SCSS fallback syntax) so readers
immediately see the constructs parsed by this module.
crates/biome_css_parser/src/syntax/scss/expression/mod.rs (1)

69-75: Clarify the hardcoded ) check.

The function always treats ) as an expression end, regardless of options.end_ts. If this is intentional (e.g., for nested parenthesised contexts), a brief inline comment would clarify the design decision.

💡 Optional clarification
 #[inline]
 pub(super) fn is_at_scss_expression_end(
     p: &mut crate::parser::CssParser,
     options: ScssExpressionOptions,
 ) -> bool {
+    // `)` always ends an expression—parenthesised sub-expressions must not
+    // consume the closing paren.
     p.at_ts(options.end_ts) || p.at(T![')'])
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_parser/src/syntax/scss/expression/mod.rs` around lines 69 -
75, The helper is_at_scss_expression_end currently treats a closing parenthesis
')' as an end unconditionally which can be confusing; either add a concise
inline comment explaining that ')' must always terminate SCSS expressions (e.g.,
to handle nested parenthesized contexts) or change the logic to respect
options.end_ts (for example only treat ')' as end when options.allow_paren_end
is true). Update the function is_at_scss_expression_end to include the chosen
behavior and reference options.end_ts and the ')' check in your comment so
future readers know why ')' is handled specially.
🤖 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_parser/src/syntax/scss/declaration/variable.rs`:
- Around line 50-52: Replace the permissive bump call with a required expect for
the colon: in the code that calls parse_scss_declaration_name(p) then
p.bump(T![:]), change p.bump(T![:]) to p.expect(T![:]) so the parser emits a
diagnostic when the required ':' is missing (this affects the variable
declaration parsing flow around parse_scss_declaration_name and the parser
instance `p`).

In `@crates/biome_css_parser/src/syntax/scss/parse_error.rs`:
- Line 62: The hint currently uses p.cur_text() which only targets the current
token; instead produce a hint that targets the entire modifier tail (the full
`!…` range). Update the call site using with_hint(format!(...)) so it formats
and inserts the modifier-level text/span (the slice/span covering the whole
modifier tail) rather than p.cur_text(); locate the code that builds the
modifier error (the with_hint call that uses p.cur_text()) and replace
p.cur_text() with the parser method or slice that returns the full modifier
text/span for the error hint.

In `@crates/biome_css_parser/src/syntax/scss/token_sets.rs`:
- Around line 12-27: SCSS statement-start recovery set is missing the percent
token; update the SCSS_STATEMENT_START_SET token_set! to include T![%] so
modifier-tail resynchronization properly stops at a % (e.g., in `$x: 1 !oops
%placeholder {}`) and doesn't swallow the following rule—locate the
SCSS_STATEMENT_START_SET constant and add T![%] to the list of tokens inside the
token_set! macro.

In `@crates/biome_css_parser/src/syntax/value/function/call.rs`:
- Around line 81-83: The lookahead using p.nth_at(2, T![style]) || p.nth_at(2,
T![media]) || p.nth_at(2, T![supports]) is too permissive; change the check to
require the opening parenthesis after those identifiers so only function calls
like style(...), media(...), supports(...) match. Update the condition that uses
p.nth_at with T![style], T![media], T![supports] to also assert the next token
is an open paren (the '(' token) — i.e., only treat these as CSS functions when
the identifier is immediately followed by '('.

In `@crates/biome_css_parser/tests/css_test_suite/ok/scss/comment/multiline.scss`:
- Around line 41-46: The inner block comment inside the commented `@mixin` (the
nested /* Comment inside mixin */ within the `@mixin` test-mixin block)
prematurely closes the outer block comment and turns the trailing `}` into live
invalid SCSS; fix by replacing the inner block comment with a line comment (use
// Comment inside mixin) or remove the inner comment entirely so the outer /*
TODO... */ remains intact and the fixture stays valid.

In `@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list.scss`:
- Line 5: The double-slash TODO comment "// TODO(interpolation support):
$no-whitespaces: $variable#{something};" triggers
scss/double-slash-comment-empty-line-before; fix by inserting a blank line
before that TODO (or place the TODO on its own separated line) so the comment is
separated from the preceding declaration; locate the TODO comment referencing
$no-whitespaces and ensure a single empty line precedes it.

---

Nitpick comments:
In `@crates/biome_css_parser/src/syntax/scss/expression/mod.rs`:
- Around line 69-75: The helper is_at_scss_expression_end currently treats a
closing parenthesis ')' as an end unconditionally which can be confusing; either
add a concise inline comment explaining that ')' must always terminate SCSS
expressions (e.g., to handle nested parenthesized contexts) or change the logic
to respect options.end_ts (for example only treat ')' as end when
options.allow_paren_end is true). Update the function is_at_scss_expression_end
to include the chosen behavior and reference options.end_ts and the ')' check in
your comment so future readers know why ')' is handled specially.

In `@crates/biome_css_parser/src/syntax/scss/expression/primary.rs`:
- Around line 11-19: Update the docstring in primary.rs so the illustrative
example matches the kinds of SCSS primaries this parser handles: replace the
current foo($x: 1, $y: 2) example with one or more examples showing a
parenthesized value and a map and an !important/fallback usage (e.g. a
parenthesized map like (key: value), and a declaration using !important or the
SCSS fallback syntax) so readers immediately see the constructs parsed by this
module.

In
`@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-call.scss`:
- Around line 22-24: Remove the trailing blank lines at the end of
function-call.scss (the test file for scss value function-call) so the file ends
with a single newline only; open function-call.scss, delete any extra empty
lines at EOF and ensure there is exactly one terminating newline character, then
save and commit the tidy change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 54e320ff-a7dc-4369-b369-a73ba8461cce

📥 Commits

Reviewing files that changed from the base of the PR and between 1bb9edc and ffdbd84.

⛔ Files ignored due to path filters (29)
  • crates/biome_css_formatter/tests/specs/css/scss/expression/edge-cases.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_formatter/tests/specs/css/scss/expression/formatting.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/duplicate-modifier-recovery.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/invalid-modifier.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/modifier-recovery.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/colon-value.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/if-disambiguation-recovery.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/comment/multiline.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/complex-expressions.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/fallback-values.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/recovery/list-space-trailing-comma.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/binary-operators.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/builtin-modules.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-call.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/functions-advanced.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/if-disambiguation.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-advanced.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-empty-elements.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-important-item.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-space.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-advanced.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-expression-key.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-list-access.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/url.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (44)
  • crates/biome_css_formatter/src/scss/any/expression_item.rs
  • crates/biome_css_parser/src/lib.rs
  • crates/biome_css_parser/src/syntax/at_rule/tailwind.rs
  • crates/biome_css_parser/src/syntax/parse_error.rs
  • crates/biome_css_parser/src/syntax/scss/declaration/mod.rs
  • crates/biome_css_parser/src/syntax/scss/declaration/nesting.rs
  • crates/biome_css_parser/src/syntax/scss/declaration/variable.rs
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs
  • crates/biome_css_parser/src/syntax/scss/expression/list.rs
  • crates/biome_css_parser/src/syntax/scss/expression/map.rs
  • crates/biome_css_parser/src/syntax/scss/expression/mod.rs
  • crates/biome_css_parser/src/syntax/scss/expression/precedence.rs
  • crates/biome_css_parser/src/syntax/scss/expression/primary.rs
  • crates/biome_css_parser/src/syntax/scss/function_name.rs
  • crates/biome_css_parser/src/syntax/scss/identifiers/identifier.rs
  • crates/biome_css_parser/src/syntax/scss/identifiers/mod.rs
  • crates/biome_css_parser/src/syntax/scss/identifiers/qualified_name.rs
  • crates/biome_css_parser/src/syntax/scss/mod.rs
  • crates/biome_css_parser/src/syntax/scss/parse_error.rs
  • crates/biome_css_parser/src/syntax/scss/token_sets.rs
  • crates/biome_css_parser/src/syntax/scss/value.rs
  • crates/biome_css_parser/src/syntax/value/function/call.rs
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/invalid-modifier.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/modifier-recovery.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/colon-value.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/if-disambiguation-recovery.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/comment/multiline.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/keyword-argument-context.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/builtin-modules.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-call.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/functions-advanced.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/if-disambiguation.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-advanced.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-empty-elements.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-important-item.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list-space.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/list.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-advanced.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-expression-key.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map-list-access.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/map.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/url.scss
  • xtask/codegen/css.ungram
💤 Files with no reviewable changes (3)
  • crates/biome_css_parser/src/syntax/parse_error.rs
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/colon-value.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-map-paren.scss

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/biome_css_parser/src/syntax/value/function/call.rs (1)

81-86: ⚠️ Potential issue | 🟠 Major

CSS if() lookahead needs to verify the opening paren.

The checks for style, media, and supports at position 2 don't confirm the following ( at position 3. This means Sass-style if(media, a, b) (where media is just a value) would incorrectly be routed as CSS if().

Suggested fix
-    p.nth_at(2, T![style])
-        || p.nth_at(2, T![media])
-        || p.nth_at(2, T![supports])
+    (p.nth_at(2, T![style]) && p.nth_at(3, T!['(']))
+        || (p.nth_at(2, T![media]) && p.nth_at(3, T!['(']))
+        || (p.nth_at(2, T![supports]) && p.nth_at(3, T!['(']))
         || p.nth_at(2, T![not])
         || p.nth_at(2, T![else])
         || p.nth_at(2, T!['('])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_parser/src/syntax/value/function/call.rs` around lines 81 -
86, The lookahead in the function handling CSS if() calls is matching tokens
like p.nth_at(2, T![style|media|supports]) without ensuring an opening paren
follows, so values like `if(media, a, b)` get misclassified; update the
condition around p.nth_at to require that when p.nth_at(2,
T![style|media|supports|not|else]) is true it also checks p.nth_at(3, T!['('])
(i.e., only treat those keywords as the CSS if() form when the next token is
'('), leaving the existing direct p.nth_at(2, T!['(']) case unchanged; adjust
the combined boolean expression in the call detection logic (the p.nth_at checks
in call.rs) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/biome_css_parser/src/syntax/value/function/call.rs`:
- Around line 81-86: The lookahead in the function handling CSS if() calls is
matching tokens like p.nth_at(2, T![style|media|supports]) without ensuring an
opening paren follows, so values like `if(media, a, b)` get misclassified;
update the condition around p.nth_at to require that when p.nth_at(2,
T![style|media|supports|not|else]) is true it also checks p.nth_at(3, T!['('])
(i.e., only treat those keywords as the CSS if() form when the next token is
'('), leaving the existing direct p.nth_at(2, T!['(']) case unchanged; adjust
the combined boolean expression in the call detection logic (the p.nth_at checks
in call.rs) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b30e6a84-bf89-4038-803d-f1427ede1716

📥 Commits

Reviewing files that changed from the base of the PR and between ffdbd84 and 88afd2c.

📒 Files selected for processing (6)
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs
  • crates/biome_css_parser/src/syntax/scss/expression/list.rs
  • crates/biome_css_parser/src/syntax/scss/expression/precedence.rs
  • crates/biome_css_parser/src/syntax/scss/mod.rs
  • crates/biome_css_parser/src/syntax/scss/parse_error.rs
  • crates/biome_css_parser/src/syntax/value/function/call.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs (1)

52-57: Consider simplifying with p.bump_any().

Since you've already confirmed the token is in SCSS_VARIABLE_MODIFIER_TOKEN_SET, the explicit if-else isn't strictly necessary here. However, the current explicit approach is perfectly readable and makes the intent clear.

🔧 Optional simplification
     if p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) {
-        if p.at(T![default]) {
-            p.bump(T![default]);
-        } else {
-            p.bump(T![global]);
-        }
+        p.bump_any();
     } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs`
around lines 52 - 57, The branch that checks
p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) then distinguishes T![default] vs
T![global] can be simplified: once p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) is
true, call p.bump_any() instead of the explicit if/else that checks
p.at(T![default]) and p.bump(T![default]) or p.bump(T![global]); replace that
inner conditional with p.bump_any() to consume whichever modifier token is
present (leave the outer p.at_ts(...) guard intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs`:
- Around line 52-57: The branch that checks
p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) then distinguishes T![default] vs
T![global] can be simplified: once p.at_ts(SCSS_VARIABLE_MODIFIER_TOKEN_SET) is
true, call p.bump_any() instead of the explicit if/else that checks
p.at(T![default]) and p.bump(T![default]) or p.bump(T![global]); replace that
inner conditional with p.bump_any() to consume whichever modifier token is
present (leave the outer p.at_ts(...) guard intact).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bb7a100f-27ea-45f4-af50-2edf446e86d5

📥 Commits

Reviewing files that changed from the base of the PR and between 88afd2c and f9ee588.

⛔ Files ignored due to path filters (3)
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/invalid-modifier.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/missing-modifier.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/declaration/modifier-recovery.scss.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs
  • crates/biome_css_parser/src/syntax/scss/mod.rs
  • crates/biome_css_parser/src/syntax/scss/parse_error.rs
  • crates/biome_css_parser/src/syntax/scss/token_sets.rs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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_parser/src/syntax/scss/declaration/variable_modifier.rs`:
- Around line 87-89: Remove the erroneous newline-based short-circuit from the
modifier-list boundary logic: in is_at_list_end (and any other checks using
has_preceding_line_break for variable modifiers) stop treating
has_preceding_line_break() as a termination condition and rely solely on
SCSS_VARIABLE_MODIFIER_LIST_END_SET (which already includes ;, }, EOF) to
determine list end; update the function is_at_list_end to only check
p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET) and remove or refactor any uses of
has_preceding_line_break() in the variable modifier parsing path so wrapped
declarations like `$x: 1\n!default;` parse correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4446bfdf-c7c6-4aab-8c78-27b50383d02d

📥 Commits

Reviewing files that changed from the base of the PR and between f9ee588 and 61ff295.

📒 Files selected for processing (1)
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs (1)

114-117: ⚠️ Potential issue | 🟠 Major

Remove has_preceding_line_break() from recovery logic.

This mirrors the earlier fix applied to is_at_list_end at line 89. SCSS doesn't treat newlines as statement terminators, so a wrapped declaration like $x: 1\n!default; should still parse the modifier correctly. The existing SCSS_VARIABLE_MODIFIER_LIST_END_SET and is_at_scss_statement_boundary checks already encode proper boundaries.

Suggested fix
     fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
-        p.at(T![!]) || p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET) || p.has_preceding_line_break()
-            || is_at_scss_statement_boundary(p)
+        p.at(T![!]) || p.at_ts(SCSS_VARIABLE_MODIFIER_LIST_END_SET)
+            || is_at_scss_statement_boundary(p)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs`
around lines 114 - 117, In is_at_recovered (function) remove the
p.has_preceding_line_break() check from the recovery condition so it becomes:
check for T![!] OR SCSS_VARIABLE_MODIFIER_LIST_END_SET OR
is_at_scss_statement_boundary(p); this mirrors the fix in is_at_list_end and
ensures a newline (e.g. between value and !default) does not break modifier
parsing while keeping the existing T![!] token and
SCSS_VARIABLE_MODIFIER_LIST_END_SET / is_at_scss_statement_boundary checks
intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs`:
- Around line 114-117: In is_at_recovered (function) remove the
p.has_preceding_line_break() check from the recovery condition so it becomes:
check for T![!] OR SCSS_VARIABLE_MODIFIER_LIST_END_SET OR
is_at_scss_statement_boundary(p); this mirrors the fix in is_at_list_end and
ensures a newline (e.g. between value and !default) does not break modifier
parsing while keeping the existing T![!] token and
SCSS_VARIABLE_MODIFIER_LIST_END_SET / is_at_scss_statement_boundary checks
intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 66221eca-8942-4868-a5cf-d9846bbe807b

📥 Commits

Reviewing files that changed from the base of the PR and between 61ff295 and bb31f84.

📒 Files selected for processing (3)
  • crates/biome_css_parser/src/syntax/scss/declaration/variable_modifier.rs
  • crates/biome_css_parser/src/syntax/scss/mod.rs
  • crates/biome_css_parser/src/syntax/scss/token_sets.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_css_parser/src/syntax/scss/token_sets.rs

@denbezrukov denbezrukov merged commit 77fe3a4 into main Mar 8, 2026
17 checks passed
@denbezrukov denbezrukov deleted the db/exp-impovments branch March 8, 2026 20:24
@Netail Netail added the L-SCSS label Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Formatter Area: formatter A-Parser Area: parser A-Tooling Area: internal tools L-CSS Language: CSS and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants