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));
Ok(ComplexSelector {
components,
line_break: false,
})
Ok(ComplexSelector::new(components, false))
} else {
Err((format!("Can't append {} to {}.", complex, parent), span).into())
}

View File

@ -1,12 +1,37 @@
use std::{
collections::HashSet,
fmt::{self, Display, Write},
hash::{Hash, Hasher},
sync::atomic::{AtomicU32, Ordering as AtomicOrdering},
};
use crate::error::SassResult;
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 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.
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 {
@ -68,6 +98,14 @@ fn omit_spaces_around(component: &ComplexSelectorComponent) -> bool {
}
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 {
self.specificity().min
}

View File

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

View File

@ -692,14 +692,8 @@ fn complex_is_parent_superselector(
complex_one.push(ComplexSelectorComponent::Compound(base.clone()));
complex_two.push(ComplexSelectorComponent::Compound(base));
ComplexSelector {
components: complex_one,
line_break: false,
}
.is_super_selector(&ComplexSelector {
components: complex_two,
line_break: false,
})
ComplexSelector::new(complex_one, false)
.is_super_selector(&ComplexSelector::new(complex_two, false))
}
/// 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 super::{
ComplexSelector, ComplexSelectorComponent, CompoundSelector, Pseudo, SelectorList,
SimpleSelector,
ComplexSelector, ComplexSelectorComponent, ComplexSelectorHashSet, CompoundSelector, Pseudo,
SelectorList, SimpleSelector,
};
pub(crate) use extended_selector::ExtendedSelector;
@ -99,7 +99,7 @@ pub(crate) struct Extender {
/// exist to satisfy the [first law of extend][].
///
/// [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.
mode: ExtendMode,
@ -129,7 +129,7 @@ impl Extender {
extensions_by_extender: HashMap::new(),
media_contexts: HashMap::new(),
source_specificity: HashMap::new(),
originals: HashSet::new(),
originals: ComplexSelectorHashSet::new(),
mode: ExtendMode::Normal,
span,
}
@ -188,9 +188,7 @@ impl Extender {
let mut extender = Extender::with_mode(mode, span);
if !selector.is_invisible() {
extender
.originals
.extend(selector.components.iter().cloned());
extender.originals.extend(selector.components.iter());
}
Ok(extender.extend_list(selector, Some(&extensions), &None))
@ -288,10 +286,7 @@ impl Extender {
.into_iter()
.take(i)
.map(|component| {
vec![ComplexSelector {
components: vec![component],
line_break: complex.line_break,
}]
vec![ComplexSelector::new(vec![component], complex.line_break)]
})
.collect(),
);
@ -302,19 +297,16 @@ impl Extender {
}
} else {
match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector {
components: vec![ComplexSelectorComponent::Compound(component.clone())],
line_break: false,
}]),
Some(v) => v.push(vec![ComplexSelector::new(
vec![ComplexSelectorComponent::Compound(component.clone())],
false,
)]),
None => {}
}
}
} else if component.is_combinator() {
match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector {
components: vec![component.clone()],
line_break: false,
}]),
Some(v) => v.push(vec![ComplexSelector::new(vec![component.clone()], false)]),
None => {}
}
}
@ -336,17 +328,17 @@ impl Extender {
)
.into_iter()
.map(|components| {
let output_complex = ComplexSelector {
let output_complex = ComplexSelector::new(
components,
line_break: complex_has_line_break
complex_has_line_break
|| path.iter().any(|input_complex| input_complex.line_break),
};
);
// Make sure that copies of `complex` retain their status as "original"
// selectors. This includes selectors that are modified because a :not()
// was extended into.
if first && self.originals.contains(&complex.clone()) {
self.originals.insert(output_complex.clone());
if first && self.originals.contains(&complex) {
self.originals.insert(&output_complex);
}
first = false;
@ -517,10 +509,7 @@ impl Extender {
Some(
complexes
.into_iter()
.map(|components| ComplexSelector {
components,
line_break,
})
.map(|components| ComplexSelector::new(components, line_break))
.collect::<Vec<ComplexSelector>>(),
)
});
@ -742,12 +731,12 @@ impl Extender {
fn extension_for_simple(&self, simple: SimpleSelector) -> Extension {
let specificity = Some(*self.source_specificity.get(&simple).unwrap_or(&0_i32));
Extension::one_off(
ComplexSelector {
components: vec![ComplexSelectorComponent::Compound(CompoundSelector {
ComplexSelector::new(
vec![ComplexSelectorComponent::Compound(CompoundSelector {
components: vec![simple],
})],
line_break: false,
},
false,
),
specificity,
true,
self.span,
@ -762,10 +751,7 @@ impl Extender {
};
let specificity = Some(self.source_specificity_for(&compound));
Extension::one_off(
ComplexSelector {
components: vec![ComplexSelectorComponent::Compound(compound)],
line_break: false,
},
ComplexSelector::new(vec![ComplexSelectorComponent::Compound(compound)], false),
specificity,
true,
self.span,
@ -883,7 +869,7 @@ impl Extender {
) -> ExtendedSelector {
if !selector.is_invisible() {
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]);
if let Some(u) = unified {
u.into_iter()
.map(|c| ComplexSelector {
components: c,
line_break: false,
})
.map(|c| ComplexSelector::new(c, false))
.collect()
} else {
Vec::new()
@ -193,10 +190,10 @@ impl SelectorList {
.map(move |parent_complex| {
let mut components = parent_complex.components;
components.append(&mut complex.components.clone());
ComplexSelector {
ComplexSelector::new(
components,
line_break: complex.line_break || parent_complex.line_break,
}
complex.line_break || parent_complex.line_break,
)
})
.collect());
}
@ -245,10 +242,7 @@ impl SelectorList {
.into_iter()
.map(|new_complex| {
i += 1;
ComplexSelector {
components: new_complex,
line_break: line_breaks[i - 1],
}
ComplexSelector::new(new_complex, line_breaks[i - 1])
})
.collect())
})

View File

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

View File

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

View File

@ -1906,6 +1906,22 @@ test!(
}",
"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!(
extend_optional_keyword_not_complete,
"a {