diff --git a/crates/compiler/src/lexer.rs b/crates/compiler/src/lexer.rs index 6d8fbda..0f8a52c 100644 --- a/crates/compiler/src/lexer.rs +++ b/crates/compiler/src/lexer.rs @@ -119,6 +119,7 @@ pub(crate) struct TokenLexer<'a> { cursor: u32, } +// todo: maybe char indices? impl<'a> TokenLexer<'a> { pub fn new(buf: Peekable>) -> TokenLexer<'a> { Self { buf, cursor: 0 } diff --git a/crates/compiler/src/selector/complex.rs b/crates/compiler/src/selector/complex.rs index dd8be40..801e86b 100644 --- a/crates/compiler/src/selector/complex.rs +++ b/crates/compiler/src/selector/complex.rs @@ -5,6 +5,8 @@ use std::{ sync::atomic::{AtomicU32, Ordering as AtomicOrdering}, }; +use codemap::Span; + use crate::error::SassResult; use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity}; @@ -323,10 +325,11 @@ impl ComplexSelectorComponent { pub fn resolve_parent_selectors( self, + span: Span, parent: SelectorList, ) -> SassResult>> { match self { - Self::Compound(c) => c.resolve_parent_selectors(parent), + Self::Compound(c) => c.resolve_parent_selectors(span, parent), Self::Combinator(..) => todo!(), } } diff --git a/crates/compiler/src/selector/compound.rs b/crates/compiler/src/selector/compound.rs index 3264fb1..d5e852b 100644 --- a/crates/compiler/src/selector/compound.rs +++ b/crates/compiler/src/selector/compound.rs @@ -1,5 +1,7 @@ use std::fmt::{self, Write}; +use codemap::Span; + use crate::error::SassResult; use super::{ @@ -106,6 +108,7 @@ impl CompoundSelector { /// Returns `None` if `compound` doesn't contain any `SimpleSelector::Parent`s. pub fn resolve_parent_selectors( self, + span: Span, parent: SelectorList, ) -> SassResult>> { let contains_selector_pseudo = self.components.iter().any(|simple| { @@ -163,7 +166,7 @@ impl CompoundSelector { )])); } - let span = parent.span; + let parent_span = parent.span; Ok(Some( parent @@ -185,7 +188,7 @@ impl CompoundSelector { if let Some(SimpleSelector::Parent(Some(suffix))) = self.components.first() { let mut end = components.pop().unwrap(); - end.add_suffix(suffix, span)?; + end.add_suffix(suffix, parent_span)?; components.push(end); } diff --git a/crates/compiler/src/selector/list.rs b/crates/compiler/src/selector/list.rs index de893ba..ff59b35 100644 --- a/crates/compiler/src/selector/list.rs +++ b/crates/compiler/src/selector/list.rs @@ -206,7 +206,7 @@ impl SelectorList { if component.is_compound() { let resolved = match component .clone() - .resolve_parent_selectors(parent.clone())? + .resolve_parent_selectors(self.span, parent.clone())? { Some(r) => r, None => { diff --git a/crates/lib/tests/is-superselector.rs b/crates/lib/tests/is-superselector.rs index 6da02d5..4ca8d79 100644 --- a/crates/lib/tests/is-superselector.rs +++ b/crates/lib/tests/is-superselector.rs @@ -469,6 +469,11 @@ test!( "a {\n color: is-superselector(\"::-pfx-slotted(c d, e f)\", \"::-pfx-slotted(c d, e f)\");\n}\n", "a {\n color: true;\n}\n" ); +test!( + other_complex_ends_in_combinator, + "a {\n color: is-superselector(\"a > b\", \"a >\");\n}\n", + "a {\n color: false;\n}\n" +); // todo: /spec/core_functions/selector/is_superselector/simple/pseudo/selector_arg/ // :not, :matches, :nth-child, :nth-last-child diff --git a/crates/lib/tests/list.rs b/crates/lib/tests/list.rs index 7c5a9e1..c68a358 100644 --- a/crates/lib/tests/list.rs +++ b/crates/lib/tests/list.rs @@ -478,6 +478,22 @@ test!( }", "a {\n color: true;\n}\n" ); +test!( + list_separator_slash, + "@use 'sass:list'; + a { + color: list-separator(list.slash(a, b)); + }", + "a {\n color: slash;\n}\n" +); +test!( + list_slash, + "@use 'sass:list'; + a { + color: list.slash(a, b, c); + }", + "a {\n color: a / b / c;\n}\n" +); error!( nth_list_index_0, "a {\n color: nth(a b c, 0);\n}\n", "Error: $n: List index may not be 0." diff --git a/crates/lib/tests/plain-css.rs b/crates/lib/tests/plain-css.rs index c3ad180..069def7 100644 --- a/crates/lib/tests/plain-css.rs +++ b/crates/lib/tests/plain-css.rs @@ -195,6 +195,14 @@ error!( "Error: Interpolation isn't allowed in plain CSS.", grass::Options::default().input_syntax(InputSyntax::Css) ); +error!( + disallows_placeholder_selector, + "%a { + color: red; + }", + "Error: Placeholder selectors aren't allowed here.", + grass::Options::default().input_syntax(InputSyntax::Css) +); test!( allows_rgb_function, "a { diff --git a/crates/lib/tests/selector-append.rs b/crates/lib/tests/selector-append.rs index 2a48e70..6594cb5 100644 --- a/crates/lib/tests/selector-append.rs +++ b/crates/lib/tests/selector-append.rs @@ -74,3 +74,7 @@ error!( "a {\n color: selector-append();\n}\n", "Error: $selectors: At least one selector must be passed." ); +error!( + append_two_type_selectors_with_namespace, + "a {\n color: selector-append(\"a|a\", \"a|a\");\n}\n", "Error: Can't append a|a to a|a." +); diff --git a/crates/lib/tests/selector-nest.rs b/crates/lib/tests/selector-nest.rs index 470c61e..5353643 100644 --- a/crates/lib/tests/selector-nest.rs +++ b/crates/lib/tests/selector-nest.rs @@ -131,6 +131,16 @@ test!( "a {\n color: selector-nest(\"c\", (d, e f));\n}\n", "a {\n color: c d, c e f;\n}\n" ); +test!( + double_nested_parent_selector_inside_psuedo_class, + "a {\n color: selector-nest(\"a\", \":matches(&:not(&))\");\n}\n", + "a {\n color: :matches(a:not(a));\n}\n" +); +test!( + double_nested_parent_selector_inside_psuedo_class_as_part_of_complex, + "a {\n color: selector-nest(\"a\", \":not(&):matches(:not(a))\");\n}\n", + "a {\n color: :not(a):matches(:not(a));\n}\n" +); error!( #[ignore = "https://github.com/sass/dart-sass/issues/966"] disallows_parent_selector_as_first_arg, @@ -169,3 +179,8 @@ error!( "a {\n color: selector-nest();\n}\n", "Error: $selectors: At least one selector must be passed." ); +error!( + parent_ends_with_combinator_and_child_has_parent_selector_as_pseudo_element, + "a {\n color: selector-nest(\"a >\", \":matches(&c)\");\n}\n", + "Error: Parent \"a >\" is incompatible with this selector." +); diff --git a/crates/lib/tests/selector-parse.rs b/crates/lib/tests/selector-parse.rs index 4d8cc22..a410bf8 100644 --- a/crates/lib/tests/selector-parse.rs +++ b/crates/lib/tests/selector-parse.rs @@ -101,6 +101,11 @@ test!( "a {\n color: selector-parse(\"b c, d e, f g\");\n}\n", "a {\n color: b c, d e, f g;\n}\n" ); +test!( + not_pseudo_invisible, + "a {\n color: selector-parse(\":not(%a)\");\n}\n", + "a {\n color: *;\n}\n" +); error!( invalid_selector, "a {\n color: selector-parse(\"!!!!!!!!\");\n}\n", "Error: $selector: expected selector." diff --git a/crates/lib/tests/selector-unify.rs b/crates/lib/tests/selector-unify.rs index c7062c6..02f89b8 100644 --- a/crates/lib/tests/selector-unify.rs +++ b/crates/lib/tests/selector-unify.rs @@ -678,3 +678,13 @@ test!( "a {\n color: selector-unify(\":is(.c)\", \":is(.d)\");\n}\n", "a {\n color: :is(.c):is(.d);\n}\n" ); +test!( + universal_and_class, + "a {\n color: selector-unify(\"*\", \".a\");\n}\n", + "a {\n color: .a;\n}\n" +); +test!( + universal_and_psuedo, + "a {\n color: selector-unify(\"*\", \":a\");\n}\n", + "a {\n color: :a;\n}\n" +); diff --git a/crates/lib/tests/selectors.rs b/crates/lib/tests/selectors.rs index 40fcdbf..7bb84fb 100644 --- a/crates/lib/tests/selectors.rs +++ b/crates/lib/tests/selectors.rs @@ -971,9 +971,17 @@ error!( ":nth-child(even#) {\n color: &;\n}\n", "Error: expected \")\"." ); error!( - a_n_plus_b_n_nothing_after_plus, + a_n_plus_b_n_double_nothing_after_plus, ":nth-child:nth-child(n+{}", "Error: Expected a number." ); +error!( + a_n_plus_b_n_nothing_after_plus, + ":nth-child(n+{}", "Error: Expected a number." +); +error!( + a_n_plus_b_n_non_numeric_after_plus, + ":nth-child(n+b) {}", "Error: Expected a number." +); error!(nothing_after_period, ". {}", "Error: Expected identifier."); error!(nothing_after_hash, "# {}", "Error: Expected identifier."); error!(nothing_after_percent, "% {}", "Error: Expected identifier.");