Skip to content

Commit 86d4e7c

Browse files
authored
fix(css_parser): fix constant crashes when editing css files #3256 (#3257)
1 parent 6ad7fbf commit 86d4e7c

File tree

7 files changed

+226
-18
lines changed

7 files changed

+226
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
1616
#### New features
1717

1818
- Implement [CSS unicode range](https://github.com/biomejs/biome/pull/3251). Contributed by @denbezrukov
19-
-
19+
2020
### Formatter
2121

2222
#### Bug fixes
2323

2424
- Fix [#3184](https://github.com/biomejs/biome/issues/3184) CSS formatter converts custom identifiers to lowercase. Contributed by @denbezrukov
25+
- Fix [#3256](https://github.com/biomejs/biome/issues/3256) constant crashes when editing css files #3256. Contributed by @denbezrukov
2526

2627
## v1.8.2 (2024-06-20)
2728

crates/biome_css_parser/src/syntax/mod.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::parser::CssParser;
1111
use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule};
1212
use crate::syntax::block::parse_declaration_or_rule_list_block;
1313
use crate::syntax::parse_error::{expected_any_rule, expected_non_css_wide_keyword_identifier};
14+
use crate::syntax::property::color::{is_at_color, parse_color};
1415
use crate::syntax::property::unicode_range::{is_at_unicode_range, parse_unicode_range};
1516
use crate::syntax::property::{is_at_any_property, parse_any_property};
1617
use crate::syntax::selector::is_nth_at_selector;
@@ -287,21 +288,6 @@ pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax {
287288
}
288289
}
289290

290-
#[inline]
291-
pub(crate) fn is_at_color(p: &mut CssParser) -> bool {
292-
p.at(T![#])
293-
}
294-
#[inline]
295-
pub(crate) fn parse_color(p: &mut CssParser) -> ParsedSyntax {
296-
if !is_at_color(p) {
297-
return Absent;
298-
}
299-
let m = p.start();
300-
p.bump_with_context(T![#], CssLexContext::Color);
301-
p.expect(CSS_COLOR_LITERAL);
302-
Present(m.complete(p, CSS_COLOR))
303-
}
304-
305291
struct CssComponentValueList;
306292
impl ParseNodeList for CssComponentValueList {
307293
type Kind = CssSyntaxKind;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use crate::lexer::CssLexContext;
2+
use crate::parser::CssParser;
3+
use biome_css_syntax::CssSyntaxKind::{CSS_COLOR, CSS_COLOR_LITERAL};
4+
use biome_css_syntax::{TextRange, T};
5+
use biome_parser::diagnostic::{expected_node, ParseDiagnostic};
6+
use biome_parser::parsed_syntax::ParsedSyntax;
7+
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
8+
use biome_parser::Parser;
9+
10+
#[inline]
11+
pub(crate) fn is_at_color(p: &mut CssParser) -> bool {
12+
p.at(T![#])
13+
}
14+
#[inline]
15+
pub(crate) fn parse_color(p: &mut CssParser) -> ParsedSyntax {
16+
if !is_at_color(p) {
17+
return Absent;
18+
}
19+
20+
let m = p.start();
21+
let hash_range = p.cur_range();
22+
p.bump_with_context(T![#], CssLexContext::Color);
23+
24+
if !p.eat(CSS_COLOR_LITERAL) {
25+
p.error(expected_color(p, hash_range));
26+
}
27+
28+
Present(m.complete(p, CSS_COLOR))
29+
}
30+
31+
/// Generates a parse diagnostic for an expected "color" error message at the given range.
32+
pub(crate) fn expected_color(p: &CssParser, range: TextRange) -> ParseDiagnostic {
33+
expected_node("color", range, p)
34+
.with_hint("Ensure the color is specified in a valid hexadecimal format. Examples: #000, #000f, #ffffff, #ffffffff")
35+
}

crates/biome_css_parser/src/syntax/property/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub(crate) mod color;
12
pub(crate) mod unicode_range;
23

34
use crate::lexer::CssLexContext;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.formTable tbody td {
2+
border-left: 1px # solid;
3+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
source: crates/biome_css_parser/tests/spec_test.rs
3+
expression: snapshot
4+
---
5+
## Input
6+
7+
```css
8+
.formTable tbody td {
9+
border-left: 1px # solid;
10+
}
11+
12+
```
13+
14+
15+
## AST
16+
17+
```
18+
CssRoot {
19+
bom_token: missing (optional),
20+
rules: CssRuleList [
21+
CssQualifiedRule {
22+
prelude: CssSelectorList [
23+
CssComplexSelector {
24+
left: CssComplexSelector {
25+
left: CssCompoundSelector {
26+
nesting_selector_token: missing (optional),
27+
simple_selector: missing (optional),
28+
sub_selectors: CssSubSelectorList [
29+
CssClassSelector {
30+
dot_token: DOT@0..1 "." [] [],
31+
name: CssCustomIdentifier {
32+
value_token: IDENT@1..10 "formTable" [] [],
33+
},
34+
},
35+
],
36+
},
37+
combinator: CSS_SPACE_LITERAL@10..11 " " [] [],
38+
right: CssCompoundSelector {
39+
nesting_selector_token: missing (optional),
40+
simple_selector: CssTypeSelector {
41+
namespace: missing (optional),
42+
ident: CssIdentifier {
43+
value_token: IDENT@11..16 "tbody" [] [],
44+
},
45+
},
46+
sub_selectors: CssSubSelectorList [],
47+
},
48+
},
49+
combinator: CSS_SPACE_LITERAL@16..17 " " [] [],
50+
right: CssCompoundSelector {
51+
nesting_selector_token: missing (optional),
52+
simple_selector: CssTypeSelector {
53+
namespace: missing (optional),
54+
ident: CssIdentifier {
55+
value_token: IDENT@17..20 "td" [] [Whitespace(" ")],
56+
},
57+
},
58+
sub_selectors: CssSubSelectorList [],
59+
},
60+
},
61+
],
62+
block: CssDeclarationOrRuleBlock {
63+
l_curly_token: L_CURLY@20..21 "{" [] [],
64+
items: CssDeclarationOrRuleList [
65+
CssDeclarationWithSemicolon {
66+
declaration: CssDeclaration {
67+
property: CssGenericProperty {
68+
name: CssIdentifier {
69+
value_token: IDENT@21..34 "border-left" [Newline("\n"), Whitespace("\t")] [],
70+
},
71+
colon_token: COLON@34..36 ":" [] [Whitespace(" ")],
72+
value: CssGenericComponentValueList [
73+
CssRegularDimension {
74+
value_token: CSS_NUMBER_LITERAL@36..37 "1" [] [],
75+
unit_token: IDENT@37..40 "px" [] [Whitespace(" ")],
76+
},
77+
CssColor {
78+
hash_token: HASH@40..42 "#" [] [Whitespace(" ")],
79+
value_token: missing (required),
80+
},
81+
CssIdentifier {
82+
value_token: IDENT@42..47 "solid" [] [],
83+
},
84+
],
85+
},
86+
important: missing (optional),
87+
},
88+
semicolon_token: SEMICOLON@47..48 ";" [] [],
89+
},
90+
],
91+
r_curly_token: R_CURLY@48..50 "}" [Newline("\n")] [],
92+
},
93+
},
94+
],
95+
eof_token: EOF@50..51 "" [Newline("\n")] [],
96+
}
97+
```
98+
99+
## CST
100+
101+
```
102+
103+
0: (empty)
104+
105+
106+
107+
108+
109+
110+
0: (empty)
111+
1: (empty)
112+
113+
114+
0: [email protected] "." [] []
115+
116+
0: [email protected] "formTable" [] []
117+
1: [email protected] " " [] []
118+
119+
0: (empty)
120+
121+
0: (empty)
122+
123+
0: [email protected] "tbody" [] []
124+
125+
1: [email protected] " " [] []
126+
127+
0: (empty)
128+
129+
0: (empty)
130+
131+
0: [email protected] "td" [] [Whitespace(" ")]
132+
133+
134+
0: [email protected] "{" [] []
135+
1: CSS_DECLARATION_OR_RULE_LIST@21..48
136+
0: CSS_DECLARATION_WITH_SEMICOLON@21..48
137+
0: CSS_DECLARATION@21..47
138+
0: CSS_GENERIC_PROPERTY@21..47
139+
0: CSS_IDENTIFIER@21..34
140+
0: IDENT@21..34 "border-left" [Newline("\n"), Whitespace("\t")] []
141+
1: COLON@34..36 ":" [] [Whitespace(" ")]
142+
2: CSS_GENERIC_COMPONENT_VALUE_LIST@36..47
143+
0: CSS_REGULAR_DIMENSION@36..40
144+
0: CSS_NUMBER_LITERAL@36..37 "1" [] []
145+
1: IDENT@37..40 "px" [] [Whitespace(" ")]
146+
1: CSS_COLOR@40..42
147+
0: HASH@40..42 "#" [] [Whitespace(" ")]
148+
1: (empty)
149+
2: CSS_IDENTIFIER@42..47
150+
0: IDENT@42..47 "solid" [] []
151+
1: (empty)
152+
1: SEMICOLON@47..48 ";" [] []
153+
2: R_CURLY@48..50 "}" [Newline("\n")] []
154+
2: EOF@50..51 "" [Newline("\n")] []
155+
156+
```
157+
158+
## Diagnostics
159+
160+
```
161+
color_error.css:2:19 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
162+
163+
× Expected a color but instead found '#'.
164+
165+
1 │ .formTable tbody td {
166+
> 2 │ border-left: 1px # solid;
167+
^
168+
3 │ }
169+
4
170+
171+
i Expected a color here.
172+
173+
1 │ .formTable tbody td {
174+
> 2 │ border-left: 1px # solid;
175+
^
176+
3 │ }
177+
4
178+
179+
i Ensure the color is specified in a valid hexadecimal format. Examples: #000, #000f, #ffffff, #ffffffff
180+
181+
```

crates/biome_css_parser/tests/spec_test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_
174174
#[test]
175175
pub fn quick_test() {
176176
let code = r#"
177-
@font-face { color: U+0100-024F; }
178-
177+
.formTable tbody td {
178+
border-left: 1px # solid;
179+
}
179180
"#;
180181

181182
let root = parse_css(

0 commit comments

Comments
 (0)