Skip to content

feat(css): use ScssExpression in ScssNestingDeclaration and CssGenericProperty#9328

Merged
denbezrukov merged 7 commits intomainfrom
dbezrukov/scss-expr-nesting
Mar 5, 2026
Merged

feat(css): use ScssExpression in ScssNestingDeclaration and CssGenericProperty#9328
denbezrukov merged 7 commits intomainfrom
dbezrukov/scss-expr-nesting

Conversation

@denbezrukov
Copy link
Copy Markdown
Contributor

@denbezrukov denbezrukov commented Mar 4, 2026

Summary

Updates SCSS nested property parsing to use ScssExpression for ScssNestingDeclaration and CssGenericProperty values
instead of CssGenericComponentValueList.

Test Plan

cargo test -p biome_css_parser
cargo test -p biome_css_formatter

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 4, 2026

⚠️ No Changeset found

Latest commit: 91e9176

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 4, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 4, 2026

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 52977 52977 0
Passed 51757 51757 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%

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 4, 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

Replaces generic component-value handling for property and nesting declarations with SCSS expression-aware parsing and formatting. Adds parse_scss_expression_allow_empty_value_until and an empty-expression helper, introduces AnyCssGenericPropertyValueOrExpression and its formatter, threads value-end TokenSet boundaries through declaration/property parsing (including parse_declaration_with_value_end_set), and updates many parser, formatter, analysis, semantic and test files to accept or ignore SCSS expressions where appropriate. Also refactors nesting-declaration formatting to use the new union value handling.

Possibly related PRs

Suggested labels

L-SCSS

Suggested reviewers

  • denbezrukov
  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: updating SCSS nested property parsing to use ScssExpression instead of CssGenericComponentValueList.
Description check ✅ Passed The description clearly relates to the changeset by summarising the core update and providing a test plan to verify the changes.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dbezrukov/scss-expr-nesting

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.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 4, 2026

Merging this PR will not alter performance

✅ 29 untouched benchmarks
⏩ 187 skipped benchmarks1


Comparing dbezrukov/scss-expr-nesting (91e9176) with main (95914f5)

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.

@github-actions github-actions Bot added the A-Linter Area: linter label Mar 4, 2026
@denbezrukov denbezrukov changed the title feat(css): use ScssExpression in ScssNestingDeclaration feat(css): use ScssExpression in ScssNestingDeclaration and CssGenericProperty Mar 4, 2026
Copy link
Copy Markdown
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: 3

🧹 Nitpick comments (7)
crates/biome_css_parser/tests/css_test_suite/error/scss/expression/paren-map-missing-value.scss (1)

1-1: Tiny wording nit: make the fixture comment singular.

“missing pair values” reads plural, but this case has one missing value. A tighter comment will make the intent crystal clear for future readers.

🤖 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/error/scss/expression/paren-map-missing-value.scss`
at line 1, Update the fixture comment text "// Map inferred from parens should
recover missing pair values" to use singular wording since the test covers a
single missing value; replace "missing pair values" with "missing pair value" so
the comment reads "// Map inferred from parens should recover missing pair
value" to make the intent clearer for readers and maintainers.
crates/biome_css_parser/tests/css_test_suite/error/scss/expression/missing-expressions.scss (1)

13-14: Optional: add one EOF-boundary malformed-expression case.

Consider a final case with a missing expression at end-of-file (no trailing token/semicolon) to guard recovery at stream end.

🤖 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/error/scss/expression/missing-expressions.scss`
around lines 13 - 14, Add an EOF-boundary malformed-expression test variation:
create a similar map with a trailing colon but no trailing semicolon or newline
at EOF to ensure recovery at stream end (e.g. add a variable like
$map-trailing-eof with value "(b: 1, c:)" and leave the file ending immediately
after it). Place it alongside the existing $map-trailing case so the parser sees
the missing expression at EOF.
crates/biome_css_parser/tests/css_test_suite/ok/scss/value/minus-operator.scss (1)

1-1: Exclude this CSS parser test fixture from any stylelint CI rules if they're added.

This fixture intentionally uses non-standard selectors and property names to test the parser's handling of the minus operator—code that would rightfully trigger selector-type-no-unknown, property-no-unknown, and related stylelint rules. Since it's a parser test case rather than production code, either add it to .stylelintignore or configure stylelint to skip the entire crates/biome_css_parser/tests/css_test_suite/ directory.

🤖 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/minus-operator.scss`
at line 1, Exclude the test fixture minus-operator.scss (the parser test using
non-standard selectors/properties) from stylelint rules by either adding its
path or the entire css_test_suite directory to .stylelintignore or updating the
stylelint config to ignore crates/biome_css_parser/tests/css_test_suite/ so
stylelint rules like selector-type-no-unknown and property-no-unknown are
skipped for these parser test files.
crates/biome_css_parser/src/syntax/value/attr.rs (1)

171-186: Please add one regression fixture for nested if() in attr fallback.

Because fallback list recovery now stops on ;, a case like attr(x, if(media(print): a; else: b;)) is worth pinning down.

🤖 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/attr.rs` around lines 171 - 186, Add
a regression fixture that parses a nested if() inside an attr fallback like
"attr(x, if(media(print): a; else: b;))" to ensure recovery behavior (in
functions is_at_list_end and recover using ParseRecoveryTokenSet with
token_set!(T![')'], T![;]) and CSS_BOGUS_PROPERTY_VALUE /
expected_component_value) doesn't prematurely stop on the inner ';'. Create a
test case that feeds that exact string into the attr fallback parsing path and
asserts the full nested if() is accepted (or produces the expected recovery), so
this scenario is pinned down for future changes.
crates/biome_css_parser/tests/css_test_suite/ok/scss/value/binary-operators.scss (1)

19-29: The at-rule block is clearly marked TODO; nice placeholder.

If you want, I can draft the follow-up fixture set once @function/@debug support is wired in this suite.

🤖 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/binary-operators.scss`
around lines 19 - 29, The test fixture contains a TODO block noting missing
support for at-rules like `@function` and `@debug`; implement support or add
follow-up test fixtures: update the parser handling for at-rules referenced
(e.g., recognize "@function f()", "@debug") and ensure the value parsing logic
treats "/" as division only in expression contexts (e.g., in "@function f() {
return a / b; }" and "1 * 2 / 3") but not in standalone debug values like "15px
/ 30px" or in cases with parentheses, function calls, variables, and namespace
access (e.g., "(1 + 2) / 3", "fn() / 3", "$var / 3", "mod.$var / 3"); add or
enable tests for these cases once `@function/`@debug are supported and ensure AST
nodes or tokenization for at-rules and binary-operator division are handled
accordingly.
crates/biome_css_semantic/src/events.rs (1)

241-245: Drop the redundant .clone() on to_trimmed_string().

This allocates twice for no gain.

🧹 Tiny tidy-up
-                        syntax = Some(value.to_trimmed_string().clone());
+                        syntax = Some(value.to_trimmed_string());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_semantic/src/events.rs` around lines 241 - 245, The
assignment to syntax unnecessarily clones the string returned by
value.to_trimmed_string(); remove the redundant .clone() so that syntax is set
with the String returned by value.to_trimmed_string() (i.e., change the
expression inside syntax = Some(...) accordingly), leaving prop.value() and
to_trimmed_string() usage intact.
crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs (1)

109-123: Consider removing temporary Vec allocations in flat_map.

You can filter_map the list first, then flat_map its iterator directly.

♻️ Small refactor sketch
-            .flat_map(|grid_props| {
-                let value = match grid_props {
-                    Ok(value) => value,
-                    Err(_) => return Vec::new(),
-                };
-                let list = match value {
-                    AnyCssGenericPropertyValueOrExpression::CssGenericComponentValueList(list) => {
-                        list
-                    }
-                    AnyCssGenericPropertyValueOrExpression::ScssExpression(_) => return Vec::new(),
-                };
-
-                list.into_iter()
-                    .filter_map(|x| x.as_any_css_value()?.as_css_string()?.value_token().ok())
-                    .collect::<Vec<_>>()
-            })
+            .filter_map(|grid_props| {
+                let value = grid_props.ok()?;
+                match value {
+                    AnyCssGenericPropertyValueOrExpression::CssGenericComponentValueList(list) => {
+                        Some(list)
+                    }
+                    AnyCssGenericPropertyValueOrExpression::ScssExpression(_) => None,
+                }
+            })
+            .flat_map(|list| {
+                list.into_iter()
+                    .filter_map(|x| x.as_any_css_value()?.as_css_string()?.value_token().ok())
+            })
Based on learnings: "Use functional methods like `map`, `filter`, and `and_then` on `Result` and `Option` types instead of nested `if let` statements to avoid excessive indentation".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs`
around lines 109 - 123, The closure passed to flat_map allocates temporary Vecs
for early returns; instead, transform the outer iterator of grid_props using
filter_map to skip Err(_) and ScssExpression cases and yield the inner list
directly, then flat_map that list iterator and apply the existing filter_map on
each component value (referencing the closure variables grid_props, value, list
and the enum AnyCssGenericPropertyValueOrExpression). Concretely: convert the
match on grid_props to a filter_map that returns Some(list) only for
Ok(AnyCssGenericComponentValueList(list)), drop the Vec::new() returns, then
replace the inner collect::<Vec<_>>() with a flat_map over
list.into_iter().filter_map(|x|
x.as_any_css_value()?.as_css_string()?.value_token().ok()) so no intermediate
Vec is allocated.
🤖 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/at_rule/supports/mod.rs`:
- Around line 307-309: parse_supports_declaration(...) is being called with
.or_recover(...).ok() and its result discarded, which lets empty SCSS `@supports
(color:)` silently pass; change the call site to capture the result of
parse_supports_declaration(p).or_recover(p, &AnyInParensParseRecovery,
expected_declaration) and if it returns None emit the missing component-value
diagnostic (use the existing expected_declaration/expected_component_value path)
instead of swallowing it so empty values produce the proper error; update the
analogous block around lines 348-352 the same way.

In `@crates/biome_css_parser/src/syntax/property/mod.rs`:
- Around line 85-89: The enabled-path for
parse_composes_property_with_value_end_set currently ignores the incoming
value_end_set/recovery_end_set and uses the fixed ComposesClassList end set;
change the enabled-path and its recovery code to use the dynamic end set passed
into the function. Concretely, replace hard-coded ComposesClassList end-set
usage in the enabled branch and its recovery calls with the composed end set
derived from the function's value_end_set/recovery_end_set parameters (i.e.,
pass value_end_set into whatever helper or check currently uses
ComposesClassList::end() and also pass recovery_end_set into the list-recovery
routine) so both normal parse and recovery use the same boundary sets.

In
`@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/logic-eq-operator.scss`:
- Line 1: Add a file-level stylelint disable comment at the top to suppress
fixture-only noise for the synthetic selectors/properties (e.g., selector "foo"
and any "test-*" properties) by disabling the specific noisy rules (for example
selector-type-no-unknown and property-no-unknown) for the whole file; place a
single stylelint-disable comment as the first line so linting is suppressed for
this fixture and no further per-line changes are needed.

---

Nitpick comments:
In `@crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs`:
- Around line 109-123: The closure passed to flat_map allocates temporary Vecs
for early returns; instead, transform the outer iterator of grid_props using
filter_map to skip Err(_) and ScssExpression cases and yield the inner list
directly, then flat_map that list iterator and apply the existing filter_map on
each component value (referencing the closure variables grid_props, value, list
and the enum AnyCssGenericPropertyValueOrExpression). Concretely: convert the
match on grid_props to a filter_map that returns Some(list) only for
Ok(AnyCssGenericComponentValueList(list)), drop the Vec::new() returns, then
replace the inner collect::<Vec<_>>() with a flat_map over
list.into_iter().filter_map(|x|
x.as_any_css_value()?.as_css_string()?.value_token().ok()) so no intermediate
Vec is allocated.

In `@crates/biome_css_parser/src/syntax/value/attr.rs`:
- Around line 171-186: Add a regression fixture that parses a nested if() inside
an attr fallback like "attr(x, if(media(print): a; else: b;))" to ensure
recovery behavior (in functions is_at_list_end and recover using
ParseRecoveryTokenSet with token_set!(T![')'], T![;]) and
CSS_BOGUS_PROPERTY_VALUE / expected_component_value) doesn't prematurely stop on
the inner ';'. Create a test case that feeds that exact string into the attr
fallback parsing path and asserts the full nested if() is accepted (or produces
the expected recovery), so this scenario is pinned down for future changes.

In
`@crates/biome_css_parser/tests/css_test_suite/error/scss/expression/missing-expressions.scss`:
- Around line 13-14: Add an EOF-boundary malformed-expression test variation:
create a similar map with a trailing colon but no trailing semicolon or newline
at EOF to ensure recovery at stream end (e.g. add a variable like
$map-trailing-eof with value "(b: 1, c:)" and leave the file ending immediately
after it). Place it alongside the existing $map-trailing case so the parser sees
the missing expression at EOF.

In
`@crates/biome_css_parser/tests/css_test_suite/error/scss/expression/paren-map-missing-value.scss`:
- Line 1: Update the fixture comment text "// Map inferred from parens should
recover missing pair values" to use singular wording since the test covers a
single missing value; replace "missing pair values" with "missing pair value" so
the comment reads "// Map inferred from parens should recover missing pair
value" to make the intent clearer for readers and maintainers.

In
`@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/binary-operators.scss`:
- Around line 19-29: The test fixture contains a TODO block noting missing
support for at-rules like `@function` and `@debug`; implement support or add
follow-up test fixtures: update the parser handling for at-rules referenced
(e.g., recognize "@function f()", "@debug") and ensure the value parsing logic
treats "/" as division only in expression contexts (e.g., in "@function f() {
return a / b; }" and "1 * 2 / 3") but not in standalone debug values like "15px
/ 30px" or in cases with parentheses, function calls, variables, and namespace
access (e.g., "(1 + 2) / 3", "fn() / 3", "$var / 3", "mod.$var / 3"); add or
enable tests for these cases once `@function/`@debug are supported and ensure AST
nodes or tokenization for at-rules and binary-operator division are handled
accordingly.

In
`@crates/biome_css_parser/tests/css_test_suite/ok/scss/value/minus-operator.scss`:
- Line 1: Exclude the test fixture minus-operator.scss (the parser test using
non-standard selectors/properties) from stylelint rules by either adding its
path or the entire css_test_suite directory to .stylelintignore or updating the
stylelint config to ignore crates/biome_css_parser/tests/css_test_suite/ so
stylelint rules like selector-type-no-unknown and property-no-unknown are
skipped for these parser test files.

In `@crates/biome_css_semantic/src/events.rs`:
- Around line 241-245: The assignment to syntax unnecessarily clones the string
returned by value.to_trimmed_string(); remove the redundant .clone() so that
syntax is set with the String returned by value.to_trimmed_string() (i.e.,
change the expression inside syntax = Some(...) accordingly), leaving
prop.value() and to_trimmed_string() usage intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a87795a-394c-41e6-83c1-7586673e2c07

📥 Commits

Reviewing files that changed from the base of the PR and between b5804db and 104ea52.

⛔ Files ignored due to path filters (56)
  • crates/biome_css_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_formatter/tests/specs/css/scss/expression/bracketed-values.scss.snap is excluded by !**/*.snap and included by **
  • 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_formatter/tests/specs/css/scss/expression/function-call.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_container/at_rule_container_style_query_error.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/function/if.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/container-general-enclosed.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/at-rule/supports-general-enclosed.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/arglist-ellipsis-recovery.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/ellipsis.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/expression/list-recovery.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/missing-expressions.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/paren-map-missing-value.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/recovery/map-missing-value.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/qualified-name-dollar-function.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/scss/value/qualified-name-missing-member.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/page.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/comment/line.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/comment/nested.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/global-flag.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/important.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/nested-variables.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/declaration/spacing-after-colon.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/core.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/edge-cases-extra.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/empty-map.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/expression/list-map-paren.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-key-colon.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-vs-paren-list.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/unary-parenthesized.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/bracketed-values.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/css-if-function.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/division-operator.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-args-inline.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/logic-eq-operator.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/logic-ne-operator.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/minus-operator.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/number.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/operator-precedence.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/parent-and-colon-values.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/plus-operator.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/qualified-names.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/unary-operator-chain.scss.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/unary-operators.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 **
  • crates/biome_css_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (46)
  • crates/biome_css_analyze/src/lint/a11y/use_generic_font_names.rs
  • crates/biome_css_analyze/src/lint/correctness/no_invalid_grid_areas.rs
  • crates/biome_css_analyze/src/lint/suspicious/no_duplicate_font_names.rs
  • crates/biome_css_formatter/src/css/any/generic_property_value_or_expression.rs
  • crates/biome_css_formatter/src/css/any/mod.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_formatter/tests/specs/css/scss/expression/bracketed-values.scss
  • crates/biome_css_formatter/tests/specs/css/scss/expression/edge-cases.scss
  • crates/biome_css_formatter/tests/specs/css/scss/expression/formatting.scss
  • crates/biome_css_formatter/tests/specs/css/scss/expression/function-call.scss
  • crates/biome_css_parser/src/syntax/at_rule/container/mod.rs
  • crates/biome_css_parser/src/syntax/at_rule/import.rs
  • crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs
  • crates/biome_css_parser/src/syntax/declaration.rs
  • crates/biome_css_parser/src/syntax/mod.rs
  • crates/biome_css_parser/src/syntax/property/mod.rs
  • crates/biome_css_parser/src/syntax/value/attr.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/arglist-ellipsis-recovery.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/ellipsis.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/list-recovery.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/missing-expressions.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/expression/paren-map-missing-value.scss
  • crates/biome_css_parser/tests/css_test_suite/error/scss/recovery/map-missing-value.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/at-rule/supports-expression.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/complex-expressions.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/edge-cases-extra.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/fallback-values.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-key-colon.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-vs-paren-list.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/recovery/list-space-trailing-comma.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/binary-operators.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/css-if-function.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/division-operator.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/function-args-inline.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/logic-eq-operator.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/logic-ne-operator.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/minus-operator.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/number.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/operator-precedence.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/plus-operator.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/unary-operator-chain.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/value/unary-operators.scss
  • crates/biome_css_semantic/src/events.rs
  • crates/biome_css_semantic/src/semantic_model/model.rs
  • xtask/codegen/css.ungram
✅ Files skipped from review due to trivial changes (3)
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/fallback-values.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-key-colon.scss
  • crates/biome_css_parser/tests/css_test_suite/ok/scss/expression/map-vs-paren-list.scss

Comment thread crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs
Comment thread crates/biome_css_parser/src/syntax/property/mod.rs
Copy link
Copy Markdown
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/property/mod.rs (1)

85-89: ⚠️ Potential issue | 🟠 Major

Thread the dynamic boundary sets through composes parsing.

Line 111 still parses classes with a fixed end set, and recovery at Lines 155-177 is fixed too. In boundary-aware contexts, this can over-consume before recovery kicks in. Please wire value_end_set/recovery_end_set into both list-end and recovery checks.

Suggested patch
-fn parse_composes_property_with_value_end_set(
+fn parse_composes_property_with_value_end_set(
     p: &mut CssParser,
     value_end_set: TokenSet<CssSyntaxKind>,
     recovery_end_set: TokenSet<CssSyntaxKind>,
 ) -> ParsedSyntax {
@@
-        let classes = ComposesClassList.parse_list(p);
+        let classes =
+            ComposesClassList::new(value_end_set, recovery_end_set).parse_list(p);
@@
-struct ComposesClassList;
+struct ComposesClassList {
+    end_set: TokenSet<CssSyntaxKind>,
+    recovery_set: TokenSet<CssSyntaxKind>,
+}
+
+impl ComposesClassList {
+    fn new(value_end_set: TokenSet<CssSyntaxKind>, recovery_end_set: TokenSet<CssSyntaxKind>) -> Self {
+        Self {
+            end_set: value_end_set.union(token_set!(T![from])),
+            recovery_set: recovery_end_set.union(token_set!(T![from])),
+        }
+    }
+}
@@
     fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
-        p.at_ts(END_OF_COMPOSES_CLASS_TOKEN_SET)
+        p.at_ts(self.end_set)
     }
@@
-        parsed_element.or_recover(p, &ComposesClassListParseRecovery, expected_identifier)
+        parsed_element.or_recover(
+            p,
+            &ComposesClassListParseRecovery::new(self.recovery_set),
+            expected_identifier,
+        )
     }
 }
 
-struct ComposesClassListParseRecovery;
+struct ComposesClassListParseRecovery {
+    recovery_set: TokenSet<CssSyntaxKind>,
+}
+
+impl ComposesClassListParseRecovery {
+    fn new(recovery_set: TokenSet<CssSyntaxKind>) -> Self {
+        Self { recovery_set }
+    }
+}
@@
     fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
-        p.at_ts(END_OF_COMPOSES_CLASS_TOKEN_SET) || is_at_identifier(p)
+        p.at_ts(self.recovery_set) || is_at_identifier(p)
     }
 }

Also applies to: 111-112, 155-177

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

In `@crates/biome_css_parser/src/syntax/property/mod.rs` around lines 85 - 89, The
composes parsing function parse_composes_property_with_value_end_set is still
using fixed end sets when parsing class lists and during recovery (the code
paths that check for list termination and the recovery loop), which can
over-consume in boundary-aware contexts; update the list-end checks and the
recovery-condition checks inside parse_composes_property_with_value_end_set (and
the helper(s) it calls for parsing class tokens) to use the passed-in
value_end_set and recovery_end_set instead of the hard-coded/endset constants so
both the normal list termination and the recovery loop break when any token in
value_end_set or recovery_end_set is encountered; ensure you pass these sets
through to any helper functions that parse individual class items so all checks
consistently use the dynamic sets (refer to symbol
parse_composes_property_with_value_end_set and any internal parsing/recovery
loops).
🤖 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/property/mod.rs`:
- Around line 85-89: The composes parsing function
parse_composes_property_with_value_end_set is still using fixed end sets when
parsing class lists and during recovery (the code paths that check for list
termination and the recovery loop), which can over-consume in boundary-aware
contexts; update the list-end checks and the recovery-condition checks inside
parse_composes_property_with_value_end_set (and the helper(s) it calls for
parsing class tokens) to use the passed-in value_end_set and recovery_end_set
instead of the hard-coded/endset constants so both the normal list termination
and the recovery loop break when any token in value_end_set or recovery_end_set
is encountered; ensure you pass these sets through to any helper functions that
parse individual class items so all checks consistently use the dynamic sets
(refer to symbol parse_composes_property_with_value_end_set and any internal
parsing/recovery loops).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9dab6a9a-9215-4758-b28a-8d0d6b15f67e

📥 Commits

Reviewing files that changed from the base of the PR and between 104ea52 and ac5f7c3.

📒 Files selected for processing (2)
  • crates/biome_css_parser/src/syntax/mod.rs
  • crates/biome_css_parser/src/syntax/property/mod.rs

Copy link
Copy Markdown
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/at_rule/supports/mod.rs (1)

350-367: Consider extracting this value-parsing logic into a shared helper.

This block now closely mirrors the generic-property value parsing in crates/biome_css_parser/src/syntax/property/mod.rs. A shared helper would reduce behavioural drift between normal declarations and @supports declarations.

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

In `@crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs` around lines 350
- 367, The parsing logic in parse_supports_property_value duplicates the
generic-property parsing in property::mod; extract the common behavior into a
shared helper (e.g., parse_property_value_allow_empty) that encapsulates the
CssSyntaxFeatures::Scss branch using
parse_scss_expression_allow_empty_value_until and the non-SCSS branch using
GenericComponentValueList::new(...).parse_list, and use that helper from
parse_supports_property_value and the equivalent function in
crates/biome_css_parser/src/syntax/property/mod.rs; preserve the existing error
reporting via expected_component_value and p.cur_range() so callers
(parse_supports_property_value and the property parser) get identical behavior.
🤖 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/at_rule/supports/mod.rs`:
- Around line 350-367: The parsing logic in parse_supports_property_value
duplicates the generic-property parsing in property::mod; extract the common
behavior into a shared helper (e.g., parse_property_value_allow_empty) that
encapsulates the CssSyntaxFeatures::Scss branch using
parse_scss_expression_allow_empty_value_until and the non-SCSS branch using
GenericComponentValueList::new(...).parse_list, and use that helper from
parse_supports_property_value and the equivalent function in
crates/biome_css_parser/src/syntax/property/mod.rs; preserve the existing error
reporting via expected_component_value and p.cur_range() so callers
(parse_supports_property_value and the property parser) get identical behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 28162075-3e4c-4c4b-bfff-03e773ec2781

📥 Commits

Reviewing files that changed from the base of the PR and between a33d1c4 and 91e9176.

📒 Files selected for processing (1)
  • crates/biome_css_parser/src/syntax/at_rule/supports/mod.rs

@denbezrukov denbezrukov merged commit bfa6157 into main Mar 5, 2026
18 checks passed
@denbezrukov denbezrukov deleted the dbezrukov/scss-expr-nesting branch March 5, 2026 09:10
@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-Linter Area: linter 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