From 46ce945df05f4fcb236a310cbb09e37b9747ed7c Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Sat, 16 Sep 2023 20:21:04 +0100 Subject: [PATCH 1/2] feat: basic class selector parsing --- Cargo.lock | 1 + .../src/generated/node_factory.rs | 40 +-- .../src/generated/syntax_factory.rs | 28 +-- crates/biome_css_parser/Cargo.toml | 1 + crates/biome_css_parser/src/syntax.rs | 13 - crates/biome_css_parser/src/syntax/mod.rs | 138 ++++++++++ .../error/css_unfinished_block.css | 1 + .../error/css_unfinished_block.css.snap | 77 ++++++ .../css_test_suite/ok/class_selector.css | 1 + .../css_test_suite/ok/class_selector.css.snap | 58 +++++ crates/biome_css_parser/tests/spec_test.rs | 17 ++ crates/biome_css_parser/tests/spec_tests.rs | 2 +- crates/biome_css_syntax/src/generated/kind.rs | 3 +- .../biome_css_syntax/src/generated/macros.rs | 13 +- .../biome_css_syntax/src/generated/nodes.rs | 235 ++++++++---------- .../src/generated/nodes_mut.rs | 8 - crates/biome_css_syntax/src/lib.rs | 11 +- crates/biome_rowan/src/ast/mod.rs | 2 +- crates/biome_test_utils/src/lib.rs | 2 +- xtask/codegen/css.ungram | 12 +- xtask/codegen/src/css_kinds_src.rs | 3 +- 21 files changed, 447 insertions(+), 219 deletions(-) delete mode 100644 crates/biome_css_parser/src/syntax.rs create mode 100644 crates/biome_css_parser/src/syntax/mod.rs create mode 100644 crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css create mode 100644 crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css.snap create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css create mode 100644 crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css.snap diff --git a/Cargo.lock b/Cargo.lock index 683c44f7e93f..f307a8a6f034 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,6 +240,7 @@ dependencies = [ "biome_js_unicode_table", "biome_parser", "biome_rowan", + "biome_test_utils", "insta", "quickcheck", "quickcheck_macros", diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index 8a94d1c4b001..00bad3d5e7e3 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -594,12 +594,6 @@ pub fn css_rule(prelude: CssSelectorList, block: CssBlock) -> CssRule { ], )) } -pub fn css_selector(pattern_list: CssAnySelectorPatternList) -> CssSelector { - CssSelector::unwrap_cast(SyntaxNode::new_detached( - CssSyntaxKind::CSS_SELECTOR, - [Some(SyntaxElement::Node(pattern_list.into_syntax()))], - )) -} pub fn css_simple_function( name: CssIdentifier, l_paren_token: SyntaxToken, @@ -686,18 +680,6 @@ pub fn css_var_function_value( ], )) } -pub fn css_any_selector_pattern_list(items: I) -> CssAnySelectorPatternList -where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, -{ - CssAnySelectorPatternList::unwrap_cast(SyntaxNode::new_detached( - CssSyntaxKind::CSS_ANY_SELECTOR_PATTERN_LIST, - items - .into_iter() - .map(|item| Some(item.into_syntax().into())), - )) -} pub fn css_at_keyframes_item_list(items: I) -> CssAtKeyframesItemList where I: IntoIterator, @@ -802,7 +784,7 @@ where } pub fn css_selector_list(items: I, separators: S) -> CssSelectorList where - I: IntoIterator, + I: IntoIterator, I::IntoIter: ExactSizeIterator, S: IntoIterator, S::IntoIter: ExactSizeIterator, @@ -828,3 +810,23 @@ where { CssBogus::unwrap_cast(SyntaxNode::new_detached(CssSyntaxKind::CSS_BOGUS, slots)) } +pub fn css_bogus_body(slots: I) -> CssBogusBody +where + I: IntoIterator>, + I::IntoIter: ExactSizeIterator, +{ + CssBogusBody::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_BOGUS_BODY, + slots, + )) +} +pub fn css_bogus_pattern(slots: I) -> CssBogusPattern +where + I: IntoIterator>, + I::IntoIter: ExactSizeIterator, +{ + CssBogusPattern::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_BOGUS_PATTERN, + slots, + )) +} diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 67eff2efaaac..910bb85b0b2a 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -14,7 +14,9 @@ impl SyntaxFactory for CssSyntaxFactory { children: ParsedChildren, ) -> RawSyntaxNode { match kind { - CSS_BOGUS => RawSyntaxNode::new(kind, children.into_iter().map(Some)), + CSS_BOGUS | CSS_BOGUS_BODY | CSS_BOGUS_PATTERN => { + RawSyntaxNode::new(kind, children.into_iter().map(Some)) + } CSS_ANY_FUNCTION => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); @@ -1202,25 +1204,6 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_RULE, children) } - CSS_SELECTOR => { - let mut elements = (&children).into_iter(); - let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); - let mut current_element = elements.next(); - if let Some(element) = ¤t_element { - if CssAnySelectorPatternList::can_cast(element.kind()) { - slots.mark_present(); - current_element = elements.next(); - } - } - slots.next_slot(); - if current_element.is_some() { - return RawSyntaxNode::new( - CSS_SELECTOR.to_bogus(), - children.into_iter().map(Some), - ); - } - slots.into_node(CSS_SELECTOR, children) - } CSS_SIMPLE_FUNCTION => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default(); @@ -1391,9 +1374,6 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_VAR_FUNCTION_VALUE, children) } - CSS_ANY_SELECTOR_PATTERN_LIST => { - Self::make_node_list_syntax(kind, children, AnyCssSelectorPattern::can_cast) - } CSS_AT_KEYFRAMES_ITEM_LIST => { Self::make_node_list_syntax(kind, children, CssKeyframesBlock::can_cast) } @@ -1424,7 +1404,7 @@ impl SyntaxFactory for CssSyntaxFactory { CSS_SELECTOR_LIST => Self::make_separated_list_syntax( kind, children, - CssSelector::can_cast, + AnyCssSelectorPattern::can_cast, T ! [,], false, ), diff --git a/crates/biome_css_parser/Cargo.toml b/crates/biome_css_parser/Cargo.toml index d48dc59f8fdd..31f3671c028a 100644 --- a/crates/biome_css_parser/Cargo.toml +++ b/crates/biome_css_parser/Cargo.toml @@ -25,6 +25,7 @@ insta = { workspace = true } quickcheck = { workspace = true } quickcheck_macros = { workspace = true } tests_macros = { workspace = true } +biome_test_utils = { workspace = true} # cargo-workspaces metadata [package.metadata.workspaces] diff --git a/crates/biome_css_parser/src/syntax.rs b/crates/biome_css_parser/src/syntax.rs deleted file mode 100644 index dfcd6e05705d..000000000000 --- a/crates/biome_css_parser/src/syntax.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::parser::CssParser; -use biome_css_syntax::CssSyntaxKind::*; -use biome_parser::Parser; - -pub(crate) fn parse_root(p: &mut CssParser) { - let m = p.start(); - - let rules = p.start(); - - rules.complete(p, CSS_RULE_LIST); - - m.complete(p, CSS_ROOT); -} diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs new file mode 100644 index 000000000000..2a07d488c6e4 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/mod.rs @@ -0,0 +1,138 @@ +use crate::parser::CssParser; +use biome_css_syntax::CssSyntaxKind::*; +use biome_css_syntax::{CssSyntaxKind, T}; +use biome_parser::diagnostic::expected_any; +use biome_parser::parse_recovery::ParseRecovery; +use biome_parser::prelude::ParsedSyntax::{Absent, Present}; +use biome_parser::prelude::{ParseDiagnostic, ParsedSyntax, ToDiagnostic}; +use biome_parser::{token_set, Parser, ParserProgress, TokenSet}; +use biome_rowan::TextRange; + +const SELECTOR_RECOVERY_SET: TokenSet = token_set![T!['{'], T!['}'],]; +const BODY_RECOVERY_SET: TokenSet = + SELECTOR_RECOVERY_SET.union(token_set![T![.], T![*],]); + +pub(crate) fn parse_root(p: &mut CssParser) { + let m = p.start(); + + parse_rules_list(p).expect("Parse rule list, handle this case"); + + m.complete(p, CSS_ROOT); +} + +pub(crate) fn parse_rules_list(p: &mut CssParser) -> ParsedSyntax { + let rules = p.start(); + while !p.at(EOF) { + match p.cur() { + T![.] => { + parse_rule(p).expect("Parse rule, handle this case properly"); + } + _ => return Absent, + } + } + + let completed = rules.complete(p, CSS_RULE_LIST); + + Present(completed) +} + +pub(crate) fn parse_rule(p: &mut CssParser) -> ParsedSyntax { + let m = p.start(); + if parse_selector_list(p) + .or_recover( + p, + &ParseRecovery::new(CSS_BOGUS_PATTERN, SELECTOR_RECOVERY_SET), + expect_pattern, + ) + .is_err() + { + m.abandon(p); + return Absent; + } + + if parse_css_block(p) + .or_recover( + p, + &ParseRecovery::new(CSS_BOGUS_BODY, BODY_RECOVERY_SET), + expect_block, + ) + .is_err() + { + m.abandon(p); + return Absent; + } + + let completed = m.complete(p, CSS_RULE); + Present(completed) +} + +pub(crate) fn parse_selector_list(p: &mut CssParser) -> ParsedSyntax { + let m = p.start(); + let mut progress = ParserProgress::default(); + + while !p.at(EOF) && !p.at(T!['{']) { + progress.assert_progressing(p); + + match p.cur() { + T![.] => { + parse_css_selector_pattern(p).expect("Handle this case"); + } + + _ => { + return Absent; + } + } + } + if p.at(EOF) { + m.abandon(p); + return Absent; + } + + Present(m.complete(p, CSS_SELECTOR_LIST)) +} + +#[inline] +pub(crate) fn parse_css_selector_pattern(p: &mut CssParser) -> ParsedSyntax { + if !p.at(T![.]) { + return Absent; + } + let m = p.start(); + + p.bump(T![.]); + + match p.cur() { + IDENT => { + let m = p.start(); + p.bump(IDENT); + m.complete(p, CSS_IDENTIFIER); + } + + _ => { + m.abandon(p); + return Absent; + } + } + + Present(m.complete(p, CSS_CLASS_SELECTOR_PATTERN)) +} + +pub(crate) fn parse_css_block(p: &mut CssParser) -> ParsedSyntax { + if !p.at(T!['{']) { + return Absent; + } + let m = p.start(); + p.expect(T!['{']); + let list = p.start(); + list.complete(p, CSS_DECLARATION_LIST); + p.expect(T!['}']); + + Present(m.complete(p, CSS_BLOCK)) +} + +fn expect_pattern(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expected_any(&["selector pattern"], range).into_diagnostic(p) +} + +fn expect_block(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expected_any(&["body"], range).into_diagnostic(p) +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css b/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css new file mode 100644 index 000000000000..23ed461208b5 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css @@ -0,0 +1 @@ +.action { diff --git a/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css.snap new file mode 100644 index 000000000000..0f37e6053cf2 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/error/css_unfinished_block.css.snap @@ -0,0 +1,77 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +.action { + +``` + + +## AST + +``` +CssRoot { + rules: CssRuleList [ + CssRule { + prelude: CssSelectorList [ + CssClassSelectorPattern { + dot_token: DOT@0..1 "." [] [], + name: CssIdentifier { + value_token: IDENT@1..8 "action" [] [Whitespace(" ")], + }, + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@8..9 "{" [] [], + declaration_list: CssDeclarationList [], + r_curly_token: missing (required), + }, + }, + ], + eof_token: EOF@9..10 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..10 + 0: CSS_RULE_LIST@0..9 + 0: CSS_RULE@0..9 + 0: CSS_SELECTOR_LIST@0..8 + 0: CSS_CLASS_SELECTOR_PATTERN@0..8 + 0: DOT@0..1 "." [] [] + 1: CSS_IDENTIFIER@1..8 + 0: IDENT@1..8 "action" [] [Whitespace(" ")] + 1: CSS_BLOCK@8..9 + 0: L_CURLY@8..9 "{" [] [] + 1: CSS_DECLARATION_LIST@9..9 + 2: (empty) + 1: EOF@9..10 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +css_unfinished_block.css:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `}` but instead the file ends + + 1 │ .action { + > 2 │ + │ + + i the file ends here + + 1 │ .action { + > 2 │ + │ + +``` + + diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css b/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css new file mode 100644 index 000000000000..927dc7094f96 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css @@ -0,0 +1 @@ +.action {} diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css.snap new file mode 100644 index 000000000000..6354d7fb97ad --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/class_selector.css.snap @@ -0,0 +1,58 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +expression: snapshot +--- + +## Input + +```css +.action {} + +``` + + +## AST + +``` +CssRoot { + rules: CssRuleList [ + CssRule { + prelude: CssSelectorList [ + CssClassSelectorPattern { + dot_token: DOT@0..1 "." [] [], + name: CssIdentifier { + value_token: IDENT@1..8 "action" [] [Whitespace(" ")], + }, + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@8..9 "{" [] [], + declaration_list: CssDeclarationList [], + r_curly_token: R_CURLY@9..10 "}" [] [], + }, + }, + ], + eof_token: EOF@10..11 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..11 + 0: CSS_RULE_LIST@0..10 + 0: CSS_RULE@0..10 + 0: CSS_SELECTOR_LIST@0..8 + 0: CSS_CLASS_SELECTOR_PATTERN@0..8 + 0: DOT@0..1 "." [] [] + 1: CSS_IDENTIFIER@1..8 + 0: IDENT@1..8 "action" [] [Whitespace(" ")] + 1: CSS_BLOCK@8..10 + 0: L_CURLY@8..9 "{" [] [] + 1: CSS_DECLARATION_LIST@9..9 + 2: R_CURLY@9..10 "}" [] [] + 1: EOF@10..11 "" [Newline("\n")] [] + +``` + + diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index 77fb7ac64c51..79953749c3da 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -5,6 +5,7 @@ use biome_diagnostics::display::PrintDiagnostic; use biome_diagnostics::termcolor; use biome_diagnostics::DiagnosticExt; use biome_rowan::SyntaxKind; +use biome_test_utils::has_bogus_nodes_or_empty_slots; use std::fmt::Write; use std::fs; use std::path::Path; @@ -123,3 +124,19 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ insta::assert_snapshot!(file_name, snapshot); }); } + +#[ignore] +#[test] +pub fn quick_test() { + let code = r#".action {}"#; + let root = parse_css(code, CssParserOptions::default()); + let syntax = root.syntax(); + dbg!(&syntax, root.diagnostics(), root.has_errors()); + + if has_bogus_nodes_or_empty_slots(&syntax) { + panic!( + "modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {}", + syntax + ) + } +} diff --git a/crates/biome_css_parser/tests/spec_tests.rs b/crates/biome_css_parser/tests/spec_tests.rs index 117649508a95..291fc2c349bd 100644 --- a/crates/biome_css_parser/tests/spec_tests.rs +++ b/crates/biome_css_parser/tests/spec_tests.rs @@ -3,6 +3,6 @@ mod spec_test; mod ok { - //! Tests that must pass according to the CSS specification tests_macros::gen_tests! {"tests/css_test_suite/ok/*.css", crate::spec_test::run, "ok"} + tests_macros::gen_tests! {"tests/css_test_suite/error/*.css", crate::spec_test::run, "error"} } diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index d3a60fbf132d..913780b0f293 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -216,7 +216,6 @@ pub enum CssSyntaxKind { CSS_ID_SELECTOR_PATTERN, CSS_RULE, CSS_SELECTOR_LIST, - CSS_SELECTOR, CSS_ANY_FUNCTION, CSS_AT_KEYFRAMES, CSS_AT_KEYFRAMES_BODY, @@ -264,6 +263,8 @@ pub enum CssSyntaxKind { CSS_PARAMETER_LIST, CSS_DECLARATION_IMPORTANT, CSS_BOGUS, + CSS_BOGUS_PATTERN, + CSS_BOGUS_BODY, #[doc(hidden)] __LAST, } diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index eb3ee386ca0a..b8c4770c7ee8 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -174,10 +174,6 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssRule::new_unchecked(node) }; $body } - $crate::CssSyntaxKind::CSS_SELECTOR => { - let $pattern = unsafe { $crate::CssSelector::new_unchecked(node) }; - $body - } $crate::CssSyntaxKind::CSS_SIMPLE_FUNCTION => { let $pattern = unsafe { $crate::CssSimpleFunction::new_unchecked(node) }; $body @@ -207,9 +203,12 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssBogus::new_unchecked(node) }; $body } - $crate::CssSyntaxKind::CSS_ANY_SELECTOR_PATTERN_LIST => { - let $pattern = - unsafe { $crate::CssAnySelectorPatternList::new_unchecked(node) }; + $crate::CssSyntaxKind::CSS_BOGUS_BODY => { + let $pattern = unsafe { $crate::CssBogusBody::new_unchecked(node) }; + $body + } + $crate::CssSyntaxKind::CSS_BOGUS_PATTERN => { + let $pattern = unsafe { $crate::CssBogusPattern::new_unchecked(node) }; $body } $crate::CssSyntaxKind::CSS_AT_KEYFRAMES_ITEM_LIST => { diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index e2c7a34d1208..97e54fbf7b69 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -1702,42 +1702,6 @@ pub struct CssRuleFields { pub block: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] -pub struct CssSelector { - pub(crate) syntax: SyntaxNode, -} -impl CssSelector { - #[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 as_fields(&self) -> CssSelectorFields { - CssSelectorFields { - pattern_list: self.pattern_list(), - } - } - pub fn pattern_list(&self) -> CssAnySelectorPatternList { - support::list(&self.syntax, 0usize) - } -} -#[cfg(feature = "serde")] -impl Serialize for CssSelector { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.as_fields().serialize(serializer) - } -} -#[cfg_attr(feature = "serde", derive(Serialize))] -pub struct CssSelectorFields { - pub pattern_list: CssAnySelectorPatternList, -} -#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssSimpleFunction { pub(crate) syntax: SyntaxNode, } @@ -2091,6 +2055,7 @@ impl AnyCssRule { #[cfg_attr(feature = "serde", derive(Serialize))] pub enum AnyCssSelectorPattern { CssAttributeSelectorPattern(CssAttributeSelectorPattern), + CssBogusPattern(CssBogusPattern), CssClassSelectorPattern(CssClassSelectorPattern), CssCombinatorSelectorPattern(CssCombinatorSelectorPattern), CssIdSelectorPattern(CssIdSelectorPattern), @@ -2105,6 +2070,12 @@ impl AnyCssSelectorPattern { _ => None, } } + pub fn as_css_bogus_pattern(&self) -> Option<&CssBogusPattern> { + match &self { + AnyCssSelectorPattern::CssBogusPattern(item) => Some(item), + _ => None, + } + } pub fn as_css_class_selector_pattern(&self) -> Option<&CssClassSelectorPattern> { match &self { AnyCssSelectorPattern::CssClassSelectorPattern(item) => Some(item), @@ -3848,44 +3819,6 @@ impl From for SyntaxElement { n.syntax.into() } } -impl AstNode for CssSelector { - type Language = Language; - const KIND_SET: SyntaxKindSet = - SyntaxKindSet::from_raw(RawSyntaxKind(CSS_SELECTOR as u16)); - fn can_cast(kind: SyntaxKind) -> bool { - kind == CSS_SELECTOR - } - fn cast(syntax: SyntaxNode) -> Option { - 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 CssSelector { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CssSelector") - .field("pattern_list", &self.pattern_list()) - .finish() - } -} -impl From for SyntaxNode { - fn from(n: CssSelector) -> SyntaxNode { - n.syntax - } -} -impl From for SyntaxElement { - fn from(n: CssSelector) -> SyntaxElement { - n.syntax.into() - } -} impl AstNode for CssSimpleFunction { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -4440,6 +4373,11 @@ impl From for AnyCssSelectorPattern { AnyCssSelectorPattern::CssAttributeSelectorPattern(node) } } +impl From for AnyCssSelectorPattern { + fn from(node: CssBogusPattern) -> AnyCssSelectorPattern { + AnyCssSelectorPattern::CssBogusPattern(node) + } +} impl From for AnyCssSelectorPattern { fn from(node: CssClassSelectorPattern) -> AnyCssSelectorPattern { AnyCssSelectorPattern::CssClassSelectorPattern(node) @@ -4473,6 +4411,7 @@ impl From for AnyCssSelectorPattern { impl AstNode for AnyCssSelectorPattern { type Language = Language; const KIND_SET: SyntaxKindSet = CssAttributeSelectorPattern::KIND_SET + .union(CssBogusPattern::KIND_SET) .union(CssClassSelectorPattern::KIND_SET) .union(CssCombinatorSelectorPattern::KIND_SET) .union(CssIdSelectorPattern::KIND_SET) @@ -4483,6 +4422,7 @@ impl AstNode for AnyCssSelectorPattern { matches!( kind, CSS_ATTRIBUTE_SELECTOR_PATTERN + | CSS_BOGUS_PATTERN | CSS_CLASS_SELECTOR_PATTERN | CSS_COMBINATOR_SELECTOR_PATTERN | CSS_ID_SELECTOR_PATTERN @@ -4498,6 +4438,7 @@ impl AstNode for AnyCssSelectorPattern { syntax, }) } + CSS_BOGUS_PATTERN => AnyCssSelectorPattern::CssBogusPattern(CssBogusPattern { syntax }), CSS_CLASS_SELECTOR_PATTERN => { AnyCssSelectorPattern::CssClassSelectorPattern(CssClassSelectorPattern { syntax }) } @@ -4529,6 +4470,7 @@ impl AstNode for AnyCssSelectorPattern { fn syntax(&self) -> &SyntaxNode { match self { AnyCssSelectorPattern::CssAttributeSelectorPattern(it) => &it.syntax, + AnyCssSelectorPattern::CssBogusPattern(it) => &it.syntax, AnyCssSelectorPattern::CssClassSelectorPattern(it) => &it.syntax, AnyCssSelectorPattern::CssCombinatorSelectorPattern(it) => &it.syntax, AnyCssSelectorPattern::CssIdSelectorPattern(it) => &it.syntax, @@ -4540,6 +4482,7 @@ impl AstNode for AnyCssSelectorPattern { fn into_syntax(self) -> SyntaxNode { match self { AnyCssSelectorPattern::CssAttributeSelectorPattern(it) => it.syntax, + AnyCssSelectorPattern::CssBogusPattern(it) => it.syntax, AnyCssSelectorPattern::CssClassSelectorPattern(it) => it.syntax, AnyCssSelectorPattern::CssCombinatorSelectorPattern(it) => it.syntax, AnyCssSelectorPattern::CssIdSelectorPattern(it) => it.syntax, @@ -4553,6 +4496,7 @@ impl std::fmt::Debug for AnyCssSelectorPattern { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AnyCssSelectorPattern::CssAttributeSelectorPattern(it) => std::fmt::Debug::fmt(it, f), + AnyCssSelectorPattern::CssBogusPattern(it) => std::fmt::Debug::fmt(it, f), AnyCssSelectorPattern::CssClassSelectorPattern(it) => std::fmt::Debug::fmt(it, f), AnyCssSelectorPattern::CssCombinatorSelectorPattern(it) => std::fmt::Debug::fmt(it, f), AnyCssSelectorPattern::CssIdSelectorPattern(it) => std::fmt::Debug::fmt(it, f), @@ -4566,6 +4510,7 @@ impl From for SyntaxNode { fn from(n: AnyCssSelectorPattern) -> SyntaxNode { match n { AnyCssSelectorPattern::CssAttributeSelectorPattern(it) => it.into(), + AnyCssSelectorPattern::CssBogusPattern(it) => it.into(), AnyCssSelectorPattern::CssClassSelectorPattern(it) => it.into(), AnyCssSelectorPattern::CssCombinatorSelectorPattern(it) => it.into(), AnyCssSelectorPattern::CssIdSelectorPattern(it) => it.into(), @@ -4920,11 +4865,6 @@ impl std::fmt::Display for CssRule { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for CssSelector { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for CssSimpleFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -5012,87 +4952,118 @@ impl From for SyntaxElement { n.syntax.into() } } -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct CssAnySelectorPatternList { - syntax_list: SyntaxList, +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct CssBogusBody { + syntax: SyntaxNode, } -impl CssAnySelectorPatternList { +impl CssBogusBody { #[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 unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { - Self { - syntax_list: syntax.into_list(), - } + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn items(&self) -> SyntaxElementChildren { + support::elements(&self.syntax) } } -impl AstNode for CssAnySelectorPatternList { +impl AstNode for CssBogusBody { type Language = Language; const KIND_SET: SyntaxKindSet = - SyntaxKindSet::from_raw(RawSyntaxKind(CSS_ANY_SELECTOR_PATTERN_LIST as u16)); + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_BOGUS_BODY as u16)); fn can_cast(kind: SyntaxKind) -> bool { - kind == CSS_ANY_SELECTOR_PATTERN_LIST + kind == CSS_BOGUS_BODY } - fn cast(syntax: SyntaxNode) -> Option { + fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { - Some(CssAnySelectorPatternList { - syntax_list: syntax.into_list(), - }) + Some(Self { syntax }) } else { None } } fn syntax(&self) -> &SyntaxNode { - self.syntax_list.node() + &self.syntax } fn into_syntax(self) -> SyntaxNode { - self.syntax_list.into_node() + self.syntax } } -#[cfg(feature = "serde")] -impl Serialize for CssAnySelectorPatternList { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(Some(self.len()))?; - for e in self.iter() { - seq.serialize_element(&e)?; - } - seq.end() +impl std::fmt::Debug for CssBogusBody { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssBogusBody") + .field("items", &DebugSyntaxElementChildren(self.items())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: CssBogusBody) -> SyntaxNode { + n.syntax } } -impl AstNodeList for CssAnySelectorPatternList { +impl From for SyntaxElement { + fn from(n: CssBogusBody) -> SyntaxElement { + n.syntax.into() + } +} +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct CssBogusPattern { + syntax: SyntaxNode, +} +impl CssBogusPattern { + #[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 CssBogusPattern { type Language = Language; - type Node = AnyCssSelectorPattern; - fn syntax_list(&self) -> &SyntaxList { - &self.syntax_list + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_BOGUS_PATTERN as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_BOGUS_PATTERN } - fn into_syntax_list(self) -> SyntaxList { - self.syntax_list + fn cast(syntax: SyntaxNode) -> Option { + 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 Debug for CssAnySelectorPatternList { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str("CssAnySelectorPatternList ")?; - f.debug_list().entries(self.iter()).finish() +impl std::fmt::Debug for CssBogusPattern { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssBogusPattern") + .field("items", &DebugSyntaxElementChildren(self.items())) + .finish() } } -impl IntoIterator for &CssAnySelectorPatternList { - type Item = AnyCssSelectorPattern; - type IntoIter = AstNodeListIterator; - fn into_iter(self) -> Self::IntoIter { - self.iter() +impl From for SyntaxNode { + fn from(n: CssBogusPattern) -> SyntaxNode { + n.syntax } } -impl IntoIterator for CssAnySelectorPatternList { - type Item = AnyCssSelectorPattern; - type IntoIter = AstNodeListIterator; - fn into_iter(self) -> Self::IntoIter { - self.iter() +impl From for SyntaxElement { + fn from(n: CssBogusPattern) -> SyntaxElement { + n.syntax.into() } } #[derive(Clone, Eq, PartialEq, Hash)] @@ -5731,7 +5702,7 @@ impl Serialize for CssSelectorList { } impl AstSeparatedList for CssSelectorList { type Language = Language; - type Node = CssSelector; + type Node = AnyCssSelectorPattern; fn syntax_list(&self) -> &SyntaxList { &self.syntax_list } @@ -5746,15 +5717,15 @@ impl Debug for CssSelectorList { } } impl IntoIterator for CssSelectorList { - type Item = SyntaxResult; - type IntoIter = AstSeparatedListNodesIterator; + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl IntoIterator for &CssSelectorList { - type Item = SyntaxResult; - type IntoIter = AstSeparatedListNodesIterator; + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } diff --git a/crates/biome_css_syntax/src/generated/nodes_mut.rs b/crates/biome_css_syntax/src/generated/nodes_mut.rs index 0fbd7331a386..8334dd4bc589 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -719,14 +719,6 @@ impl CssRule { ) } } -impl CssSelector { - pub fn with_pattern_list(self, element: CssAnySelectorPatternList) -> Self { - Self::unwrap_cast( - self.syntax - .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), - ) - } -} impl CssSimpleFunction { pub fn with_name(self, element: CssIdentifier) -> Self { Self::unwrap_cast( diff --git a/crates/biome_css_syntax/src/lib.rs b/crates/biome_css_syntax/src/lib.rs index 589b2a4fe0a9..836f01af06fd 100644 --- a/crates/biome_css_syntax/src/lib.rs +++ b/crates/biome_css_syntax/src/lib.rs @@ -9,7 +9,7 @@ pub use biome_rowan::{ pub use syntax_node::*; use crate::CssSyntaxKind::*; -use biome_rowan::RawSyntaxKind; +use biome_rowan::{AstNode, RawSyntaxKind}; impl From for CssSyntaxKind { fn from(d: u16) -> CssSyntaxKind { @@ -41,7 +41,7 @@ impl CssSyntaxKind { /// Returns `true` for contextual keywords (excluding strict mode contextual keywords) #[inline] pub const fn is_contextual_keyword(self) -> bool { - (self as u16) >= (ALICEBLUE_KW as u16) && (self as u16) <= (CSS_SELECTOR as u16) + (self as u16) >= (ALICEBLUE_KW as u16) && (self as u16) <= (VAR_KW as u16) } /// Returns true for all non-contextual keywords (includes future reserved keywords) @@ -60,7 +60,12 @@ impl biome_rowan::SyntaxKind for CssSyntaxKind { } fn to_bogus(&self) -> Self { - todo!() + dbg!(&self); + match self { + kind if AnyCssSelectorPattern::can_cast(*kind) => CSS_BOGUS_PATTERN, + + _ => CSS_BOGUS, + } } #[inline] diff --git a/crates/biome_rowan/src/ast/mod.rs b/crates/biome_rowan/src/ast/mod.rs index 5de85754a084..04375935095f 100644 --- a/crates/biome_rowan/src/ast/mod.rs +++ b/crates/biome_rowan/src/ast/mod.rs @@ -742,7 +742,7 @@ pub mod support { slot_index: usize, ) -> N { required_node(parent, slot_index) - .unwrap_or_else(|_| panic!("expected a list in slot {}", slot_index)) + .unwrap_or_else(|_| panic!("expected a list in slot {} of {:?}", slot_index, parent)) } pub fn token(parent: &SyntaxNode, slot_index: usize) -> Option> { diff --git a/crates/biome_test_utils/src/lib.rs b/crates/biome_test_utils/src/lib.rs index 98175120a8e6..ff3b23864068 100644 --- a/crates/biome_test_utils/src/lib.rs +++ b/crates/biome_test_utils/src/lib.rs @@ -145,7 +145,7 @@ pub fn parse_test_path(file: &Path) -> (&str, &str) { /// This check is used in the parser test to ensure it doesn't emit /// bogus nodes without diagnostics, and in the analyzer tests to /// check the syntax trees resulting from code actions are correct -pub fn has_bogus_nodes_or_empty_slots(node: &SyntaxNode) -> bool { +pub fn has_bogus_nodes_or_empty_slots(node: &SyntaxNode) -> bool { node.descendants().any(|descendant| { let kind = descendant.kind(); if kind.is_bogus() { diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 702936ec36b0..96573612b7e5 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -37,6 +37,8 @@ SyntaxElement = SyntaxElement CssBogus = SyntaxElement* +CssBogusPattern = SyntaxElement* +CssBogusBody = SyntaxElement* CssRoot = rules: CssRuleList @@ -58,14 +60,7 @@ CssRule = // .header, .app {} // ^^^^^^^^^^^^^ -CssSelectorList = (CssSelector (',' CssSelector)*) - - -CssSelector = - pattern_list: CssAnySelectorPatternList - - -CssAnySelectorPatternList = AnyCssSelectorPattern* +CssSelectorList = (AnyCssSelectorPattern (',' AnyCssSelectorPattern)*) AnyCssSelectorPattern = CssIdSelectorPattern @@ -75,6 +70,7 @@ AnyCssSelectorPattern = | CssPseudoClassSelectorPattern | CssTypeSelectorPattern | CssCombinatorSelectorPattern + | CssBogusPattern ///////////// /// SELECTORS diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index b25e75fe989b..703c57fff9d3 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -216,7 +216,6 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_ID_SELECTOR_PATTERN", "CSS_RULE", "CSS_SELECTOR_LIST", - "CSS_SELECTOR", "CSS_ANY_FUNCTION", "CSS_AT_KEYFRAMES", "CSS_AT_KEYFRAMES_BODY", @@ -265,5 +264,7 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_DECLARATION_IMPORTANT", // Bogs nodes "CSS_BOGUS", + "CSS_BOGUS_PATTERN", + "CSS_BOGUS_BODY", ], }; From aad5329c9e7dbb79ea095f8ecd3ce7479113674b Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Sun, 17 Sep 2023 18:16:52 +0100 Subject: [PATCH 2/2] chore: clippy and format --- crates/biome_css_parser/Cargo.toml | 2 +- crates/biome_css_syntax/src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/biome_css_parser/Cargo.toml b/crates/biome_css_parser/Cargo.toml index 31f3671c028a..2c2f58f63d21 100644 --- a/crates/biome_css_parser/Cargo.toml +++ b/crates/biome_css_parser/Cargo.toml @@ -21,11 +21,11 @@ biome_rowan = { workspace = true } tracing = { workspace = true } [dev-dependencies] +biome_test_utils = { workspace = true } insta = { workspace = true } quickcheck = { workspace = true } quickcheck_macros = { workspace = true } tests_macros = { workspace = true } -biome_test_utils = { workspace = true} # cargo-workspaces metadata [package.metadata.workspaces] diff --git a/crates/biome_css_syntax/src/lib.rs b/crates/biome_css_syntax/src/lib.rs index 836f01af06fd..552994f602af 100644 --- a/crates/biome_css_syntax/src/lib.rs +++ b/crates/biome_css_syntax/src/lib.rs @@ -60,7 +60,6 @@ impl biome_rowan::SyntaxKind for CssSyntaxKind { } fn to_bogus(&self) -> Self { - dbg!(&self); match self { kind if AnyCssSelectorPattern::can_cast(*kind) => CSS_BOGUS_PATTERN,