This commit is contained in:
Ryan Walker 2025-12-22 10:54:06 +02:00 committed by GitHub
commit eb16eb8360
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1243 additions and 640 deletions

View file

@ -3585,6 +3585,16 @@ where
slots,
))
}
pub fn css_bogus_if_test_boolean_expr<I>(slots: I) -> CssBogusIfTestBooleanExpr
where
I: IntoIterator<Item = Option<SyntaxElement>>,
I::IntoIter: ExactSizeIterator,
{
CssBogusIfTestBooleanExpr::unwrap_cast(SyntaxNode::new_detached(
CssSyntaxKind::CSS_BOGUS_IF_TEST_BOOLEAN_EXPR,
slots,
))
}
pub fn css_bogus_keyframes_item<I>(slots: I) -> CssBogusKeyframesItem
where
I: IntoIterator<Item = Option<SyntaxElement>>,

View file

@ -24,6 +24,7 @@ impl SyntaxFactory for CssSyntaxFactory {
| CSS_BOGUS_FONT_FEATURE_VALUES_ITEM
| CSS_BOGUS_IF_BRANCH
| CSS_BOGUS_IF_TEST
| CSS_BOGUS_IF_TEST_BOOLEAN_EXPR
| CSS_BOGUS_KEYFRAMES_ITEM
| CSS_BOGUS_KEYFRAMES_NAME
| CSS_BOGUS_LAYER

View file

@ -14,6 +14,7 @@ impl FormatRule<AnyCssIfTestBooleanExpr> for FormatAnyCssIfTestBooleanExpr {
AnyCssIfTestBooleanExpr::AnyCssIfTestBooleanOrCombinableExpr(node) => {
node.format().fmt(f)
}
AnyCssIfTestBooleanExpr::CssBogusIfTestBooleanExpr(node) => node.format().fmt(f),
AnyCssIfTestBooleanExpr::CssIfTestBooleanNotExpr(node) => node.format().fmt(f),
}
}

View file

@ -0,0 +1,5 @@
use crate::FormatBogusNodeRule;
use biome_css_syntax::CssBogusIfTestBooleanExpr;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssBogusIfTestBooleanExpr;
impl FormatBogusNodeRule<CssBogusIfTestBooleanExpr> for FormatCssBogusIfTestBooleanExpr {}

View file

@ -11,6 +11,7 @@ pub(crate) mod bogus_font_family_name;
pub(crate) mod bogus_font_feature_values_item;
pub(crate) mod bogus_if_branch;
pub(crate) mod bogus_if_test;
pub(crate) mod bogus_if_test_boolean_expr;
pub(crate) mod bogus_keyframes_item;
pub(crate) mod bogus_keyframes_name;
pub(crate) mod bogus_layer;

View file

@ -7873,6 +7873,46 @@ impl IntoFormat<CssFormatContext> for biome_css_syntax::CssBogusIfTest {
)
}
}
impl FormatRule<biome_css_syntax::CssBogusIfTestBooleanExpr>
for crate::css::bogus::bogus_if_test_boolean_expr::FormatCssBogusIfTestBooleanExpr
{
type Context = CssFormatContext;
#[inline(always)]
fn fmt(
&self,
node: &biome_css_syntax::CssBogusIfTestBooleanExpr,
f: &mut CssFormatter,
) -> FormatResult<()> {
FormatBogusNodeRule::<biome_css_syntax::CssBogusIfTestBooleanExpr>::fmt(self, node, f)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::CssBogusIfTestBooleanExpr {
type Format<'a> = FormatRefWithRule<
'a,
biome_css_syntax::CssBogusIfTestBooleanExpr,
crate::css::bogus::bogus_if_test_boolean_expr::FormatCssBogusIfTestBooleanExpr,
>;
fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(
self,
crate::css::bogus::bogus_if_test_boolean_expr::FormatCssBogusIfTestBooleanExpr::default(
),
)
}
}
impl IntoFormat<CssFormatContext> for biome_css_syntax::CssBogusIfTestBooleanExpr {
type Format = FormatOwnedWithRule<
biome_css_syntax::CssBogusIfTestBooleanExpr,
crate::css::bogus::bogus_if_test_boolean_expr::FormatCssBogusIfTestBooleanExpr,
>;
fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(
self,
crate::css::bogus::bogus_if_test_boolean_expr::FormatCssBogusIfTestBooleanExpr::default(
),
)
}
}
impl FormatRule<biome_css_syntax::CssBogusKeyframesItem>
for crate::css::bogus::bogus_keyframes_item::FormatCssBogusKeyframesItem
{

View file

@ -239,7 +239,7 @@ impl ParseNodeList for GenericComponentValueList {
}
#[inline]
fn is_at_generic_component_value(p: &mut CssParser) -> bool {
pub(crate) fn is_at_generic_component_value(p: &mut CssParser) -> bool {
is_at_any_value(p) || is_at_generic_delimiter(p)
}

View file

@ -2,12 +2,15 @@ use biome_css_syntax::CssSyntaxKind;
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::T;
use biome_parser::Parser;
use biome_parser::TokenSet;
use biome_parser::parse_lists::ParseNodeList;
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parse_recovery::ParseRecovery;
use biome_parser::parse_recovery::ParseRecoveryTokenSet;
use biome_parser::parse_recovery::RecoveryResult;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::prelude::{CompletedMarker, ParsedSyntax};
use biome_parser::token_set;
use crate::parser::CssParser;
use crate::syntax::at_rule::container::error::expected_any_container_style_query;
@ -25,6 +28,10 @@ use crate::syntax::parse_declaration;
use crate::syntax::property::GenericComponentValueList;
use crate::syntax::value::parse_error::expected_if_branch;
use crate::syntax::value::parse_error::expected_if_test_boolean_expr_group;
use crate::syntax::value::parse_error::expected_if_test_boolean_not_expr;
const IF_BRANCH_RECOVERY_TOKEN_SET: TokenSet<CssSyntaxKind> =
token_set![T![;], T![')'], T!['}'], EOF];
pub(crate) fn is_at_if_function(p: &mut CssParser) -> bool {
p.at(T![if])
@ -217,6 +224,11 @@ fn parse_if_media_test(p: &mut CssParser) -> ParsedSyntax {
Present(m.complete(p, CSS_IF_MEDIA_TEST))
}
#[inline]
fn is_at_if_test(p: &mut CssParser) -> bool {
is_at_if_supports_test(p) || is_at_if_style_test(p) || is_at_if_media_test(p)
}
#[inline]
fn parse_if_test(p: &mut CssParser) -> ParsedSyntax {
if is_at_if_supports_test(p) {
@ -234,13 +246,26 @@ fn parse_if_test(p: &mut CssParser) -> ParsedSyntax {
Absent
}
#[inline]
fn is_at_if_test_boolean_expr_group(p: &mut CssParser) -> bool {
p.at(T!['(']) || is_at_if_test(p)
}
#[inline]
fn parse_any_if_test_boolean_expr_group(p: &mut CssParser) -> ParsedSyntax {
// ( <boolean-expr> )
if p.at(T!['(']) {
let m = p.start();
p.bump(T!['(']);
parse_any_if_test_boolean_expr(p).ok();
parse_any_if_test_boolean_expr(p)
.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(CSS_BOGUS_IF_TEST_BOOLEAN_EXPR, token_set![T![')']]),
expected_if_test_boolean_not_expr,
)
.ok();
p.expect(T![')']);
return Present(m.complete(p, CSS_IF_TEST_BOOLEAN_EXPR_IN_PARENS));
}
@ -269,7 +294,14 @@ fn parse_if_test_boolean_not_expr(p: &mut CssParser) -> ParsedSyntax {
let m = p.start();
p.bump(T![not]);
parse_any_if_test_boolean_expr_group(p).ok();
parse_any_if_test_boolean_expr_group(p)
.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(CSS_BOGUS_IF_TEST_BOOLEAN_EXPR, token_set![T![')'], T![:]]),
expected_if_test_boolean_expr_group,
)
.ok();
Present(m.complete(p, CSS_IF_TEST_BOOLEAN_NOT_EXPR))
}
@ -371,6 +403,11 @@ fn parse_any_if_test_boolean_expr(p: &mut CssParser) -> ParsedSyntax {
})
}
#[inline]
fn is_at_any_if_condition(p: &mut CssParser) -> bool {
p.at(T![else]) || is_at_if_test_boolean_expr_group(p) || is_at_if_test_boolean_not_expr(p)
}
#[inline]
fn parse_any_if_condition(p: &mut CssParser) -> ParsedSyntax {
if p.at(T![else]) {
@ -384,10 +421,37 @@ fn parse_any_if_condition(p: &mut CssParser) -> ParsedSyntax {
#[inline]
fn parse_if_branch(p: &mut CssParser) -> ParsedSyntax {
// 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.
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;
}
let m = p.start();
parse_any_if_condition(p)
.or_recover(p, &AnyIfTestParseRecovery, expected_if_branch)
.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(CSS_BOGUS_IF_BRANCH, token_set![T![')'], T![:]]),
expected_if_branch,
)
.ok();
p.expect(T![:]);
@ -402,28 +466,17 @@ struct AnyIfTestBooleanExprChainParseRecovery;
impl ParseRecovery for AnyIfTestBooleanExprChainParseRecovery {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_IF_TEST_BOOLEAN_EXPR;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_if_test_boolean_not_expr(p)
p.at(T![')'])
|| is_at_if_test_boolean_not_expr(p)
|| is_at_if_test_boolean_and_expr(p)
|| is_at_if_test_boolean_or_expr(p)
|| p.has_preceding_line_break()
}
}
struct AnyIfTestParseRecovery;
impl ParseRecovery for AnyIfTestParseRecovery {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_IF_TEST;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T![')']) || p.has_preceding_line_break()
}
}
struct IfBranchListParseRecovery;
impl ParseRecovery for IfBranchListParseRecovery {
@ -433,7 +486,7 @@ impl ParseRecovery for IfBranchListParseRecovery {
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_IF_BRANCH;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T![;]) || p.at(T![')']) || p.has_preceding_line_break()
p.at_ts(IF_BRANCH_RECOVERY_TOKEN_SET)
}
}

View file

@ -30,3 +30,10 @@ pub(crate) fn expected_if_test_boolean_expr_group(
) -> ParseDiagnostic {
expected_any(&["parenthesized boolean expression", "if test"], range, p)
}
pub(crate) fn expected_if_test_boolean_not_expr(
p: &CssParser,
range: TextRange,
) -> ParseDiagnostic {
expected_any(&["not boolean expression", "if test"], range, p)
}

View file

@ -81,3 +81,15 @@
.empty-if {
color: if();
}
.if-branch-list-recovery {
a: if(style(--foo: bar): black; !bogus; else: white);
a: if(style(--foo: bar): black; ???; else: white);
}
.todo {
background: if(style(--foo: bar) and (!bogus): red;);
background: if(not foo: red;);
}

View file

@ -544,6 +544,7 @@ pub enum CssSyntaxKind {
CSS_BOGUS_SUPPORTS_CONDITION,
CSS_BOGUS_IF_BRANCH,
CSS_BOGUS_IF_TEST,
CSS_BOGUS_IF_TEST_BOOLEAN_EXPR,
CSS_METAVARIABLE,
#[doc(hidden)]
__LAST,

View file

@ -919,6 +919,11 @@ macro_rules! map_syntax_node {
let $pattern = unsafe { $crate::CssBogusIfTest::new_unchecked(node) };
$body
}
$crate::CssSyntaxKind::CSS_BOGUS_IF_TEST_BOOLEAN_EXPR => {
let $pattern =
unsafe { $crate::CssBogusIfTestBooleanExpr::new_unchecked(node) };
$body
}
$crate::CssSyntaxKind::CSS_BOGUS_KEYFRAMES_ITEM => {
let $pattern = unsafe { $crate::CssBogusKeyframesItem::new_unchecked(node) };
$body

View file

@ -9620,6 +9620,7 @@ impl AnyCssIfTestBooleanAndCombinableExpr {
pub enum AnyCssIfTestBooleanExpr {
AnyCssIfTestBooleanAndCombinableExpr(AnyCssIfTestBooleanAndCombinableExpr),
AnyCssIfTestBooleanOrCombinableExpr(AnyCssIfTestBooleanOrCombinableExpr),
CssBogusIfTestBooleanExpr(CssBogusIfTestBooleanExpr),
CssIfTestBooleanNotExpr(CssIfTestBooleanNotExpr),
}
impl AnyCssIfTestBooleanExpr {
@ -9639,6 +9640,12 @@ impl AnyCssIfTestBooleanExpr {
_ => None,
}
}
pub fn as_css_bogus_if_test_boolean_expr(&self) -> Option<&CssBogusIfTestBooleanExpr> {
match &self {
Self::CssBogusIfTestBooleanExpr(item) => Some(item),
_ => None,
}
}
pub fn as_css_if_test_boolean_not_expr(&self) -> Option<&CssIfTestBooleanNotExpr> {
match &self {
Self::CssIfTestBooleanNotExpr(item) => Some(item),
@ -24506,6 +24513,11 @@ impl From<AnyCssIfTestBooleanAndCombinableExpr> for SyntaxElement {
node.into()
}
}
impl From<CssBogusIfTestBooleanExpr> for AnyCssIfTestBooleanExpr {
fn from(node: CssBogusIfTestBooleanExpr) -> Self {
Self::CssBogusIfTestBooleanExpr(node)
}
}
impl From<CssIfTestBooleanNotExpr> for AnyCssIfTestBooleanExpr {
fn from(node: CssIfTestBooleanNotExpr) -> Self {
Self::CssIfTestBooleanNotExpr(node)
@ -24515,10 +24527,11 @@ impl AstNode for AnyCssIfTestBooleanExpr {
type Language = Language;
const KIND_SET: SyntaxKindSet<Language> = AnyCssIfTestBooleanAndCombinableExpr::KIND_SET
.union(AnyCssIfTestBooleanOrCombinableExpr::KIND_SET)
.union(CssBogusIfTestBooleanExpr::KIND_SET)
.union(CssIfTestBooleanNotExpr::KIND_SET);
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
CSS_IF_TEST_BOOLEAN_NOT_EXPR => true,
CSS_BOGUS_IF_TEST_BOOLEAN_EXPR | CSS_IF_TEST_BOOLEAN_NOT_EXPR => true,
k if AnyCssIfTestBooleanAndCombinableExpr::can_cast(k) => true,
k if AnyCssIfTestBooleanOrCombinableExpr::can_cast(k) => true,
_ => false,
@ -24526,6 +24539,9 @@ impl AstNode for AnyCssIfTestBooleanExpr {
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
CSS_BOGUS_IF_TEST_BOOLEAN_EXPR => {
Self::CssBogusIfTestBooleanExpr(CssBogusIfTestBooleanExpr { syntax })
}
CSS_IF_TEST_BOOLEAN_NOT_EXPR => {
Self::CssIfTestBooleanNotExpr(CssIfTestBooleanNotExpr { syntax })
}
@ -24552,6 +24568,7 @@ impl AstNode for AnyCssIfTestBooleanExpr {
}
fn syntax(&self) -> &SyntaxNode {
match self {
Self::CssBogusIfTestBooleanExpr(it) => it.syntax(),
Self::CssIfTestBooleanNotExpr(it) => it.syntax(),
Self::AnyCssIfTestBooleanAndCombinableExpr(it) => it.syntax(),
Self::AnyCssIfTestBooleanOrCombinableExpr(it) => it.syntax(),
@ -24559,6 +24576,7 @@ impl AstNode for AnyCssIfTestBooleanExpr {
}
fn into_syntax(self) -> SyntaxNode {
match self {
Self::CssBogusIfTestBooleanExpr(it) => it.into_syntax(),
Self::CssIfTestBooleanNotExpr(it) => it.into_syntax(),
Self::AnyCssIfTestBooleanAndCombinableExpr(it) => it.into_syntax(),
Self::AnyCssIfTestBooleanOrCombinableExpr(it) => it.into_syntax(),
@ -24570,6 +24588,7 @@ impl std::fmt::Debug for AnyCssIfTestBooleanExpr {
match self {
Self::AnyCssIfTestBooleanAndCombinableExpr(it) => std::fmt::Debug::fmt(it, f),
Self::AnyCssIfTestBooleanOrCombinableExpr(it) => std::fmt::Debug::fmt(it, f),
Self::CssBogusIfTestBooleanExpr(it) => std::fmt::Debug::fmt(it, f),
Self::CssIfTestBooleanNotExpr(it) => std::fmt::Debug::fmt(it, f),
}
}
@ -24579,6 +24598,7 @@ impl From<AnyCssIfTestBooleanExpr> for SyntaxNode {
match n {
AnyCssIfTestBooleanExpr::AnyCssIfTestBooleanAndCombinableExpr(it) => it.into_syntax(),
AnyCssIfTestBooleanExpr::AnyCssIfTestBooleanOrCombinableExpr(it) => it.into_syntax(),
AnyCssIfTestBooleanExpr::CssBogusIfTestBooleanExpr(it) => it.into_syntax(),
AnyCssIfTestBooleanExpr::CssIfTestBooleanNotExpr(it) => it.into_syntax(),
}
}
@ -30936,6 +30956,62 @@ impl From<CssBogusIfTest> for SyntaxElement {
}
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
pub struct CssBogusIfTestBooleanExpr {
syntax: SyntaxNode,
}
impl CssBogusIfTestBooleanExpr {
#[doc = r" Create an AstNode from a SyntaxNode without checking its kind"]
#[doc = r""]
#[doc = r" # Safety"]
#[doc = r" This function must be guarded with a call to [AstNode::can_cast]"]
#[doc = r" or a match on [SyntaxNode::kind]"]
#[inline]
pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self {
Self { syntax }
}
pub fn items(&self) -> SyntaxElementChildren {
support::elements(&self.syntax)
}
}
impl AstNode for CssBogusIfTestBooleanExpr {
type Language = Language;
const KIND_SET: SyntaxKindSet<Language> =
SyntaxKindSet::from_raw(RawSyntaxKind(CSS_BOGUS_IF_TEST_BOOLEAN_EXPR as u16));
fn can_cast(kind: SyntaxKind) -> bool {
kind == CSS_BOGUS_IF_TEST_BOOLEAN_EXPR
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode {
&self.syntax
}
fn into_syntax(self) -> SyntaxNode {
self.syntax
}
}
impl std::fmt::Debug for CssBogusIfTestBooleanExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CssBogusIfTestBooleanExpr")
.field("items", &DebugSyntaxElementChildren(self.items()))
.finish()
}
}
impl From<CssBogusIfTestBooleanExpr> for SyntaxNode {
fn from(n: CssBogusIfTestBooleanExpr) -> Self {
n.syntax
}
}
impl From<CssBogusIfTestBooleanExpr> for SyntaxElement {
fn from(n: CssBogusIfTestBooleanExpr) -> Self {
n.syntax.into()
}
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
pub struct CssBogusKeyframesItem {
syntax: SyntaxNode,
}
@ -31999,7 +32075,7 @@ impl From<CssValueAtRuleGenericValue> for SyntaxElement {
n.syntax.into()
}
}
biome_rowan::declare_node_union! { pub AnyCssBogusNode = CssBogus | CssBogusAtRule | CssBogusBlock | CssBogusCustomIdentifier | CssBogusDeclarationItem | CssBogusDocumentMatcher | CssBogusFontFamilyName | CssBogusFontFeatureValuesItem | CssBogusIfBranch | CssBogusIfTest | CssBogusKeyframesItem | CssBogusKeyframesName | CssBogusLayer | CssBogusMediaQuery | CssBogusPageSelectorPseudo | CssBogusParameter | CssBogusProperty | CssBogusPropertyValue | CssBogusPseudoClass | CssBogusPseudoElement | CssBogusRule | CssBogusScopeRange | CssBogusSelector | CssBogusSubSelector | CssBogusSupportsCondition | CssBogusUnicodeRangeValue | CssBogusUrlModifier | CssUnknownAtRuleComponentList | CssValueAtRuleGenericValue }
biome_rowan::declare_node_union! { pub AnyCssBogusNode = CssBogus | CssBogusAtRule | CssBogusBlock | CssBogusCustomIdentifier | CssBogusDeclarationItem | CssBogusDocumentMatcher | CssBogusFontFamilyName | CssBogusFontFeatureValuesItem | CssBogusIfBranch | CssBogusIfTest | CssBogusIfTestBooleanExpr | CssBogusKeyframesItem | CssBogusKeyframesName | CssBogusLayer | CssBogusMediaQuery | CssBogusPageSelectorPseudo | CssBogusParameter | CssBogusProperty | CssBogusPropertyValue | CssBogusPseudoClass | CssBogusPseudoElement | CssBogusRule | CssBogusScopeRange | CssBogusSelector | CssBogusSubSelector | CssBogusSupportsCondition | CssBogusUnicodeRangeValue | CssBogusUrlModifier | CssUnknownAtRuleComponentList | CssValueAtRuleGenericValue }
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct CssBracketedValueList {
syntax_list: SyntaxList,

View file

@ -131,6 +131,9 @@ impl biome_rowan::SyntaxKind for CssSyntaxKind {
kind if AnyCssFontFeatureValuesBlock::can_cast(*kind) => CSS_BOGUS_BLOCK,
kind if AnyCssUnicodeValue::can_cast(*kind) => CSS_BOGUS_UNICODE_RANGE_VALUE,
kind if AnyCssSupportsCondition::can_cast(*kind) => CSS_BOGUS_SUPPORTS_CONDITION,
kind if AnyCssIfBranch::can_cast(*kind) => CSS_BOGUS_IF_BRANCH,
kind if AnyCssIfTest::can_cast(*kind) => CSS_BOGUS_IF_TEST,
kind if AnyCssIfTestBooleanExpr::can_cast(*kind) => CSS_BOGUS_IF_TEST_BOOLEAN_EXPR,
_ => CSS_BOGUS,
}

View file

@ -63,6 +63,7 @@ CssBogusUnicodeRangeValue = SyntaxElement*
CssBogusSupportsCondition = SyntaxElement*
CssBogusIfBranch = SyntaxElement*
CssBogusIfTest = SyntaxElement*
CssBogusIfTestBooleanExpr = SyntaxElement*
CssRoot =
bom: 'UNICODE_BOM'?
@ -1859,12 +1860,16 @@ CssParameter = AnyCssExpression
// supports( [ <ident> : <declaration-value> ] | <supports-condition> ) |
// media( <media-feature> | <media-condition> ) |
// style( <style-query> )
//
// if(style(--color: green)): red);
CssIfFunction =
'if'
'('
CssIfBranchList
')'
// if(style(--theme: dark): red; else: blue)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CssIfBranchList = AnyCssIfBranch (';' AnyCssIfBranch)* ';'?
// https://drafts.csswg.org/css-values-5/#typedef-boolean-expr
@ -1876,8 +1881,12 @@ AnyCssIfTestBooleanExpr =
CssIfTestBooleanNotExpr
| AnyCssIfTestBooleanAndCombinableExpr
| AnyCssIfTestBooleanOrCombinableExpr
| CssBogusIfTestBooleanExpr
// not <boolean-expr-group>
//
// if(not style(--color: green): red)
// ^^^^^^^^^^^^^^^^^^^^^^^^
CssIfTestBooleanNotExpr =
'not'
expression: AnyCssIfTestBooleanExprGroup
@ -1886,6 +1895,8 @@ AnyCssIfTestBooleanAndCombinableExpr =
AnyCssIfTestBooleanExprGroup
| CssIfTestBooleanAndExpr
// if(style(--color: green) and style(--color: blue)): red)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CssIfTestBooleanAndExpr =
left: AnyCssIfTestBooleanExprGroup
'and'
@ -1895,6 +1906,8 @@ AnyCssIfTestBooleanOrCombinableExpr =
AnyCssIfTestBooleanExprGroup
| CssIfTestBooleanOrExpr
// if(style(--color: green) or style(--color: blue)): red)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CssIfTestBooleanOrExpr =
left: AnyCssIfTestBooleanExprGroup
'or'
@ -1906,15 +1919,22 @@ AnyCssIfTestBooleanExprGroup =
| CssIfTestBooleanExprInParens
// ( <boolean-expr> )
//
// if(style(--foo: bar) and (style(--fiz: buzz) or style(--baz: qux)): red)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CssIfTestBooleanExprInParens =
'('
expression: AnyCssIfTestBooleanExpr
')'
// if(media(width < 700px): 0 auto; else: 20px auto;);
// ^^^^
CssElseKeyword = 'else'
AnyCssIfCondition = AnyCssIfTestBooleanExpr | CssElseKeyword
// if(style(--color: green): red; else: blue)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
CssIfBranch =
condition: AnyCssIfCondition
':'
@ -1924,6 +1944,8 @@ AnyCssIfBranch =
CssIfBranch
| CssBogusIfBranch
// supports(color: lch(75% 0 0)): lch(75% 0 0)
// ^^^^^^^^^^^^^^^^^^^
CssIfSupportsIdentifierTest =
ident: CssIdentifier
':'
@ -1933,6 +1955,8 @@ AnyCssIfSupportsTestCondition =
CssIfSupportsIdentifierTest
| AnyCssImportSupportsCondition
// if(supports(selector(:buffering)): 1em;)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CssIfSupportsTest =
'supports'
'('
@ -1943,6 +1967,8 @@ AnyCssIfMediaTestQuery =
AnyCssMediaCondition
| AnyCssQueryFeature
// if(media(width > 700px): calc(3rem + 2vw))
// ^^^^^^^^^^^^^^^^^^^^
CssIfMediaTest =
'media'
'('

View file

@ -577,6 +577,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc {
"CSS_BOGUS_SUPPORTS_CONDITION",
"CSS_BOGUS_IF_BRANCH",
"CSS_BOGUS_IF_TEST",
"CSS_BOGUS_IF_TEST_BOOLEAN_EXPR",
// Grit metavariable
"CSS_METAVARIABLE",
],