Skip to content

Commit

Permalink
fix(css_parser): fix Constant crashes when editing css files #3256
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Jun 22, 2024
1 parent 6ad7fbf commit 373db83
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 17 deletions.
17 changes: 2 additions & 15 deletions crates/biome_css_parser/src/syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ use biome_parser::parse_recovery::{ParseRecovery, ParseRecoveryTokenSet, Recover
use biome_parser::prelude::ParsedSyntax;
use biome_parser::prelude::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser};
use biome_parser::diagnostic::expected_token;
use value::dimension::{is_at_any_dimension, parse_any_dimension};
use value::function::{is_at_any_function, parse_any_function};
use crate::syntax::property::color::{is_at_color, parse_color};

use self::parse_error::{expected_component_value, expected_declaration_item};

Expand Down Expand Up @@ -287,21 +289,6 @@ pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax {
}
}

#[inline]
pub(crate) fn is_at_color(p: &mut CssParser) -> bool {
p.at(T![#])
}
#[inline]
pub(crate) fn parse_color(p: &mut CssParser) -> ParsedSyntax {
if !is_at_color(p) {
return Absent;
}
let m = p.start();
p.bump_with_context(T![#], CssLexContext::Color);
p.expect(CSS_COLOR_LITERAL);
Present(m.complete(p, CSS_COLOR))
}

struct CssComponentValueList;
impl ParseNodeList for CssComponentValueList {
type Kind = CssSyntaxKind;
Expand Down
34 changes: 34 additions & 0 deletions crates/biome_css_parser/src/syntax/property/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use biome_css_syntax::CssSyntaxKind::{CSS_COLOR, CSS_COLOR_LITERAL};
use biome_css_syntax::{T, TextRange};
use biome_parser::diagnostic::{expected_node, ParseDiagnostic};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::Parser;
use crate::lexer::CssLexContext;
use crate::parser::CssParser;

#[inline]
pub(crate) fn is_at_color(p: &mut CssParser) -> bool {
p.at(T![#])
}
#[inline]
pub(crate) fn parse_color(p: &mut CssParser) -> ParsedSyntax {
if !is_at_color(p) {
return Absent;
}

let m = p.start();
p.bump_with_context(T![#], CssLexContext::Color);

if !p.eat(CSS_COLOR_LITERAL) {
p.error(expected_color(p, p.cur_range()));
}

Present(m.complete(p, CSS_COLOR))
}

/// Generates a parse diagnostic for an expected "color" error message at the given range.
pub(crate) fn expected_color(p: &CssParser, range: TextRange) -> ParseDiagnostic {
expected_node("color", range, p)
.with_hint("Ensure the color is specified in a valid hexadecimal format (#RRGGBB or #RGB). Examples: #000, #000f, #ffffff, #ffffffff")
}
1 change: 1 addition & 0 deletions crates/biome_css_parser/src/syntax/property/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod unicode_range;
pub(crate) mod color;

use crate::lexer::CssLexContext;
use crate::parser::CssParser;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.formTable tbody td {
border-left: 1px # solid;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
source: crates/biome_css_parser/tests/spec_test.rs
assertion_line: 169
expression: snapshot
---
## Input

```css
.formTable tbody td {
border-left: 1px # solid;
}

```


## AST

```
CssRoot {
bom_token: missing (optional),
rules: CssRuleList [
CssQualifiedRule {
prelude: CssSelectorList [
CssComplexSelector {
left: CssComplexSelector {
left: CssCompoundSelector {
nesting_selector_token: missing (optional),
simple_selector: missing (optional),
sub_selectors: CssSubSelectorList [
CssClassSelector {
dot_token: DOT@0..1 "." [] [],
name: CssCustomIdentifier {
value_token: IDENT@1..10 "formTable" [] [],
},
},
],
},
combinator: CSS_SPACE_LITERAL@10..11 " " [] [],
right: CssCompoundSelector {
nesting_selector_token: missing (optional),
simple_selector: CssTypeSelector {
namespace: missing (optional),
ident: CssIdentifier {
value_token: IDENT@11..16 "tbody" [] [],
},
},
sub_selectors: CssSubSelectorList [],
},
},
combinator: CSS_SPACE_LITERAL@16..17 " " [] [],
right: CssCompoundSelector {
nesting_selector_token: missing (optional),
simple_selector: CssTypeSelector {
namespace: missing (optional),
ident: CssIdentifier {
value_token: IDENT@17..20 "td" [] [Whitespace(" ")],
},
},
sub_selectors: CssSubSelectorList [],
},
},
],
block: CssDeclarationOrRuleBlock {
l_curly_token: L_CURLY@20..21 "{" [] [],
items: CssDeclarationOrRuleList [
CssDeclarationWithSemicolon {
declaration: CssDeclaration {
property: CssGenericProperty {
name: CssIdentifier {
value_token: IDENT@21..34 "border-left" [Newline("\n"), Whitespace("\t")] [],
},
colon_token: COLON@34..36 ":" [] [Whitespace(" ")],
value: CssGenericComponentValueList [
CssRegularDimension {
value_token: CSS_NUMBER_LITERAL@36..37 "1" [] [],
unit_token: IDENT@37..40 "px" [] [Whitespace(" ")],
},
CssColor {
hash_token: HASH@40..42 "#" [] [Whitespace(" ")],
value_token: missing (required),
},
CssIdentifier {
value_token: IDENT@42..47 "solid" [] [],
},
],
},
important: missing (optional),
},
semicolon_token: SEMICOLON@47..48 ";" [] [],
},
],
r_curly_token: R_CURLY@48..50 "}" [Newline("\n")] [],
},
},
],
eof_token: EOF@50..51 "" [Newline("\n")] [],
}
```

## CST

```
0: CSS_ROOT@0..51
0: (empty)
1: CSS_RULE_LIST@0..50
0: CSS_QUALIFIED_RULE@0..50
0: CSS_SELECTOR_LIST@0..20
0: CSS_COMPLEX_SELECTOR@0..20
0: CSS_COMPLEX_SELECTOR@0..16
0: CSS_COMPOUND_SELECTOR@0..10
0: (empty)
1: (empty)
2: CSS_SUB_SELECTOR_LIST@0..10
0: CSS_CLASS_SELECTOR@0..10
0: DOT@0..1 "." [] []
1: CSS_CUSTOM_IDENTIFIER@1..10
0: IDENT@1..10 "formTable" [] []
1: CSS_SPACE_LITERAL@10..11 " " [] []
2: CSS_COMPOUND_SELECTOR@11..16
0: (empty)
1: CSS_TYPE_SELECTOR@11..16
0: (empty)
1: CSS_IDENTIFIER@11..16
0: IDENT@11..16 "tbody" [] []
2: CSS_SUB_SELECTOR_LIST@16..16
1: CSS_SPACE_LITERAL@16..17 " " [] []
2: CSS_COMPOUND_SELECTOR@17..20
0: (empty)
1: CSS_TYPE_SELECTOR@17..20
0: (empty)
1: CSS_IDENTIFIER@17..20
0: IDENT@17..20 "td" [] [Whitespace(" ")]
2: CSS_SUB_SELECTOR_LIST@20..20
1: CSS_DECLARATION_OR_RULE_BLOCK@20..50
0: L_CURLY@20..21 "{" [] []
1: CSS_DECLARATION_OR_RULE_LIST@21..48
0: CSS_DECLARATION_WITH_SEMICOLON@21..48
0: CSS_DECLARATION@21..47
0: CSS_GENERIC_PROPERTY@21..47
0: CSS_IDENTIFIER@21..34
0: IDENT@21..34 "border-left" [Newline("\n"), Whitespace("\t")] []
1: COLON@34..36 ":" [] [Whitespace(" ")]
2: CSS_GENERIC_COMPONENT_VALUE_LIST@36..47
0: CSS_REGULAR_DIMENSION@36..40
0: CSS_NUMBER_LITERAL@36..37 "1" [] []
1: IDENT@37..40 "px" [] [Whitespace(" ")]
1: CSS_COLOR@40..42
0: HASH@40..42 "#" [] [Whitespace(" ")]
1: (empty)
2: CSS_IDENTIFIER@42..47
0: IDENT@42..47 "solid" [] []
1: (empty)
1: SEMICOLON@47..48 ";" [] []
2: R_CURLY@48..50 "}" [Newline("\n")] []
2: EOF@50..51 "" [Newline("\n")] []

```

## Diagnostics

```
color_error.css:2:21 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Expected a color but instead found 'solid'.

1 │ .formTable tbody td {
> 2 │ border-left: 1px # solid;
│ ^^^^^
3 │ }
4 │

i Expected a color here.

1 │ .formTable tbody td {
> 2 │ border-left: 1px # solid;
│ ^^^^^
3 │ }
4 │

i Ensure the color is specified in a valid hexadecimal format (#RRGGBB or #RGB). Examples: #000, #000f, #ffffff, #ffffffff

```
5 changes: 3 additions & 2 deletions crates/biome_css_parser/tests/spec_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,9 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_
#[test]
pub fn quick_test() {
let code = r#"
@font-face { color: U+0100-024F; }
.formTable tbody td {
border-left: 1px # solid;
}
"#;

let root = parse_css(
Expand Down

0 comments on commit 373db83

Please sign in to comment.