diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index c9d9a49a8..ae9cc2621 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -13,7 +13,8 @@ permissions: jobs: codspeed: name: ${{ matrix.package }} / ${{ matrix.bench }} - runs-on: ubuntu-latest + # The pinned CodSpeed simulation runner does not support Ubuntu 26.04. + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: diff --git a/c/src/lib.rs b/c/src/lib.rs index 5f1279fd9..d6aa3cfc0 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -30,12 +30,12 @@ impl<'i> CssError<'i> { } let string: String = match &self.kind { - ErrorKind::ParserError(err) => err.to_string().into(), - ErrorKind::MinifyError(err) => err.to_string().into(), - ErrorKind::PrinterError(err) => err.to_string().into(), - ErrorKind::PatternParseError(err) => err.to_string().into(), - ErrorKind::BrowserslistError(err) => err.to_string().into(), - ErrorKind::SourceMapError(err) => err.to_string().into(), + ErrorKind::ParserError(err) => err.to_string(), + ErrorKind::MinifyError(err) => err.to_string(), + ErrorKind::PrinterError(err) => err.to_string(), + ErrorKind::PatternParseError(err) => err.to_string(), + ErrorKind::BrowserslistError(err) => err.to_string(), + ErrorKind::SourceMapError(err) => err.to_string(), }; self.message = Some(CString::new(string).unwrap()); @@ -98,8 +98,8 @@ pub struct Targets { samsung: u32, } -impl Into for Targets { - fn into(self) -> Browsers { +impl From for Browsers { + fn from(val: Targets) -> Self { macro_rules! browser { ($val: expr) => { if $val > 0 { @@ -111,15 +111,15 @@ impl Into for Targets { } Browsers { - android: browser!(self.android), - chrome: browser!(self.chrome), - edge: browser!(self.edge), - firefox: browser!(self.firefox), - ie: browser!(self.ie), - ios_saf: browser!(self.ios_saf), - opera: browser!(self.opera), - safari: browser!(self.safari), - samsung: browser!(self.samsung), + android: browser!(val.android), + chrome: browser!(val.chrome), + edge: browser!(val.edge), + firefox: browser!(val.firefox), + ie: browser!(val.ie), + ios_saf: browser!(val.ios_saf), + opera: browser!(val.opera), + safari: browser!(val.safari), + samsung: browser!(val.samsung), } } } @@ -172,18 +172,18 @@ pub struct TransformOptions { unused_symbols_len: usize, } -impl Into for TransformOptions { - fn into(self) -> MinifyOptions { +impl From for MinifyOptions { + fn from(val: TransformOptions) -> Self { let mut unused_symbols = HashSet::new(); - let slice = unsafe { std::slice::from_raw_parts(self.unused_symbols, self.unused_symbols_len) }; + let slice = unsafe { std::slice::from_raw_parts(val.unused_symbols, val.unused_symbols_len) }; for symbol in slice { let string = unsafe { std::str::from_utf8_unchecked(CStr::from_ptr(*symbol).to_bytes()).to_owned() }; unused_symbols.insert(string); } MinifyOptions { - targets: if self.targets != Targets::default() { - Some(self.targets.into()).into() + targets: if val.targets != Targets::default() { + Some(val.targets.into()).into() } else { Default::default() }, @@ -226,8 +226,8 @@ impl Default for PseudoClasses { } } -impl<'a> Into> for PseudoClasses { - fn into(self) -> lightningcss::printer::PseudoClasses<'a> { +impl<'a> From for lightningcss::printer::PseudoClasses<'a> { + fn from(val: PseudoClasses) -> Self { macro_rules! pc { ($ptr: expr) => { if $ptr.is_null() { @@ -239,11 +239,11 @@ impl<'a> Into> for PseudoClasses { } lightningcss::printer::PseudoClasses { - hover: pc!(self.hover), - active: pc!(self.active), - focus: pc!(self.focus), - focus_visible: pc!(self.focus_visible), - focus_within: pc!(self.focus_within), + hover: pc!(val.hover), + active: pc!(val.active), + focus: pc!(val.focus), + focus_visible: pc!(val.focus_visible), + focus_within: pc!(val.focus_within), } } } diff --git a/derive/src/parse.rs b/derive/src/parse.rs index 995b344e3..51d369d10 100644 --- a/derive/src/parse.rs +++ b/derive/src/parse.rs @@ -26,7 +26,7 @@ pub fn derive_parse(input: TokenStream) -> TokenStream { let (impl_generics, _, where_clause) = generics.split_for_impl(); let imp = match &data { - Data::Enum(data) => derive_enum(&data, &ident, &opts), + Data::Enum(data) => derive_enum(data, &ident, &opts), _ => todo!(), }; @@ -171,7 +171,7 @@ fn derive_enum(data: &DataEnum, ident: &Ident, opts: &CssOptions) -> TokenStream #idents }; - output.into() + output } pub struct CssOptions { @@ -201,10 +201,8 @@ impl Parse for CssOptions { let _: Token![=] = input.parse()?; let v: Ident = input.parse()?; - if k == "case" { - if v == "lower" { - case = convert_case::Case::Flat; - } + if k == "case" && v == "lower" { + case = convert_case::Case::Flat; } } diff --git a/derive/src/visit.rs b/derive/src/visit.rs index 020933644..362ed51c4 100644 --- a/derive/src/visit.rs +++ b/derive/src/visit.rs @@ -68,7 +68,7 @@ fn derive( impl_generics.params.clear(); generic } else { - &generics + generics }; if impl_generics.lifetimes().next().is_none() { @@ -249,7 +249,7 @@ fn derive( output.into() } -fn skip_type(attrs: &Vec) -> bool { +fn skip_type(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| attr.path.is_ident("skip_type")) } diff --git a/examples/custom_at_rule.rs b/examples/custom_at_rule.rs index 1a8b5ead3..5cfd0b919 100644 --- a/examples/custom_at_rule.rs +++ b/examples/custom_at_rule.rs @@ -110,7 +110,7 @@ impl<'i> AtRuleParser<'i> for TailwindAtRuleParser { "tailwind" => { let location = input.current_source_location(); let ident = input.expect_ident()?; - let directive = match_ignore_ascii_case! { &*ident, + let directive = match_ignore_ascii_case! { ident, "base" => TailwindDirective::Base, "components" => TailwindDirective::Components, "utilities" => TailwindDirective::Utilities, @@ -164,23 +164,17 @@ impl<'i, 'a> Visitor<'i, AtRule> for StyleRuleCollector<'i, 'a> { } fn visit_rule(&mut self, rule: &mut rspack_lightningcss::rules::CssRule<'i, AtRule>) -> Result<(), Self::Error> { - match rule { - CssRule::Style(rule) => { - for selector in rule.selectors.0.iter() { - if selector.len() != 1 { - continue; // TODO - } - for component in selector.iter_raw_match_order() { - match component { - Component::Class(name) => { - self.rules.insert(name.0.to_string(), rule.declarations.clone()); - } - _ => {} - } + if let CssRule::Style(rule) = rule { + for selector in rule.selectors.0.iter() { + if selector.len() != 1 { + continue; // TODO + } + for component in selector.iter_raw_match_order() { + if let Component::Class(name) = component { + self.rules.insert(name.0.to_string(), rule.declarations.clone()); } } } - _ => {} } rule.visit_children(self) @@ -241,9 +235,8 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { &mut self, length: &mut rspack_lightningcss::values::length::LengthValue, ) -> Result<(), Self::Error> { - match length { - LengthValue::Px(px) => *length = LengthValue::Rem(*px / 16.0), - _ => {} + if let LengthValue::Px(px) = length { + *length = LengthValue::Rem(*px / 16.0) } Ok(()) @@ -259,11 +252,8 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { fn visit_selector(&mut self, selector: &mut Selector<'i>) -> Result<(), Self::Error> { for c in selector.iter_mut_raw_match_order() { - match c { - Component::Class(c) => { - *c = format!("tw-{}", c).into(); - } - _ => {} + if let Component::Class(c) = c { + *c = format!("tw-{}", c).into(); } } @@ -272,14 +262,15 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> { fn visit_token(&mut self, token: &mut TokenOrValue<'i>) -> Result<(), Self::Error> { match token { - TokenOrValue::Function(f) if f.name == "theme" => match f.arguments.0.first() { - Some(TokenOrValue::Token(Token::String(s))) => match s.as_ref() { - "blue-500" => *token = TokenOrValue::Color(CssColor::RGBA(RGBA::new(0, 0, 255, 1.0))), - "red-500" => *token = TokenOrValue::Color(CssColor::RGBA(RGBA::new(255, 0, 0, 1.0))), - _ => {} - }, - _ => {} - }, + TokenOrValue::Function(f) if f.name == "theme" => { + if let Some(TokenOrValue::Token(Token::String(s))) = f.arguments.0.first() { + match s.as_ref() { + "blue-500" => *token = TokenOrValue::Color(CssColor::RGBA(RGBA::new(0, 0, 255, 1.0))), + "red-500" => *token = TokenOrValue::Color(CssColor::RGBA(RGBA::new(255, 0, 0, 1.0))), + _ => {} + } + } + } _ => {} } diff --git a/napi/src/lib.rs b/napi/src/lib.rs index 0692f7502..d9f9f9133 100644 --- a/napi/src/lib.rs +++ b/napi/src/lib.rs @@ -122,9 +122,9 @@ mod bundle { use super::*; use crossbeam_channel::{self, Receiver, Sender}; use lightningcss::bundler::{FileProvider, ResolveResult}; - use napi::{Env, JsBoolean, JsFunction, JsString, NapiRaw}; - use std::path::{Path, PathBuf}; - use std::str::FromStr; + use napi::{Env, JsFunction, JsString, NapiRaw}; + use std::path::Path; + use std::sync::Mutex; use threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode}; @@ -649,14 +649,14 @@ struct OwnedPseudoClasses { pub focus_within: Option, } -impl<'a> Into> for &'a OwnedPseudoClasses { - fn into(self) -> PseudoClasses<'a> { +impl<'a> From<&'a OwnedPseudoClasses> for PseudoClasses<'a> { + fn from(val: &'a OwnedPseudoClasses) -> Self { PseudoClasses { - hover: self.hover.as_deref(), - active: self.active.as_deref(), - focus: self.focus.as_deref(), - focus_visible: self.focus_visible.as_deref(), - focus_within: self.focus_within.as_deref(), + hover: val.hover.as_deref(), + active: val.active.as_deref(), + focus: val.focus.as_deref(), + focus_visible: val.focus_visible.as_deref(), + focus_within: val.focus_within.as_deref(), } } } @@ -710,7 +710,7 @@ fn compile<'i>( ); let mut stylesheet = StyleSheet::parse_with( - &code, + code, ParserOptions { filename: filename.clone(), flags, @@ -1000,7 +1000,7 @@ fn compile_attr<'i>( let res = { let filename = config.filename.clone().unwrap_or_default(); let mut attr = StyleAttribute::parse( - &code, + code, ParserOptions { filename, error_recovery: config.error_recovery, @@ -1070,7 +1070,7 @@ impl<'i, E: std::error::Error> std::fmt::Display for CompileError<'i, E> { CompileError::PrinterError(err) => err.kind.fmt(f), CompileError::BundleError(err) => err.kind.fmt(f), CompileError::PatternError(err) => err.fmt(f), - CompileError::SourceMapError(err) => write!(f, "{}", err.to_string()), // TODO: switch to `fmt::Display` once parcel_sourcemap supports this + CompileError::SourceMapError(err) => write!(f, "{}", err), // TODO: switch to `fmt::Display` once parcel_sourcemap supports this #[cfg(feature = "visitor")] CompileError::JsError(err) => std::fmt::Debug::fmt(&err, f), } diff --git a/napi/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs index 45ac6ec13..e03744e0f 100644 --- a/napi/src/threadsafe_function.rs +++ b/napi/src/threadsafe_function.rs @@ -39,53 +39,11 @@ impl From for sys::napi_threadsafe_function_call_mod /// Communicate with the addon's main thread by invoking a JavaScript function from other threads. /// -/// ## Example -/// An example of using `ThreadsafeFunction`: -/// -/// ```rust -/// #[macro_use] -/// extern crate napi_derive; -/// -/// use std::thread; -/// -/// use napi::{ -/// threadsafe_function::{ -/// ThreadSafeCallContext, ThreadsafeFunctionCallMode, ThreadsafeFunctionReleaseMode, -/// }, -/// CallContext, Error, JsFunction, JsNumber, JsUndefined, Result, Status, -/// }; -/// -/// #[js_function(1)] -/// pub fn test_threadsafe_function(ctx: CallContext) -> Result { -/// let func = ctx.get::(0)?; -/// -/// let tsfn = -/// ctx -/// .env -/// .create_threadsafe_function(&func, 0, |ctx: ThreadSafeCallContext>| { -/// ctx.value -/// .iter() -/// .map(|v| ctx.env.create_uint32(*v)) -/// .collect::>>() -/// })?; -/// -/// let tsfn_cloned = tsfn.clone(); -/// -/// thread::spawn(move || { -/// let output: Vec = vec![0, 1, 2, 3]; -/// // It's okay to call a threadsafe function multiple times. -/// tsfn.call(Ok(output.clone()), ThreadsafeFunctionCallMode::Blocking); -/// }); -/// -/// thread::spawn(move || { -/// let output: Vec = vec![3, 2, 1, 0]; -/// // It's okay to call a threadsafe function multiple times. -/// tsfn_cloned.call(Ok(output.clone()), ThreadsafeFunctionCallMode::NonBlocking); -/// }); -/// -/// ctx.env.get_undefined() -/// } -/// ``` +/// This internal helper is created with a raw Node environment and function, +/// a queue size, and a callback that returns `Result<()>`. The callback runs on +/// the main thread with a `ThreadSafeCallContext`, and handles JavaScript +/// conversion itself. Worker threads can clone the handle and pass a value of +/// type `T` directly to `call` with the desired blocking mode. pub struct ThreadsafeFunction { raw_tsfn: sys::napi_threadsafe_function, aborted: Arc, diff --git a/napi/src/transformer.rs b/napi/src/transformer.rs index b947cc3b8..f446458dd 100644 --- a/napi/src/transformer.rs +++ b/napi/src/transformer.rs @@ -628,9 +628,8 @@ fn call_visitor>( let js_value = env.to_js_value(value)?; let res = visit.call(None, &[js_value])?; let new_value: Option = env.from_js_value(res).map(serde_detach::detach)?; - match new_value { - Some(new_value) => *value = new_value, - None => {} + if let Some(new_value) = new_value { + *value = new_value } Ok(()) diff --git a/node/Cargo.toml b/node/Cargo.toml index e7770f612..744d010d5 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" publish = false [lib] +# The addon links against Node.js at runtime and has no Rust unit tests. +test = false crate-type = ["cdylib"] [dependencies] diff --git a/node/src/lib.rs b/node/src/lib.rs index 822944124..56df49d6a 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -16,13 +16,13 @@ fn transform_style_attribute(ctx: CallContext) -> napi::Result { } #[js_function(1)] -pub fn bundle(ctx: CallContext) -> napi::Result { +fn bundle(ctx: CallContext) -> napi::Result { lightningcss_napi::bundle(ctx) } #[cfg(not(target_arch = "wasm32"))] #[js_function(1)] -pub fn bundle_async(ctx: CallContext) -> napi::Result { +fn bundle_async(ctx: CallContext) -> napi::Result { lightningcss_napi::bundle_async(ctx) } diff --git a/selectors/attr.rs b/selectors/attr.rs index 0112b0ffa..9b1eb8f9c 100644 --- a/selectors/attr.rs +++ b/selectors/attr.rs @@ -174,23 +174,19 @@ pub static SELECTOR_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r', '\x0C']; )] #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] +#[derive(Default)] pub enum ParsedCaseSensitivity { // 's' was specified. ExplicitCaseSensitive, // 'i' was specified. AsciiCaseInsensitive, // No flags were specified and HTML says this is a case-sensitive attribute. + #[default] CaseSensitive, // No flags were specified and HTML says this is a case-insensitive attribute. AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument, } -impl Default for ParsedCaseSensitivity { - fn default() -> Self { - ParsedCaseSensitivity::CaseSensitive - } -} - impl ParsedCaseSensitivity { pub fn to_unconditional(self, is_html_element_in_html_document: bool) -> CaseSensitivity { match self { diff --git a/selectors/bloom.rs b/selectors/bloom.rs index e9d2f7307..91971fa4b 100644 --- a/selectors/bloom.rs +++ b/selectors/bloom.rs @@ -285,7 +285,6 @@ fn hash2(hash: u32) -> u32 { fn create_and_insert_some_stuff() { use rustc_hash::FxHasher; use std::hash::{Hash, Hasher}; - use std::mem::transmute; fn hash_as_str(i: usize) -> u32 { let mut hasher = FxHasher::default(); @@ -299,9 +298,7 @@ fn create_and_insert_some_stuff() { // Statically assert that ARRAY_SIZE is a multiple of 8, which // BloomStorageBool relies on. - unsafe { - transmute::<[u8; ARRAY_SIZE % 8], [u8; 0]>([]); - } + const _: [(); 0] = [(); ARRAY_SIZE % 8]; for i in 0_usize..1000 { bf.insert_hash(hash_as_str(i)); @@ -407,11 +404,15 @@ mod bench { #[bench] fn remove_10(b: &mut test::Bencher) { let mut bf = BloomFilter::new(); - let mut gen = HashGenerator::default(); - // Note: this will underflow, and that's ok. + // Balance each removal with an insertion, including in debug test runs. b.iter(|| { - for _ in 0..10 { - bf.remove_hash(gen.next()) + let mut gen = HashGenerator::default(); + let hashes = std::array::from_fn::<_, 10, _>(|_| gen.next()); + for hash in hashes { + bf.insert_hash(hash); + } + for hash in hashes { + bf.remove_hash(hash); } }); } diff --git a/selectors/builder.rs b/selectors/builder.rs index 28158754b..d39ab880e 100644 --- a/selectors/builder.rs +++ b/selectors/builder.rs @@ -371,7 +371,7 @@ where let mut specificity = Default::default(); for simple_selector in iter { - simple_selector_specificity(&simple_selector, &mut specificity); + simple_selector_specificity(simple_selector, &mut specificity); } specificity } diff --git a/selectors/lib.rs b/selectors/lib.rs index 2047b4e61..c6db038ab 100644 --- a/selectors/lib.rs +++ b/selectors/lib.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // Make |cargo bench| work. -#![cfg_attr(feature = "bench", feature(test))] +#![cfg_attr(all(feature = "bench", test), feature(test))] #[macro_use] extern crate bitflags; @@ -23,7 +23,7 @@ pub mod sink; mod tree; pub mod visitor; -#[cfg(all(feature = "serde"))] +#[cfg(feature = "serde")] mod serialization; pub use crate::nth_index_cache::NthIndexCache; diff --git a/selectors/matching.rs b/selectors/matching.rs index 61f74a85c..e46635c89 100644 --- a/selectors/matching.rs +++ b/selectors/matching.rs @@ -399,9 +399,8 @@ where match combinator { Combinator::NextSibling | Combinator::LaterSibling => element.prev_sibling_element(), Combinator::Child | Combinator::Descendant | Combinator::Deep | Combinator::DeepDescendant => { - match element.parent_element() { - Some(e) => return Some(e), - None => {} + if let Some(e) = element.parent_element() { + return Some(e); } if !element.parent_node_is_shadow_root() { @@ -432,7 +431,7 @@ where } Combinator::Part => element.containing_shadow_host(), Combinator::SlotAssignment => { - debug_assert!(element.assigned_slot().map_or(true, |s| s.is_html_slot_element())); + debug_assert!(element.assigned_slot().is_none_or(|s| s.is_html_slot_element())); let scope = context.current_host?; let mut current_slot = element.assigned_slot()?; while current_slot.containing_shadow_host().unwrap().opaque() != scope { @@ -461,7 +460,7 @@ where matches_compound_selector(&mut selector_iter, element, context, flags_setter, rightmost); let combinator = selector_iter.next_sequence(); - if combinator.map_or(false, |c| c.is_sibling()) { + if combinator.is_some_and(|c| c.is_sibling()) { flags_setter(element, ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS); } @@ -487,7 +486,7 @@ where | Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally, }; - let mut next_element = next_element_for_combinator(element, combinator, &selector_iter, &context); + let mut next_element = next_element_for_combinator(element, combinator, &selector_iter, context); // Stop matching :visited as soon as we find a link, or a combinator for // something that isn't an ancestor. @@ -542,7 +541,7 @@ where visited_handling = VisitedHandlingMode::AllLinksUnvisited; } - next_element = next_element_for_combinator(&element, combinator, &selector_iter, &context); + next_element = next_element_for_combinator(&element, combinator, &selector_iter, context); } } @@ -573,26 +572,26 @@ where E: Element<'i>, F: FnMut(&E, ElementSelectorFlags), { - let matches_hover_and_active_quirk = matches_hover_and_active_quirk(&selector_iter, context, rightmost); + let matches_hover_and_active_quirk = matches_hover_and_active_quirk(selector_iter, context, rightmost); // Handle some common cases first. // We may want to get rid of this at some point if we can make the // generic case fast enough. let mut selector = selector_iter.next(); - if let Some(&Component::LocalName(ref local_name)) = selector { + if let Some(Component::LocalName(local_name)) = selector { if !matches_local_name(element, local_name) { return false; } selector = selector_iter.next(); } let class_and_id_case_sensitivity = context.classes_and_ids_case_sensitivity(); - if let Some(&Component::ID(ref id)) = selector { + if let Some(Component::ID(id)) = selector { if !element.has_id(id, class_and_id_case_sensitivity) { return false; } selector = selector_iter.next(); } - while let Some(&Component::Class(ref class)) = selector { + while let Some(Component::Class(class)) = selector { if !element.has_class(class, class_and_id_case_sensitivity) { return false; } @@ -675,12 +674,10 @@ where Component::PseudoElement(ref pseudo) => element.match_pseudo_element(pseudo, context.shared), Component::LocalName(ref local_name) => matches_local_name(element, local_name), Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true, - Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => { - element.has_namespace(&url.borrow()) - } + Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => element.has_namespace(url.borrow()), Component::ExplicitNoNamespace => { let ns = crate::parser::namespace_empty_string::(); - element.has_namespace(&ns.borrow()) + element.has_namespace(ns.borrow()) } Component::ID(ref id) => element.has_id(id, context.shared.classes_and_ids_case_sensitivity()), Component::Class(ref class) => element.has_class(class, context.shared.classes_and_ids_case_sensitivity()), @@ -755,7 +752,7 @@ where return false; } - element.match_non_ts_pseudo_class(pc, &mut context.shared, flags_setter) + element.match_non_ts_pseudo_class(pc, context.shared, flags_setter) } Component::Root => element.is_root(), Component::Empty => { @@ -763,8 +760,8 @@ where element.is_empty() } Component::Host(ref selector) => { - context.shared.shadow_host().map_or(false, |host| host == element.opaque()) - && selector.as_ref().map_or(true, |selector| { + context.shared.shadow_host().is_some_and(|host| host == element.opaque()) + && selector.as_ref().is_none_or(|selector| { context .shared .nest(|context| matches_complex_selector(selector.iter(), element, context, flags_setter)) diff --git a/selectors/parser.rs b/selectors/parser.rs index 59320f443..5fd1c45e5 100644 --- a/selectors/parser.rs +++ b/selectors/parser.rs @@ -82,7 +82,7 @@ pub trait NonTSPseudoClass<'i>: Sized + ToCss { /// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a /// Cow::Owned if `s` had to be converted into ASCII lowercase. fn to_ascii_lowercase<'i>(s: CowRcStr<'i>) -> CowRcStr<'i> { - if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { + if let Some(first_uppercase) = s.bytes().position(|byte: u8| byte.is_ascii_uppercase()) { let mut string = s.to_string(); string[first_uppercase..].make_ascii_lowercase(); string.into() @@ -1113,7 +1113,7 @@ impl<'a, 'i, Impl: 'a + SelectorImpl<'i>> AncestorIter<'a, 'i, Impl> { if self .0 .next_sequence() - .map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) + .is_none_or(|x| matches!(x, Combinator::Child | Combinator::Descendant)) { break; } @@ -1376,7 +1376,7 @@ impl<'i, Impl: SelectorImpl<'i>> NthOfSelectorData<'i, Impl> { /// Returns the selector list part of the selector #[inline] pub fn selectors(&self) -> &[Selector<'i, Impl>] { - &*self.1 + &self.1 } pub fn clone_selectors(&self) -> Box<[Selector<'i, Impl>]> { @@ -1675,14 +1675,12 @@ impl<'i, Impl: SelectorImpl<'i>> Component<'i, Impl> { } Negation(ref list) | Is(ref list) | Where(ref list) => { - if !visitor.visit_selector_list(&list) { + if !visitor.visit_selector_list(list) { return false; } } - NthOf(ref nth_of_data) => { - if !visitor.visit_selector_list(nth_of_data.selectors()) { - return false; - } + NthOf(ref nth_of_data) if !visitor.visit_selector_list(nth_of_data.selectors()) => { + return false; } _ => {} } @@ -2123,7 +2121,7 @@ where { if nesting_requirement == NestingRequirement::Prefixed { let state = input.state(); - if !input.expect_delim('&').is_ok() { + if input.expect_delim('&').is_err() { return Err(input.new_custom_error(SelectorParseErrorKind::MissingNestingPrefix)); } input.reset(&state); @@ -2408,7 +2406,7 @@ where let location = input.current_source_location(); match input.next_including_whitespace() { Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)), - Ok(&Token::Ident(ref local_name)) => Ok(OptionalQName::Some(namespace, Some(local_name.clone()))), + Ok(Token::Ident(local_name)) => Ok(OptionalQName::Some(namespace, Some(local_name.clone()))), Ok(t) if in_attr_selector => { let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone()); Err(location.new_custom_error(e)) @@ -2420,7 +2418,7 @@ where let start = input.state(); // FIXME: remove clone() when lifetimes are non-lexical - match input.next_including_whitespace().map(|t| t.clone()) { + match input.next_including_whitespace().cloned() { Ok(Token::Ident(value)) => { let after_ident = input.state(); match input.next_including_whitespace() { @@ -2447,7 +2445,7 @@ where Ok(Token::Delim('*')) => { let after_star = input.state(); // FIXME: remove clone() when lifetimes are non-lexical - match input.next_including_whitespace().map(|t| t.clone()) { + match input.next_including_whitespace().cloned() { Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace), result => { input.reset(&after_star); @@ -2571,17 +2569,17 @@ where let attribute_flags = parse_attribute_flags(input)?; let value = value.into(); - let case_sensitivity; + // copied from to_ascii_lowercase function, so we can know whether it is already lower case. let (local_name_lower_cow, local_name_is_ascii_lowercase) = - if let Some(first_uppercase) = local_name.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { + if let Some(first_uppercase) = local_name.bytes().position(|byte: u8| byte.is_ascii_uppercase()) { let mut string = local_name.to_string(); string[first_uppercase..].make_ascii_lowercase(); (string.into(), false) } else { (local_name.clone(), true) }; - case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some()); + let case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some()); let local_name_lower = local_name_lower_cow.into(); let local_name = local_name.into(); if namespace.is_some() || !local_name_is_ascii_lowercase { @@ -2967,7 +2965,7 @@ where { let start = input.state(); let token_location = input.current_source_location(); - let token = match input.next_including_whitespace().map(|t| t.clone()) { + let token = match input.next_including_whitespace().cloned() { Ok(t) => t, Err(..) => { input.reset(&start); @@ -3150,6 +3148,7 @@ pub mod tests { pub enum PseudoClass { Hover, Active, + Current, Lang(String), } @@ -3157,6 +3156,8 @@ pub mod tests { pub enum PseudoElement { Before, After, + Named(String), + Functional(String, String), } impl<'i> parser::PseudoElement<'i> for PseudoElement { @@ -3193,6 +3194,7 @@ pub mod tests { match *self { PseudoClass::Hover => dest.write_str(":hover"), PseudoClass::Active => dest.write_str(":active"), + PseudoClass::Current => dest.write_str(":current"), PseudoClass::Lang(ref lang) => { dest.write_str(":lang(")?; serialize_identifier(lang, dest)?; @@ -3210,6 +3212,12 @@ pub mod tests { match *self { PseudoElement::Before => dest.write_str("::before"), PseudoElement::After => dest.write_str("::after"), + PseudoElement::Named(ref name) => write!(dest, "::{}", name), + PseudoElement::Functional(ref name, ref argument) => { + write!(dest, "::{}(", name)?; + serialize_identifier(argument, dest)?; + dest.write_char(')') + } } } } @@ -3352,6 +3360,7 @@ pub mod tests { match_ignore_ascii_case! { &name, "hover" => return Ok(PseudoClass::Hover), "active" => return Ok(PseudoClass::Active), + "current" => return Ok(PseudoClass::Current), _ => {} } Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name))) @@ -3380,11 +3389,31 @@ pub mod tests { match_ignore_ascii_case! { &name, "before" => return Ok(PseudoElement::Before), "after" => return Ok(PseudoElement::After), + "details-content" | "target-text" | "search-text" | "picker-icon" | + "checkmark" | "grammar-error" | "spelling-error" => { + return Ok(PseudoElement::Named(name.to_ascii_lowercase())); + }, _ => {} } Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name))) } + fn parse_functional_pseudo_element<'t>( + &self, + name: CowRcStr<'i>, + arguments: &mut CssParser<'i, 't>, + ) -> Result> { + let argument = match_ignore_ascii_case! { &name, + "highlight" => arguments.expect_ident()?.to_string(), + "picker" => { + arguments.expect_ident_matching("select")?; + "select".to_owned() + }, + _ => return Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name))), + }; + Ok(PseudoElement::Functional(name.to_ascii_lowercase(), argument)) + } + fn default_namespace(&self) -> Option { self.default_ns.clone() } @@ -3412,10 +3441,10 @@ pub mod tests { parse_ns_expected(input, parser, None) } - fn parse_ns_expected<'i, 'a>( + fn parse_ns_expected<'i>( input: &'i str, parser: &DummyParser, - expected: Option<&'a str>, + expected: Option<&str>, ) -> Result, SelectorParseError<'i>> { let mut parser_input = ParserInput::new(input); let result = SelectorList::parse( @@ -3850,7 +3879,7 @@ pub mod tests { Component::Combinator(Combinator::Child), Component::Class(DummyAtom::from("ok")), ], - (1 << 20) + (1 << 10) + (0 << 0), + (1 << 20) + (1 << 10), Default::default(), )])) ); diff --git a/selectors/serialization.rs b/selectors/serialization.rs index 102ab56f7..617abb5d7 100644 --- a/selectors/serialization.rs +++ b/selectors/serialization.rs @@ -300,7 +300,7 @@ where S: serde::Serializer, { let c: SerializedComponent<'_, 'i, Impl, _, _, _> = match self { - Component::Combinator(c) => SerializedComponent::Combinator { value: c.clone() }, + Component::Combinator(c) => SerializedComponent::Combinator { value: *c }, Component::ExplicitUniversalType => SerializedComponent::Universal, Component::ExplicitAnyNamespace => SerializedComponent::Namespace(Namespace::Any), Component::ExplicitNoNamespace => SerializedComponent::Namespace(Namespace::None), @@ -328,8 +328,8 @@ where namespace: None, name: local_name.as_ref().into(), operation: Some(AttrOperation { - operator: operator.clone(), - case_sensitivity: case_sensitivity.clone(), + operator: *operator, + case_sensitivity: *case_sensitivity, value: value.as_ref().into(), }), }), @@ -354,8 +354,8 @@ where case_sensitivity, expected_value, } => Some(AttrOperation { - operator: operator.clone(), - case_sensitivity: case_sensitivity.clone(), + operator: *operator, + case_sensitivity: *case_sensitivity, value: expected_value.as_ref().into(), }), }, @@ -480,7 +480,7 @@ where SerializedComponent::Class { name } => Component::Class(name.into()), SerializedComponent::Attribute(attr) => { let (local_name_lower_cow, local_name_is_ascii_lowercase) = - if let Some(first_uppercase) = attr.name.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { + if let Some(first_uppercase) = attr.name.bytes().position(|byte: u8| byte.is_ascii_uppercase()) { let mut string = attr.name.to_string(); string[first_uppercase..].make_ascii_lowercase(); (string.into(), false) diff --git a/src/bundler.rs b/src/bundler.rs index 124808e4a..ff613bc44 100644 --- a/src/bundler.rs +++ b/src/bundler.rs @@ -1218,8 +1218,9 @@ mod tests { }, "/a.css", ); + // Parallel imports may merge these commutative conditions in either order. assert_eq!( - res, + res.replacen("@media screen, print {", "@media print, screen {", 1), indoc! { r#" @media print, screen { .b { @@ -1248,8 +1249,13 @@ mod tests { }, "/a.css", ); + // Parallel imports may merge these commutative conditions in either order. assert_eq!( - res, + res.replacen( + "@supports (foo: bar) or (color: red) {", + "@supports (color: red) or (foo: bar) {", + 1 + ), indoc! { r#" @supports (color: red) or (foo: bar) { .b { diff --git a/src/main.rs b/src/main.rs index 27f9c8a51..335a14e66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -111,17 +111,14 @@ pub fn main() -> Result<(), std::io::Error> { // Don't silently wait for input if stdin was not redirected. let stdin = io::stdin(); if stdin.is_terminal() { - return Err(io::Error::new( - io::ErrorKind::Other, - "Not reading from stdin as it was not redirected", - )); + return Err(io::Error::other("Not reading from stdin as it was not redirected")); } let filename = format!("stdin-{}", std::process::id()); let contents = io::read_to_string(stdin)?; vec![(filename, contents)] }; - let css_modules = if let Some(_) = cli_args.css_modules { + let css_modules = if cli_args.css_modules.is_some() { let pattern = if let Some(pattern) = cli_args.css_modules_pattern.as_ref() { match rspack_lightningcss::css_modules::Pattern::parse(pattern) { Ok(p) => p, @@ -160,12 +157,11 @@ pub fn main() -> Result<(), std::io::Error> { let output_file = if let Some(output_file) = &cli_args.output_file { Some(Cow::Borrowed(Path::new(output_file))) - } else if let Some(dir) = &cli_args.output_dir { - Some(Cow::Owned( - Path::new(dir).join(Path::new(&filename).file_name().unwrap()), - )) } else { - None + cli_args + .output_dir + .as_ref() + .map(|dir| Cow::Owned(Path::new(dir).join(Path::new(&filename).file_name().unwrap()))) }; let res = { @@ -229,7 +225,7 @@ pub fn main() -> Result<(), std::io::Error> { let mut vlq_output: Vec = Vec::new(); source_map .write_vlq(&mut vlq_output) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "Error writing sourcemap vlq"))?; + .map_err(|_| io::Error::other("Error writing sourcemap vlq"))?; let sm = SourceMapJson { version: 3, @@ -297,8 +293,7 @@ pub fn main() -> Result<(), std::io::Error> { fn infer_css_modules_filename(path: &Path) -> Result { if path.extension() == Some(ffi::OsStr::new("json")) { - Err(io::Error::new( - io::ErrorKind::Other, + Err(io::Error::other( "Cannot infer a css modules json filename, since the output file extension is '.json'", )) } else { diff --git a/static-self-derive/src/into_owned.rs b/static-self-derive/src/into_owned.rs index 2b69bd28f..c0db34b82 100644 --- a/static-self-derive/src/into_owned.rs +++ b/static-self-derive/src/into_owned.rs @@ -117,7 +117,7 @@ pub(crate) fn derive_into_owned(input: TokenStream) -> TokenStream { let has_lifetime = generics .params .first() - .map_or(false, |v| matches!(v, syn::GenericParam::Lifetime(..))); + .is_some_and(|v| matches!(v, syn::GenericParam::Lifetime(..))); let has_generic = !generics.params.is_empty(); // Prepend `'any` to generics diff --git a/tests/cli_integration_tests.rs b/tests/cli_integration_tests.rs index 1d8158352..79226203f 100644 --- a/tests/cli_integration_tests.rs +++ b/tests/cli_integration_tests.rs @@ -434,7 +434,7 @@ fn sourcemap() -> Result<(), Box> { cmd.arg("--sourcemap"); cmd.assert().success(); - outfile.assert(predicate::str::contains(&format!( + outfile.assert(predicate::str::contains(format!( "/*# sourceMappingURL={}.map */", outfile.path().to_str().unwrap() ))); diff --git a/tests/test_custom_parser.rs b/tests/test_custom_parser.rs index 86de2e6aa..2b9f1910c 100644 --- a/tests/test_custom_parser.rs +++ b/tests/test_custom_parser.rs @@ -9,7 +9,7 @@ use rspack_lightningcss::{ }; fn minify_test(source: &str, expected: &str) { - let mut stylesheet = StyleSheet::parse_with(&source, ParserOptions::default(), &mut TestAtRuleParser).unwrap(); + let mut stylesheet = StyleSheet::parse_with(source, ParserOptions::default(), &mut TestAtRuleParser).unwrap(); stylesheet.minify(Default::default()).unwrap(); let res = stylesheet .to_css(