originals uses pointer equality check, rather than true equality

This commit is contained in:
Connor Skees 2021-07-24 14:42:21 -04:00
parent b44f81bd60
commit fccf93cd96
9 changed files with 92 additions and 76 deletions

View File

@ -116,10 +116,7 @@ pub(crate) fn selector_append(args: CallArgs, parser: &mut Parser<'_>) -> SassRe
} }
}]; }];
components.extend(complex.components.into_iter().skip(1)); components.extend(complex.components.into_iter().skip(1));
Ok(ComplexSelector { Ok(ComplexSelector::new(components, false))
components,
line_break: false,
})
} else { } else {
Err((format!("Can't append {} to {}.", complex, parent), span).into()) Err((format!("Can't append {} to {}.", complex, parent), span).into())
} }

View File

@ -1,12 +1,37 @@
use std::{ use std::{
collections::HashSet,
fmt::{self, Display, Write}, fmt::{self, Display, Write},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
sync::atomic::{AtomicU32, Ordering as AtomicOrdering},
}; };
use crate::error::SassResult; use crate::error::SassResult;
use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity}; use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
pub(crate) static COMPLEX_SELECTOR_UNIQUE_ID: AtomicU32 = AtomicU32::new(0);
#[derive(Clone, Debug)]
pub(crate) struct ComplexSelectorHashSet(HashSet<u32>);
impl ComplexSelectorHashSet {
pub fn new() -> Self {
Self(HashSet::new())
}
pub fn insert(&mut self, complex: &ComplexSelector) -> bool {
self.0.insert(complex.unique_id)
}
pub fn contains(&self, complex: &ComplexSelector) -> bool {
self.0.contains(&complex.unique_id)
}
pub fn extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>) {
self.0.extend(complexes.map(|complex| complex.unique_id));
}
}
/// A complex selector. /// A complex selector.
/// ///
/// A complex selector is composed of `CompoundSelector`s separated by /// A complex selector is composed of `CompoundSelector`s separated by
@ -27,6 +52,11 @@ pub(crate) struct ComplexSelector {
/// Whether a line break should be emitted *before* this selector. /// Whether a line break should be emitted *before* this selector.
pub line_break: bool, pub line_break: bool,
/// A unique identifier for this complex selector. Used to perform a pointer
/// equality check, like would be done for objects in a language like JavaScript
/// or dart
unique_id: u32,
} }
impl PartialEq for ComplexSelector { impl PartialEq for ComplexSelector {
@ -68,6 +98,14 @@ fn omit_spaces_around(component: &ComplexSelectorComponent) -> bool {
} }
impl ComplexSelector { impl ComplexSelector {
pub fn new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self {
Self {
components,
line_break,
unique_id: COMPLEX_SELECTOR_UNIQUE_ID.fetch_add(1, AtomicOrdering::Relaxed),
}
}
pub fn max_specificity(&self) -> i32 { pub fn max_specificity(&self) -> i32 {
self.specificity().min self.specificity().min
} }

View File

@ -155,12 +155,12 @@ impl CompoundSelector {
return Ok(Some(parent.components)); return Ok(Some(parent.components));
} }
} else { } else {
return Ok(Some(vec![ComplexSelector { return Ok(Some(vec![ComplexSelector::new(
components: vec![ComplexSelectorComponent::Compound(CompoundSelector { vec![ComplexSelectorComponent::Compound(CompoundSelector {
components: resolved_members, components: resolved_members,
})], })],
line_break: false, false,
}])); )]));
} }
let span = parent.span; let span = parent.span;
@ -201,10 +201,7 @@ impl CompoundSelector {
let mut components = complex.components; let mut components = complex.components;
components.push(ComplexSelectorComponent::Compound(last)); components.push(ComplexSelectorComponent::Compound(last));
Ok(ComplexSelector { Ok(ComplexSelector::new(components, complex.line_break))
components,
line_break: complex.line_break,
})
}) })
.collect::<SassResult<Vec<ComplexSelector>>>()?, .collect::<SassResult<Vec<ComplexSelector>>>()?,
)) ))

View File

@ -692,14 +692,8 @@ fn complex_is_parent_superselector(
complex_one.push(ComplexSelectorComponent::Compound(base.clone())); complex_one.push(ComplexSelectorComponent::Compound(base.clone()));
complex_two.push(ComplexSelectorComponent::Compound(base)); complex_two.push(ComplexSelectorComponent::Compound(base));
ComplexSelector { ComplexSelector::new(complex_one, false)
components: complex_one, .is_super_selector(&ComplexSelector::new(complex_two, false))
line_break: false,
}
.is_super_selector(&ComplexSelector {
components: complex_two,
line_break: false,
})
} }
/// Returns a list of all possible paths through the given lists. /// Returns a list of all possible paths through the given lists.

View File

@ -10,8 +10,8 @@ use indexmap::IndexMap;
use crate::error::SassResult; use crate::error::SassResult;
use super::{ use super::{
ComplexSelector, ComplexSelectorComponent, CompoundSelector, Pseudo, SelectorList, ComplexSelector, ComplexSelectorComponent, ComplexSelectorHashSet, CompoundSelector, Pseudo,
SimpleSelector, SelectorList, SimpleSelector,
}; };
pub(crate) use extended_selector::ExtendedSelector; pub(crate) use extended_selector::ExtendedSelector;
@ -99,7 +99,7 @@ pub(crate) struct Extender {
/// exist to satisfy the [first law of extend][]. /// exist to satisfy the [first law of extend][].
/// ///
/// [first law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184 /// [first law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184
originals: HashSet<ComplexSelector>, originals: ComplexSelectorHashSet,
/// The mode that controls this extender's behavior. /// The mode that controls this extender's behavior.
mode: ExtendMode, mode: ExtendMode,
@ -129,7 +129,7 @@ impl Extender {
extensions_by_extender: HashMap::new(), extensions_by_extender: HashMap::new(),
media_contexts: HashMap::new(), media_contexts: HashMap::new(),
source_specificity: HashMap::new(), source_specificity: HashMap::new(),
originals: HashSet::new(), originals: ComplexSelectorHashSet::new(),
mode: ExtendMode::Normal, mode: ExtendMode::Normal,
span, span,
} }
@ -188,9 +188,7 @@ impl Extender {
let mut extender = Extender::with_mode(mode, span); let mut extender = Extender::with_mode(mode, span);
if !selector.is_invisible() { if !selector.is_invisible() {
extender extender.originals.extend(selector.components.iter());
.originals
.extend(selector.components.iter().cloned());
} }
Ok(extender.extend_list(selector, Some(&extensions), &None)) Ok(extender.extend_list(selector, Some(&extensions), &None))
@ -288,10 +286,7 @@ impl Extender {
.into_iter() .into_iter()
.take(i) .take(i)
.map(|component| { .map(|component| {
vec![ComplexSelector { vec![ComplexSelector::new(vec![component], complex.line_break)]
components: vec![component],
line_break: complex.line_break,
}]
}) })
.collect(), .collect(),
); );
@ -302,19 +297,16 @@ impl Extender {
} }
} else { } else {
match extended_not_expanded.as_mut() { match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector { Some(v) => v.push(vec![ComplexSelector::new(
components: vec![ComplexSelectorComponent::Compound(component.clone())], vec![ComplexSelectorComponent::Compound(component.clone())],
line_break: false, false,
}]), )]),
None => {} None => {}
} }
} }
} else if component.is_combinator() { } else if component.is_combinator() {
match extended_not_expanded.as_mut() { match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector { Some(v) => v.push(vec![ComplexSelector::new(vec![component.clone()], false)]),
components: vec![component.clone()],
line_break: false,
}]),
None => {} None => {}
} }
} }
@ -336,17 +328,17 @@ impl Extender {
) )
.into_iter() .into_iter()
.map(|components| { .map(|components| {
let output_complex = ComplexSelector { let output_complex = ComplexSelector::new(
components, components,
line_break: complex_has_line_break complex_has_line_break
|| path.iter().any(|input_complex| input_complex.line_break), || path.iter().any(|input_complex| input_complex.line_break),
}; );
// Make sure that copies of `complex` retain their status as "original" // Make sure that copies of `complex` retain their status as "original"
// selectors. This includes selectors that are modified because a :not() // selectors. This includes selectors that are modified because a :not()
// was extended into. // was extended into.
if first && self.originals.contains(&complex.clone()) { if first && self.originals.contains(&complex) {
self.originals.insert(output_complex.clone()); self.originals.insert(&output_complex);
} }
first = false; first = false;
@ -517,10 +509,7 @@ impl Extender {
Some( Some(
complexes complexes
.into_iter() .into_iter()
.map(|components| ComplexSelector { .map(|components| ComplexSelector::new(components, line_break))
components,
line_break,
})
.collect::<Vec<ComplexSelector>>(), .collect::<Vec<ComplexSelector>>(),
) )
}); });
@ -742,12 +731,12 @@ impl Extender {
fn extension_for_simple(&self, simple: SimpleSelector) -> Extension { fn extension_for_simple(&self, simple: SimpleSelector) -> Extension {
let specificity = Some(*self.source_specificity.get(&simple).unwrap_or(&0_i32)); let specificity = Some(*self.source_specificity.get(&simple).unwrap_or(&0_i32));
Extension::one_off( Extension::one_off(
ComplexSelector { ComplexSelector::new(
components: vec![ComplexSelectorComponent::Compound(CompoundSelector { vec![ComplexSelectorComponent::Compound(CompoundSelector {
components: vec![simple], components: vec![simple],
})], })],
line_break: false, false,
}, ),
specificity, specificity,
true, true,
self.span, self.span,
@ -762,10 +751,7 @@ impl Extender {
}; };
let specificity = Some(self.source_specificity_for(&compound)); let specificity = Some(self.source_specificity_for(&compound));
Extension::one_off( Extension::one_off(
ComplexSelector { ComplexSelector::new(vec![ComplexSelectorComponent::Compound(compound)], false),
components: vec![ComplexSelectorComponent::Compound(compound)],
line_break: false,
},
specificity, specificity,
true, true,
self.span, self.span,
@ -883,7 +869,7 @@ impl Extender {
) -> ExtendedSelector { ) -> ExtendedSelector {
if !selector.is_invisible() { if !selector.is_invisible() {
for complex in selector.components.clone() { for complex in selector.components.clone() {
self.originals.insert(complex); self.originals.insert(&complex);
} }
} }

View File

@ -128,10 +128,7 @@ impl SelectorList {
unify_complex(vec![c1.components.clone(), c2.components]); unify_complex(vec![c1.components.clone(), c2.components]);
if let Some(u) = unified { if let Some(u) = unified {
u.into_iter() u.into_iter()
.map(|c| ComplexSelector { .map(|c| ComplexSelector::new(c, false))
components: c,
line_break: false,
})
.collect() .collect()
} else { } else {
Vec::new() Vec::new()
@ -193,10 +190,10 @@ impl SelectorList {
.map(move |parent_complex| { .map(move |parent_complex| {
let mut components = parent_complex.components; let mut components = parent_complex.components;
components.append(&mut complex.components.clone()); components.append(&mut complex.components.clone());
ComplexSelector { ComplexSelector::new(
components, components,
line_break: complex.line_break || parent_complex.line_break, complex.line_break || parent_complex.line_break,
} )
}) })
.collect()); .collect());
} }
@ -245,10 +242,7 @@ impl SelectorList {
.into_iter() .into_iter()
.map(|new_complex| { .map(|new_complex| {
i += 1; i += 1;
ComplexSelector { ComplexSelector::new(new_complex, line_breaks[i - 1])
components: new_complex,
line_break: line_breaks[i - 1],
}
}) })
.collect()) .collect())
}) })

View File

@ -182,10 +182,7 @@ impl<'a, 'b> SelectorParser<'a, 'b> {
return Err(("expected selector.", self.span).into()); return Err(("expected selector.", self.span).into());
} }
Ok(ComplexSelector { Ok(ComplexSelector::new(components, line_break))
components,
line_break,
})
} }
fn parse_compound_selector(&mut self) -> SassResult<CompoundSelector> { fn parse_compound_selector(&mut self) -> SassResult<CompoundSelector> {

View File

@ -502,10 +502,7 @@ impl Pseudo {
.any(move |complex1| { .any(move |complex1| {
let mut components = parents.clone().unwrap_or_default(); let mut components = parents.clone().unwrap_or_default();
components.push(ComplexSelectorComponent::Compound(compound.clone())); components.push(ComplexSelectorComponent::Compound(compound.clone()));
complex1.is_super_selector(&ComplexSelector { complex1.is_super_selector(&ComplexSelector::new(components, false))
components,
line_break: false,
})
}) })
} }
"has" | "host" | "host-context" => { "has" | "host" | "host-context" => {

View File

@ -1906,6 +1906,22 @@ test!(
}", }",
"a, b {\n color: a;\n}\n" "a, b {\n color: a;\n}\n"
); );
test!(
complex_selector_with_combinator_removed_by_complex_selector_without_combinator,
"c b {
@extend %d;
}
c > b {
@extend %d;
}
%d {
color: red;
}",
"c b {\n color: red;\n}\n"
);
error!( error!(
extend_optional_keyword_not_complete, extend_optional_keyword_not_complete,
"a { "a {