Skip to content

Conversation

@ryan-m-walker
Copy link
Contributor

Summary

Based on feedback from @ematipico in PR #8255 I realized that I had forgotten to add the new if bogus nodes to the to_bogus function in my PR that added the functionality. This PR adds those nodes to the functoin and also improves the parseing recovery of the if() function parsing. It adds better diagnostics for failed parsing nodes and also now correctly recovers on branch lists dividers and boolean expressions. Based on feedback from the other PR I also added the mandator comments for the css.ungram file.

Test Plan

New error snapshot tests added and all existing tests passing

Docs

n/a

AI Usage Disclosure

Claude Code was used some to help debug and research the speculative parsing conflict issue. Most of the actual written code is my own.

@changeset-bot
Copy link

changeset-bot bot commented Dec 1, 2025

⚠️ No Changeset found

Latest commit: 7df03ef

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 labels Dec 1, 2025
Comment on lines +424 to +429
// The DeclarationOrRuleList parser that this function call will be parsed in higher up
// the node tree uses speculative parsing and the semicolon character to determine when
// parsing is complete or when to recover from a parsing error. This comes in conflict with
// error recovery parsing for if branches since this parser also uses the semicolon as a recovery
// point. To get around this, we handle our own manual recovery here by consuming all unexpected
// tokens into the CssBogusIfBranch node until we are able to recover.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me know if the reasoning here is clear or it there is a better way to handle this that I might have missed

Copy link
Member

Choose a reason for hiding this comment

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

@denbezrukov do you have any advice for this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, the problem with DeclarationOrRuleList is that we can't know whether the next item is a declaration or a rule. Currently it works by checking after parsing whether we’re in the correct parsing state: if the last token was ; or we’re at }, which marks the end of any declaration, then we assume the parse was correct.

So if we don't disable recovery, declaration parsing can recover the parser back into this “correct” state (last token is ; or }).

At this point we have a few options:

  • If we're not sure whether recovery might break rule parsing, it's safer to drop recovery entirely.
  • We can try disabling recovery for ; and } tokens, but this could lead to cases where we parse the whole file simply because we try to recover.
  • We can introduce a function similar to try_parse but with speculative parsing enabled and wrap this function. This would only be used when we're 100% sure it's impossible for a rule to appear in that position.
  • We can try a solution that checks whether recovery reached ; or }, and introduce something similar to try_parse but without speculative parsing.

I think that the last solution could be a preferable way how to handle the problem. There could be some pitfalls here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

Walkthrough

This PR extends CSS conditional formatting and parsing by introducing support for a new CssBogusIfTestBooleanExpr syntax node. Changes include adding a new formatter implementation, integrating it into the formatter dispatcher, enhancing the CSS parser with improved recovery logic and token-set-driven error handling for if-test boolean expressions, and updating the CSS grammar to define new boolean expression constructs and branching structures.

Possibly related PRs

  • #8111 – Introduces comprehensive if(...) parsing and formatting support, establishing the foundation that this PR extends with bogus node handling and enhanced recovery paths.

Suggested labels

A-Parser, A-Formatter, A-Tooling, L-CSS

Suggested reviewers

  • dyc3
  • ematipico

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: improving CSS if() function parsing recovery, which aligns with the PR objectives and the substantial parsing logic changes in the changeset.
Description check ✅ Passed The description clearly relates to the changeset, explaining the missing bogus nodes, parsing recovery improvements, and diagnostic enhancements that match the actual code changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8de2774 and 7c92cba.

⛔ Files ignored due to path filters (6)
  • 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_parser/tests/css_test_suite/error/function/if.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (11)
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs (1 hunks)
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs (1 hunks)
  • crates/biome_css_formatter/src/css/bogus/mod.rs (1 hunks)
  • crates/biome_css_formatter/src/generated.rs (1 hunks)
  • crates/biome_css_parser/src/syntax/property/mod.rs (1 hunks)
  • crates/biome_css_parser/src/syntax/value/if.rs (9 hunks)
  • crates/biome_css_parser/src/syntax/value/parse_error.rs (1 hunks)
  • crates/biome_css_parser/tests/css_test_suite/error/function/if.css (1 hunks)
  • crates/biome_css_syntax/src/lib.rs (1 hunks)
  • xtask/codegen/css.ungram (9 hunks)
  • xtask/codegen/src/css_kinds_src.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • xtask/codegen/src/css_kinds_src.rs
  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_parser/src/syntax/property/mod.rs
  • crates/biome_css_syntax/src/lib.rs
  • crates/biome_css_parser/src/syntax/value/parse_error.rs
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
🧠 Learnings (39)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnknown` prefix for rules that report mistyped entities in CSS (e.g., `noUnknownUnit`)

Applied to files:

  • xtask/codegen/src/css_kinds_src.rs
  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_syntax/src/lib.rs
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_parser/tests/css_test_suite/error/function/if.css
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • xtask/codegen/src/css_kinds_src.rs
  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_parser/src/syntax/property/mod.rs
  • crates/biome_css_syntax/src/lib.rs
  • crates/biome_css_parser/src/syntax/value/parse_error.rs
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
  • xtask/codegen/css.ungram
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : Nodes for enclosing syntax errors must have the `Bogus` word, e.g., `HtmlBogusAttribute`, and must be part of a variant

Applied to files:

  • xtask/codegen/src/css_kinds_src.rs
  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_syntax/src/lib.rs
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • xtask/codegen/css.ungram
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/Cargo.toml : Include development dependencies in `Cargo.toml` for formatter tests: `biome_formatter_test`, `biome_<language>_factory`, `biome_<language>_parser`, `biome_parser`, `biome_service`, `countme`, `iai`, `quickcheck`, `quickcheck_macros`, and `tests_macros`

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Check if a variable is global before banning it to avoid false positives when the variable is redeclared in local scope; use the semantic model to verify global scope

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/mod.rs
  • crates/biome_css_parser/src/syntax/property/mod.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/local_inference.rs : Implement local inference in dedicated modules to derive type definitions from expressions without context of surrounding scopes

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/mod.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : No module may copy or clone data from another module in the module graph, not even behind an `Arc`

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/mod.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/js_module_info/collector.rs : Implement module-level (thin) inference to resolve `TypeReference::Qualifier` variants by looking up declarations in module scopes and handling import statements

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must take a mutable reference to the parser as their only parameter and return a `ParsedSyntax`

Applied to files:

  • crates/biome_css_parser/src/syntax/property/mod.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes

Applied to files:

  • crates/biome_css_parser/src/syntax/property/mod.rs
  • crates/biome_css_parser/src/syntax/value/parse_error.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

Applied to files:

  • crates/biome_css_syntax/src/lib.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/language_kind.rs : Add a new variant to `LanguageKind` enum in `language_kind.rs` file and implement all methods for the new language variant

Applied to files:

  • crates/biome_css_syntax/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `diagnostic` function must return a `RuleDiagnostic` that defines the message reported to the user using the `markup!` macro

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:04:57.309Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:57.309Z
Learning: Applies to crates/biome_diagnostics/**/*.rs : Ensure the type implementing Diagnostic derives Debug

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-24T18:04:57.309Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:57.309Z
Learning: Applies to crates/biome_diagnostics/**/*.rs : Use #[derive(Diagnostic)] on enums when every variant contains a type that is itself a diagnostic

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-24T18:04:57.309Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:04:57.309Z
Learning: Applies to crates/biome_diagnostics/**/*.rs : Fields with #[advice] or #[verbose_advice] attributes must implement the Advices trait to record advices on the diagnostic

Applied to files:

  • crates/biome_css_parser/src/syntax/value/parse_error.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import the `FormatNode` trait and implement it for your Node when creating formatters in biome_js_formatter

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Define `FormatHtmlSyntaxNode` struct in a `cst.rs` file implementing `FormatRule<HtmlSyntaxNode>`, `AsFormat<HtmlFormatContext>`, and `IntoFormat<HtmlFormatContext>` traits using the provided boilerplate code

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/language.rs : Implement `TestFormatLanguage` trait in `tests/language.rs` for the formatter's test language

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/context.rs : Define `<Language>FormatContext` struct in a `context.rs` file containing `comments` and `source_map` fields, implementing `FormatContext` and `CstFormatContext` traits

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/lib.rs : Expose a public `format_node` function that accepts formatting options and a root syntax node, returning a `FormatResult<Formatted<Context>>` with appropriate documentation

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `FormatNodeRule<N>` trait with `fmt_fields` as the only required method; default implementations of `fmt`, `is_suppressed`, `fmt_leading_comments`, `fmt_dangling_comments`, and `fmt_trailing_comments` are provided

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: The formatter foundation relies on using the generic `Format` trait and `FormatNode` for nodes, with creation of an intermediate IR via a series of helpers

Applied to files:

  • crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to 'fix' the code; if a token/node is known to be mandatory but is missing, return `None` instead

Applied to files:

  • crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs
  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `AsFormat<Context>` trait in `lib.rs` with generic implementations for references, `SyntaxResult`, and `Option` types as provided in the formatter boilerplate code

Applied to files:

  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/src/lib.rs : Define a type alias `<Language>Formatter<'buf>` as `Formatter<'buf, <Language>FormatContext>` in the main formatter crate

Applied to files:

  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Implement the `IntoFormat<Context>` trait in `lib.rs` with implementations for `SyntaxResult` and `Option` types as part of the formatter infrastructure

Applied to files:

  • crates/biome_css_formatter/src/generated.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement a token source struct that wraps the lexer and implements `TokenSourceWithBufferedLexer` and `LexerWithCheckpoint` for lookahead and re-lexing capabilities

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ParseSeparatedList` and `ParseNodeList` for parsing lists with error recovery to avoid infinite loops

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For tokens that are not mandatory, use helper functions instead of hardcoding

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-10-25T07:22:18.540Z
Learnt from: ematipico
Repo: biomejs/biome PR: 7852
File: crates/biome_css_parser/src/syntax/property/mod.rs:161-168
Timestamp: 2025-10-25T07:22:18.540Z
Learning: In the Biome CSS parser, lexer token emission should not be gated behind parser options like `is_tailwind_directives_enabled()`. The lexer must emit correct tokens regardless of parser options to enable accurate diagnostics and error messages when the syntax is used incorrectly.

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
📚 Learning: 2025-11-09T12:47:46.298Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8031
File: crates/biome_html_parser/src/syntax/svelte.rs:140-147
Timestamp: 2025-11-09T12:47:46.298Z
Learning: In the Biome HTML parser, `expect` and `expect_with_context` consume the current token and then lex the next token. The context parameter in `expect_with_context` controls how the next token (after the consumed one) is lexed, not the current token being consumed. For example, in Svelte parsing, after `bump_with_context(T!["{:"], HtmlLexContext::Svelte)`, the next token is already lexed in the Svelte context, so `expect(T![else])` is sufficient unless the token after `else` also needs to be lexed in a specific context.

Applied to files:

  • crates/biome_css_parser/src/syntax/value/if.rs
🧬 Code graph analysis (2)
crates/biome_css_parser/src/syntax/value/parse_error.rs (1)
crates/biome_parser/src/diagnostic.rs (1)
  • expected_any (486-488)
crates/biome_css_formatter/src/generated.rs (2)
crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs (1)
  • fmt (9-20)
crates/biome_css_formatter/src/css/any/if_test.rs (1)
  • fmt (9-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Parser conformance
🔇 Additional comments (19)
crates/biome_css_parser/src/syntax/property/mod.rs (1)

241-244: Making is_at_generic_component_value pub(crate) keeps the parser API consistent

This matches the visibility of parse_generic_component_value and makes the predicate reusable across the crate without widening the public API; no behavioural change, so all good from my side.

crates/biome_css_formatter/src/css/any/if_test_boolean_expr.rs (1)

17-17: LGTM!

The new CssBogusIfTestBooleanExpr variant is handled consistently with the other match arms, delegating to the node's own formatter.

xtask/codegen/src/css_kinds_src.rs (1)

579-579: LGTM!

New bogus node kind correctly placed within the bogus nodes section, following the existing naming convention. Based on learnings, bogus nodes must contain the "Bogus" word in their name.

crates/biome_css_parser/tests/css_test_suite/error/function/if.css (1)

84-95: Good error test coverage.

These test cases exercise the new recovery paths for if-branch-list parsing and boolean expression handling. The .todo class seems intentional for tracking edge cases with and (!bogus) and not foo patterns.

crates/biome_css_formatter/src/css/bogus/mod.rs (1)

14-14: LGTM!

Module declaration correctly placed in alphabetical order, following the established pattern for bogus formatter modules.

crates/biome_css_parser/src/syntax/value/parse_error.rs (1)

33-39: LGTM!

The new diagnostic helper follows the established pattern. The labels describe what was expected when parsing a not expression within an if-test context.

crates/biome_css_formatter/src/css/bogus/bogus_if_test_boolean_expr.rs (1)

1-5: LGTM!

Standard bogus node formatter implementation. The empty trait impl delegates to the default bogus node formatting behaviour.

crates/biome_css_parser/src/syntax/value/if.rs (7)

33-34: LGTM!

Well-defined recovery token set including EOF, ensuring the recovery loop won't run indefinitely.


227-230: LGTM!

Clean helper predicate consolidating the three if-test checks.


260-267: LGTM!

Using or_recover_with_token_set with CSS_BOGUS_IF_TEST_BOOLEAN_EXPR provides better recovery for nested boolean expressions within parentheses.


297-304: LGTM!

The recovery token set correctly includes both ) and : as potential follow-up tokens after a not expression, allowing recovery in both nested and top-level contexts.


423-462: Solid manual recovery implementation.

The comment explaining the conflict with DeclarationOrRuleList's speculative parsing is helpful. The recovery logic correctly:

  1. Checks if not at a valid condition and not at a recovery token
  2. Consumes tokens into CSS_BOGUS_IF_BRANCH
  3. Reports the diagnostic with the bogus node's range
  4. Returns Present(bogus) to continue parsing

This follows the learnings for using or_recover() to wrap unparseable tokens in bogus nodes.


464-478: LGTM!

The updated RECOVERED_KIND to CSS_BOGUS_IF_TEST_BOOLEAN_EXPR aligns with the new bogus node type, and adding the check for is_at_if_test_boolean_not_expr ensures recovery can continue when a not expression follows.


488-490: LGTM!

Using the shared IF_BRANCH_RECOVERY_TOKEN_SET keeps the recovery behaviour consistent across the parser.

crates/biome_css_syntax/src/lib.rs (1)

133-135: Verify consistency with is_bogus() method.

The new bogus kinds are correctly mapped in to_bogus(). However, the is_bogus() method (lines 81-104) may not include CSS_BOGUS_IF_BRANCH, CSS_BOGUS_IF_TEST, or CSS_BOGUS_IF_TEST_BOOLEAN_EXPR. Verify whether this is intentional or if is_bogus() should be updated to include these new bogus kinds as well.

crates/biome_css_formatter/src/generated.rs (1)

7877-7916: Bogus boolean expr wiring looks spot on

This trio of impls for CssBogusIfTestBooleanExpr matches the existing bogus-node pattern (using FormatBogusNodeRule plus the usual AsFormat/IntoFormat shims) and lines up with the AnyCssIfTestBooleanExpr dispatcher. Exactly what the formatter needed for the new bogus variant.

xtask/codegen/css.ungram (3)

66-66: LGTM!

The new bogus node follows the established pattern and naming convention. Correctly placed in the BOGUS NODES section alongside its siblings. Based on learnings, bogus nodes must have the Bogus word and be part of a variant — both conditions are met here.


1876-1880: LGTM!

Correctly integrates the bogus node as a fallback variant for parsing recovery. This was the missing piece mentioned in the PR objectives — now malformed if-test boolean expressions can be captured without breaking the parser.


1858-1860: LGTM!

Appreciate the addition of these mandatory comments — they follow the existing documentation style and provide helpful examples for understanding the if() function syntax constructs.


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

codspeed-hq bot commented Dec 1, 2025

CodSpeed Performance Report

Merging #8321 will not alter performance

Comparing ryan-m-walker:fix/improve-css-if-function-parsing-recovery (7df03ef) with main (98ca2ae)

Summary

✅ 29 untouched
⏩ 126 skipped1

Footnotes

  1. 126 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.

Comment on lines +430 to +445
if !is_at_any_if_condition(p) {
if !p.at_ts(IF_BRANCH_RECOVERY_TOKEN_SET) {
let m = p.start();

while !p.at_ts(IF_BRANCH_RECOVERY_TOKEN_SET) {
p.bump_any();
}

let bogus = m.complete(p, CSS_BOGUS_IF_BRANCH);
p.error(expected_if_branch(p, bogus.range(p)));

return Present(bogus);
}

return Absent;
}
Copy link
Member

Choose a reason for hiding this comment

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

For custom recovery, we have a trait called ParseRecovery. You usually create an empty struct and then implement ParseRecovery for it

struct UrlModifierListParseRecovery;
impl ParseRecovery for UrlModifierListParseRecovery {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_URL_MODIFIER;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
// url("//aa.com/img.svg" foo "bar" func(test));
// ^ ^
// "bar" is an invalid modifier |____| func is the recovery point
//
// url("//aa.com/img.svg" foo "bar" );
// ^ ^
// "bar" is an invalid modifier |____| ')' is the recovery point
p.at(T![')']) || is_at_url_modifier(p)
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used that originally but faced issues with the fact that the recovery is disabled for speculative parsing which is the case when trying to parse the declaration with a semi-colon. Basically it was causing my parsing recovery to get overridden when encountering a semicolon by the declaration parsing which would then fail at that semicolon since that is a recovery token for the declaration speculative parsing as well. I think this might be a new edge case with the if() function since it might be the only time that a semicolon has been introduced other than at the end of a declaration (that I can think of). That's why I opted to bypass the typical parser recovery here and just handle a one off manual recovery here.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants