diff --git a/crates/biome_css_parser/src/syntax/value/function.rs b/crates/biome_css_parser/src/syntax/value/function.rs index d5b33d0163ca..c6dadd5111a7 100644 --- a/crates/biome_css_parser/src/syntax/value/function.rs +++ b/crates/biome_css_parser/src/syntax/value/function.rs @@ -93,6 +93,7 @@ impl ParseRecovery for ParameterListParseRecovery { /// - A comma ',', indicating the list separator. /// - The next parameter, indicating the start of a new parameter. /// - A closing parenthesis ')', indicating the end of a parameter list. + /// - A ';', indicating the end of a declaration. /// # Examples /// Basic usage in CSS: /// @@ -100,7 +101,7 @@ impl ParseRecovery for ParameterListParseRecovery { /// transform: rotate(30deg,, /* Error in parameter, recover here */) /// ``` fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { - p.at_ts(token_set!(T![,], T![')'])) || is_at_parameter(p) + p.at_ts(token_set!(T![,], T![')'], T![;])) || is_at_parameter(p) } } diff --git a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes.css.snap index a93c626934c3..b72754ba2fb8 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes.css.snap @@ -1348,339 +1348,339 @@ CssRoot { at_rule_keyframes.css:1:12 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected an identifier but instead found '{'. - + > 1 │ @keyframes { │ ^ 2 │ from { 3 │ transform: translateX(0%); - + i Expected an identifier here. - + > 1 │ @keyframes { │ ^ 2 │ from { 3 │ transform: translateX(0%); - + at_rule_keyframes.css:12:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 11 │ @keyframes slidein { > 12 │ { │ ^ 13 │ transform: translateX(0%); 14 │ } - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:18:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × expected `,` but instead found `to` - + 17 │ @keyframes slidein { > 18 │ from to { │ ^^ 19 │ transform: translateX(100%); 20 │ } - + i Remove to - + at_rule_keyframes.css:24:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 23 │ @keyframes slidein { > 24 │ 100vh { │ ^^^^^ 25 │ transform: translateX(0%); 26 │ } - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:34:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a declaration item but instead found 'to { transform: translateX(100%);'. - + > 34 │ to { │ ^^^^ > 35 │ transform: translateX(100%); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 36 │ } 37 │ } - + i Expected a declaration item here. - + > 34 │ to { │ ^^^^ > 35 │ transform: translateX(100%); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 36 │ } 37 │ } - + at_rule_keyframes.css:41:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a body but instead found 'transform: translateX('. - + 39 │ @keyframes slidein { 40 │ from > 41 │ transform: translateX(0%); │ ^^^^^^^^^^^^^^^^^^^^^^ 42 │ } - 43 │ - + 43 │ + i Expected a body here. - + 39 │ @keyframes slidein { 40 │ from > 41 │ transform: translateX(0%); │ ^^^^^^^^^^^^^^^^^^^^^^ 42 │ } - 43 │ - + 43 │ + at_rule_keyframes.css:41:27 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × expected `,` but instead found `)` - + 39 │ @keyframes slidein { 40 │ from > 41 │ transform: translateX(0%); │ ^ 42 │ } - 43 │ - + 43 │ + i Remove ) - + at_rule_keyframes.css:42:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a body but instead found '}'. - + 40 │ from 41 │ transform: translateX(0%); > 42 │ } │ ^ - 43 │ + 43 │ 44 │ to { - + i Expected a body here. - + 40 │ from 41 │ transform: translateX(0%); > 42 │ } │ ^ - 43 │ + 43 │ 44 │ to { - + at_rule_keyframes.css:47:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a qualified rule, or an at rule but instead found '}'. - + 45 │ transform: translateX(100%); 46 │ } > 47 │ } │ ^ - 48 │ + 48 │ 49 │ @keyframes slidein { - + i Expected a qualified rule, or an at rule here. - + 45 │ transform: translateX(100%); 46 │ } > 47 │ } │ ^ - 48 │ + 48 │ 49 │ @keyframes slidein { - + at_rule_keyframes.css:55:3 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a body but instead found 'transform: translateX('. - + 54 │ to > 55 │ transform: translateX(100%); │ ^^^^^^^^^^^^^^^^^^^^^^ 56 │ } 57 │ } - + i Expected a body here. - + 54 │ to > 55 │ transform: translateX(100%); │ ^^^^^^^^^^^^^^^^^^^^^^ 56 │ } 57 │ } - + at_rule_keyframes.css:55:29 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × expected `,` but instead found `)` - + 54 │ to > 55 │ transform: translateX(100%); │ ^ 56 │ } 57 │ } - + i Remove ) - + at_rule_keyframes.css:56:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a body but instead found '}'. - + 54 │ to 55 │ transform: translateX(100%); > 56 │ } │ ^ 57 │ } - 58 │ - + 58 │ + i Expected a body here. - + 54 │ to 55 │ transform: translateX(100%); > 56 │ } │ ^ 57 │ } - 58 │ - + 58 │ + at_rule_keyframes.css:57:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a qualified rule, or an at rule but instead found '}'. - + 55 │ transform: translateX(100%); 56 │ } > 57 │ } │ ^ - 58 │ + 58 │ 59 │ @keyframes slidein { - + i Expected a qualified rule, or an at rule here. - + 55 │ transform: translateX(100%); 56 │ } > 57 │ } │ ^ - 58 │ + 58 │ 59 │ @keyframes slidein { - + at_rule_keyframes.css:69:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + > 69 │ @keyframes inherit { } │ ^^^^^^^^^^^^^^^^^^ 70 │ @keyframes initial { } 71 │ @keyframes unset { } - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:70:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 69 │ @keyframes inherit { } > 70 │ @keyframes initial { } │ ^^^^^^^^^^^^^^^^^^ 71 │ @keyframes unset { } 72 │ @keyframes revert { } - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:71:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 69 │ @keyframes inherit { } 70 │ @keyframes initial { } > 71 │ @keyframes unset { } │ ^^^^^^^^^^^^^^^^ 72 │ @keyframes revert { } 73 │ @keyframes revert-layer { } - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:72:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 70 │ @keyframes initial { } 71 │ @keyframes unset { } > 72 │ @keyframes revert { } │ ^^^^^^^^^^^^^^^^^ 73 │ @keyframes revert-layer { } - 74 │ - + 74 │ + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:73:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 71 │ @keyframes unset { } 72 │ @keyframes revert { } > 73 │ @keyframes revert-layer { } │ ^^^^^^^^^^^^^^^^^^^^^^^ - 74 │ + 74 │ 75 │ @keyframes slidein - + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:75:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Unexpected value or character. - + 73 │ @keyframes revert-layer { } - 74 │ + 74 │ > 75 │ @keyframes slidein │ ^^^^^^^^^^^^^^^^^^ - 76 │ - + 76 │ + i Expected one of: - + - from - to - a percentage - + at_rule_keyframes.css:76:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ × Expected a body but instead found the end of the file. - + 75 │ @keyframes slidein - > 76 │ - │ - + > 76 │ + │ + i Expected a body here. - + 75 │ @keyframes slidein - > 76 │ - │ - + > 76 │ + │ + ``` diff --git a/crates/biome_css_parser/tests/css_test_suite/error/property/property_generic_error.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/property/property_generic_error.css.snap index bb419e7d6461..e8d00c362cc8 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/property/property_generic_error.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/property/property_generic_error.css.snap @@ -37,50 +37,67 @@ CssRoot { l_curly_token: L_CURLY@4..5 "{" [] [], declarations: CssDeclarationList [ CssDeclaration { - property: CssBogusProperty { - items: [ - CssIdentifier { - value_token: IDENT@5..17 "unknown" [Newline("\n"), Whitespace(" ")] [], + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@5..17 "unknown" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@17..19 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@19..23 "calc" [] [], + }, + l_paren_token: L_PAREN@23..24 "(" [] [], + items: CssParameterList [], + r_paren_token: missing (required), }, - COLON@17..19 ":" [] [Whitespace(" ")], - CssBogus { - items: [ - CssBogus { - items: [ - CssIdentifier { - value_token: IDENT@19..23 "calc" [] [], - }, - L_PAREN@23..24 "(" [] [], - CssBogus { - items: [ - CssBogusParameter { - items: [ - SEMICOLON@24..25 ";" [] [], - IDENT@25..35 "width" [Newline("\n"), Whitespace(" ")] [], - COLON@35..37 ":" [] [Whitespace(" ")], - IDENT@37..41 "calc" [] [], - L_PAREN@41..42 "(" [] [], - SEMICOLON@42..43 ";" [] [], - IDENT@43..56 "--custom" [Newline("\n"), Whitespace(" ")] [], - COLON@56..58 ":" [] [Whitespace(" ")], - IDENT@58..62 "calc" [] [], - L_PAREN@62..63 "(" [] [], - SEMICOLON@63..64 ";" [] [], - R_CURLY@64..66 "}" [Newline("\n")] [], - ], - }, - ], - }, - ], - }, - ], + ], + }, + important: missing (optional), + }, + SEMICOLON@24..25 ";" [] [], + CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@25..35 "width" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@35..37 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@37..41 "calc" [] [], + }, + l_paren_token: L_PAREN@41..42 "(" [] [], + items: CssParameterList [], + r_paren_token: missing (required), + }, + ], + }, + important: missing (optional), + }, + SEMICOLON@42..43 ";" [] [], + CssDeclaration { + property: CssGenericProperty { + name: CssIdentifier { + value_token: IDENT@43..56 "--custom" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@56..58 ":" [] [Whitespace(" ")], + value: CssGenericComponentValueList [ + CssFunction { + name: CssIdentifier { + value_token: IDENT@58..62 "calc" [] [], + }, + l_paren_token: L_PAREN@62..63 "(" [] [], + items: CssParameterList [], + r_paren_token: missing (required), }, ], }, important: missing (optional), }, + SEMICOLON@63..64 ";" [] [], ], - r_curly_token: missing (required), + r_curly_token: R_CURLY@64..66 "}" [Newline("\n")] [], }, }, ], @@ -105,33 +122,50 @@ CssRoot { 2: CSS_SUB_SELECTOR_LIST@4..4 1: CSS_DECLARATION_LIST_BLOCK@4..66 0: L_CURLY@4..5 "{" [] [] - 1: CSS_DECLARATION_LIST@5..66 - 0: CSS_DECLARATION@5..66 - 0: CSS_BOGUS_PROPERTY@5..66 + 1: CSS_DECLARATION_LIST@5..64 + 0: CSS_DECLARATION@5..24 + 0: CSS_GENERIC_PROPERTY@5..24 0: CSS_IDENTIFIER@5..17 0: IDENT@5..17 "unknown" [Newline("\n"), Whitespace(" ")] [] 1: COLON@17..19 ":" [] [Whitespace(" ")] - 2: CSS_BOGUS@19..66 - 0: CSS_BOGUS@19..66 + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@19..24 + 0: CSS_FUNCTION@19..24 0: CSS_IDENTIFIER@19..23 0: IDENT@19..23 "calc" [] [] 1: L_PAREN@23..24 "(" [] [] - 2: CSS_BOGUS@24..66 - 0: CSS_BOGUS_PARAMETER@24..66 - 0: SEMICOLON@24..25 ";" [] [] - 1: IDENT@25..35 "width" [Newline("\n"), Whitespace(" ")] [] - 2: COLON@35..37 ":" [] [Whitespace(" ")] - 3: IDENT@37..41 "calc" [] [] - 4: L_PAREN@41..42 "(" [] [] - 5: SEMICOLON@42..43 ";" [] [] - 6: IDENT@43..56 "--custom" [Newline("\n"), Whitespace(" ")] [] - 7: COLON@56..58 ":" [] [Whitespace(" ")] - 8: IDENT@58..62 "calc" [] [] - 9: L_PAREN@62..63 "(" [] [] - 10: SEMICOLON@63..64 ";" [] [] - 11: R_CURLY@64..66 "}" [Newline("\n")] [] + 2: CSS_PARAMETER_LIST@24..24 + 3: (empty) + 1: (empty) + 1: SEMICOLON@24..25 ";" [] [] + 2: CSS_DECLARATION@25..42 + 0: CSS_GENERIC_PROPERTY@25..42 + 0: CSS_IDENTIFIER@25..35 + 0: IDENT@25..35 "width" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@35..37 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@37..42 + 0: CSS_FUNCTION@37..42 + 0: CSS_IDENTIFIER@37..41 + 0: IDENT@37..41 "calc" [] [] + 1: L_PAREN@41..42 "(" [] [] + 2: CSS_PARAMETER_LIST@42..42 + 3: (empty) 1: (empty) - 2: (empty) + 3: SEMICOLON@42..43 ";" [] [] + 4: CSS_DECLARATION@43..63 + 0: CSS_GENERIC_PROPERTY@43..63 + 0: CSS_IDENTIFIER@43..56 + 0: IDENT@43..56 "--custom" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@56..58 ":" [] [Whitespace(" ")] + 2: CSS_GENERIC_COMPONENT_VALUE_LIST@58..63 + 0: CSS_FUNCTION@58..63 + 0: CSS_IDENTIFIER@58..62 + 0: IDENT@58..62 "calc" [] [] + 1: L_PAREN@62..63 "(" [] [] + 2: CSS_PARAMETER_LIST@63..63 + 3: (empty) + 1: (empty) + 5: SEMICOLON@63..64 ";" [] [] + 2: R_CURLY@64..66 "}" [Newline("\n")] [] 2: EOF@66..66 "" [] [] ``` @@ -141,44 +175,59 @@ CssRoot { ``` property_generic_error.css:2:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × Expected a declaration item but instead found '; - width: calc(; - --custom: calc(; - }'. + × Expected a declaration item but instead found ';'. 1 │ div { > 2 │ unknown: calc(; │ ^ - > 3 │ width: calc(; - > 4 │ --custom: calc(; - > 5 │ } - │ ^ + 3 │ width: calc(; + 4 │ --custom: calc(; i Expected a declaration item here. 1 │ div { > 2 │ unknown: calc(; │ ^ + 3 │ width: calc(; + 4 │ --custom: calc(; + +property_generic_error.css:3:17 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a declaration item but instead found ';'. + + 1 │ div { + 2 │ unknown: calc(; > 3 │ width: calc(; - > 4 │ --custom: calc(; - > 5 │ } - │ ^ + │ ^ + 4 │ --custom: calc(; + 5 │ } + + i Expected a declaration item here. -property_generic_error.css:5:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + 1 │ div { + 2 │ unknown: calc(; + > 3 │ width: calc(; + │ ^ + 4 │ --custom: calc(; + 5 │ } + +property_generic_error.css:4:20 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × expected `)` but instead the file ends + × Expected a declaration item but instead found ';'. + 2 │ unknown: calc(; 3 │ width: calc(; - 4 │ --custom: calc(; - > 5 │ } - │ + > 4 │ --custom: calc(; + │ ^ + 5 │ } - i the file ends here + i Expected a declaration item here. + 2 │ unknown: calc(; 3 │ width: calc(; - 4 │ --custom: calc(; - > 5 │ } - │ + > 4 │ --custom: calc(; + │ ^ + 5 │ } ```