diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index f7d8ea5d6..5017ec8c1 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1,9 +1,9 @@ use crate::ast::*; -use std::{cell::Cell, fmt, hash::Hash}; +use std::{borrow::Cow, cell::Cell, fmt, hash::Hash}; use oxc_allocator::{Box, FromIn, Vec}; -use oxc_span::{Atom, CompactStr, GetSpan, SourceType, Span}; +use oxc_span::{Atom, GetSpan, SourceType, Span}; use oxc_syntax::{ operator::UnaryOperator, reference::{ReferenceFlag, ReferenceId}, @@ -332,20 +332,20 @@ impl<'a> ObjectExpression<'a> { } impl<'a> PropertyKey<'a> { - pub fn static_name(&self) -> Option { + pub fn static_name(&self) -> Option> { match self { - Self::StaticIdentifier(ident) => Some(ident.name.to_compact_str()), - Self::StringLiteral(lit) => Some(lit.value.to_compact_str()), - Self::RegExpLiteral(lit) => Some(lit.regex.to_string().into()), - Self::NumericLiteral(lit) => Some(lit.value.to_string().into()), - Self::BigIntLiteral(lit) => Some(lit.raw.to_compact_str()), - Self::NullLiteral(_) => Some("null".into()), + Self::StaticIdentifier(ident) => Some(Cow::Borrowed(ident.name.as_str())), + Self::StringLiteral(lit) => Some(Cow::Borrowed(lit.value.as_str())), + Self::RegExpLiteral(lit) => Some(Cow::Owned(lit.regex.to_string())), + Self::NumericLiteral(lit) => Some(Cow::Owned(lit.value.to_string())), + Self::BigIntLiteral(lit) => Some(Cow::Borrowed(lit.raw.as_str())), + Self::NullLiteral(_) => Some(Cow::Borrowed("null")), Self::TemplateLiteral(lit) => lit .expressions .is_empty() .then(|| lit.quasi()) .flatten() - .map(|quasi| quasi.to_compact_str()), + .map(std::convert::Into::into), _ => None, } } @@ -369,9 +369,9 @@ impl<'a> PropertyKey<'a> { } } - pub fn name(&self) -> Option { + pub fn name(&self) -> Option> { if self.is_private_identifier() { - self.private_name().map(|name| name.to_compact_str()) + self.private_name().map(|name| Cow::Borrowed(name.as_str())) } else { self.static_name() } @@ -1237,7 +1237,7 @@ impl<'a> ClassElement<'a> { } } - pub fn static_name(&self) -> Option { + pub fn static_name(&self) -> Option> { match self { Self::TSIndexSignature(_) | Self::StaticBlock(_) => None, Self::MethodDefinition(def) => def.key.static_name(), @@ -1424,8 +1424,8 @@ impl<'a> ImportDeclarationSpecifier<'a> { ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => &specifier.local, } } - pub fn name(&self) -> CompactStr { - self.local().name.to_compact_str() + pub fn name(&self) -> Cow<'a, str> { + Cow::Borrowed(self.local().name.as_str()) } } diff --git a/crates/oxc_isolated_declarations/src/module.rs b/crates/oxc_isolated_declarations/src/module.rs index da2df150c..7abf046fa 100644 --- a/crates/oxc_isolated_declarations/src/module.rs +++ b/crates/oxc_isolated_declarations/src/module.rs @@ -104,7 +104,7 @@ impl<'a> IsolatedDeclarations<'a> { self.scope.has_reference(&specifier.local.name) } ImportDeclarationSpecifier::ImportNamespaceSpecifier(_) => { - self.scope.has_reference(specifier.name().as_str()) + self.scope.has_reference(&specifier.name()) } }); if specifiers.is_empty() { diff --git a/crates/oxc_linter/src/rules/eslint/sort_imports.rs b/crates/oxc_linter/src/rules/eslint/sort_imports.rs index b78a4257b..5597f9094 100644 --- a/crates/oxc_linter/src/rules/eslint/sort_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/sort_imports.rs @@ -1,4 +1,5 @@ use std::{ + borrow::Cow, fmt::{Display, Write}, str::FromStr, }; @@ -10,7 +11,7 @@ use oxc_ast::{ }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_span::{CompactStr, Span}; +use oxc_span::Span; use crate::{context::LintContext, rule::Rule}; @@ -208,9 +209,9 @@ impl SortImports { if self.ignore_case { current_local_member_name = - current_local_member_name.map(|name| name.to_lowercase()).map(CompactStr::from); + current_local_member_name.map(|name| name.to_lowercase().into()); previous_local_member_name = - previous_local_member_name.map(|name| name.to_lowercase()).map(CompactStr::from); + previous_local_member_name.map(|name| name.to_lowercase().into()); } // "memberSyntaxSortOrder": ["none", "all", "multiple", "single"] @@ -240,8 +241,6 @@ impl SortImports { if let Some((current_name, previous_name)) = current_local_member_name.zip(previous_local_member_name) { - let current_name = current_name.as_str(); - let previous_name = previous_name.as_str(); if current_name < previous_name { ctx.diagnostic(sort_imports_alphabetically_diagnostic(current.span)); } @@ -443,7 +442,7 @@ impl Display for ImportKind { } } -fn get_first_local_member_name(decl: &ImportDeclaration) -> Option { +fn get_first_local_member_name<'a>(decl: &ImportDeclaration<'a>) -> Option> { let specifiers = decl.specifiers.as_ref()?; specifiers.first().map(ImportDeclarationSpecifier::name) } diff --git a/crates/oxc_linter/src/rules/react/no_danger.rs b/crates/oxc_linter/src/rules/react/no_danger.rs index 90bbc738e..9b3f2927d 100644 --- a/crates/oxc_linter/src/rules/react/no_danger.rs +++ b/crates/oxc_linter/src/rules/react/no_danger.rs @@ -64,7 +64,7 @@ impl Rule for NoDanger { for prop in &obj_expr.properties { if let ObjectPropertyKind::ObjectProperty(obj_prop) = prop { if let Some(prop_name) = obj_prop.key.static_name() { - if prop_name.as_str() == "dangerouslySetInnerHTML" { + if prop_name == "dangerouslySetInnerHTML" { ctx.diagnostic(no_danger_diagnostic(obj_prop.key.span())); } } diff --git a/crates/oxc_linter/src/rules/react/rules_of_hooks.rs b/crates/oxc_linter/src/rules/react/rules_of_hooks.rs index 524b1e111..873709c9a 100644 --- a/crates/oxc_linter/src/rules/react/rules_of_hooks.rs +++ b/crates/oxc_linter/src/rules/react/rules_of_hooks.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use oxc_ast::{ ast::{ArrowFunctionExpression, Function}, AstKind, @@ -8,7 +10,6 @@ use oxc_cfg::{ }; use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNodeId, AstNodes}; -use oxc_span::CompactStr; use oxc_syntax::operator::AssignmentOperator; use crate::{ @@ -191,7 +192,7 @@ impl Rule for RulesOfHooks { // useState(0); // } // } - if ident.is_some_and(|name| !is_react_component_or_hook_name(name.as_str())) + if ident.is_some_and(|name| !is_react_component_or_hook_name(&name)) || is_export_default(nodes, parent_func.id()) { return ctx.diagnostic(diagnostics::function_error( @@ -352,7 +353,9 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) - ( node.id(), match node.kind() { - AstKind::Function(func) => func.id.as_ref().map(|it| it.name.to_compact_str()), + AstKind::Function(func) => { + func.id.as_ref().map(|it| Cow::Borrowed(it.name.as_str())) + } AstKind::ArrowFunctionExpression(_) => { get_declaration_identifier(nodes, node.id()) } @@ -362,8 +365,7 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) - }) .any(|(id, ident)| { ident.is_some_and(|name| { - is_react_component_or_hook_name(name.as_str()) - || is_memo_or_forward_ref_callback(nodes, id) + is_react_component_or_hook_name(&name) || is_memo_or_forward_ref_callback(nodes, id) }) }) } @@ -371,12 +373,12 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) - fn get_declaration_identifier<'a>( nodes: &'a AstNodes<'a>, node_id: AstNodeId, -) -> Option { +) -> Option> { nodes.ancestors(node_id).map(|id| nodes.kind(id)).find_map(|kind| { match kind { // const useHook = () => {}; AstKind::VariableDeclaration(decl) if decl.declarations.len() == 1 => { - decl.declarations[0].id.get_identifier().map(|id| id.to_compact_str()) + decl.declarations[0].id.get_identifier().map(|id| Cow::Borrowed(id.as_str())) } // useHook = () => {}; AstKind::AssignmentExpression(expr) @@ -387,7 +389,7 @@ fn get_declaration_identifier<'a>( // const {useHook = () => {}} = {}; // ({useHook = () => {}} = {}); AstKind::AssignmentPattern(patt) => { - patt.left.get_identifier().map(|id| id.to_compact_str()) + patt.left.get_identifier().map(|id| Cow::Borrowed(id.as_str())) } // { useHook: () => {} } // { useHook() {} } diff --git a/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs b/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs index b9afa394f..c7da96fe5 100644 --- a/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs +++ b/crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs @@ -127,7 +127,7 @@ impl GetMethod for ClassElement<'_> { fn get_method(&self) -> Option { match self { ClassElement::MethodDefinition(def) => def.key.static_name().map(|name| Method { - name, + name: name.into(), r#static: def.r#static, call_signature: false, kind: get_kind_from_key(&def.key), @@ -142,7 +142,7 @@ impl GetMethod for TSSignature<'_> { fn get_method(&self) -> Option { match self { TSSignature::TSMethodSignature(sig) => sig.key.static_name().map(|name| Method { - name: name.clone(), + name: name.into(), r#static: false, call_signature: false, kind: get_kind_from_key(&sig.key), diff --git a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs index 2b6d3e12c..f42aad8ac 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs @@ -1,4 +1,4 @@ -use std::{error::Error, ops::Deref}; +use std::{borrow::Cow, error::Error, ops::Deref}; use itertools::Itertools; use oxc_ast::{ @@ -11,7 +11,7 @@ use oxc_ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::{Reference, SymbolId}; -use oxc_span::{CompactStr, GetSpan, Span}; +use oxc_span::{GetSpan, Span}; use crate::{ context::LintContext, @@ -231,7 +231,7 @@ impl Rule for ConsistentTypeImports { // ['foo', 'bar', 'baz' ] => "foo, bar, and baz". let type_imports = format_word_list(&type_names); - let type_names = type_names.iter().map(CompactStr::as_str).collect::>(); + let type_names = type_names.iter().map(std::convert::AsRef::as_ref).collect::>(); let fixer_fn = |fixer: RuleFixer<'_, 'a>| { let fix_options = FixOptions { @@ -272,13 +272,13 @@ impl Rule for ConsistentTypeImports { // the `and` clause inserted before the last item. // // Example: ['foo', 'bar', 'baz' ] returns the string "foo, bar, and baz". -fn format_word_list(words: &[CompactStr]) -> String { +fn format_word_list<'a>(words: &[Cow<'a, str>]) -> Cow<'a, str> { match words.len() { - 0 => String::new(), - 1 => words[0].to_string(), - 2 => format!("{} and {}", words[0], words[1]), + 0 => Cow::Borrowed(""), + 1 => words[0].clone(), + 2 => Cow::Owned(format!("{} and {}", words[0], words[1])), _ => { - let mut result = String::new(); + let mut result = String::with_capacity(words.len() * 2); for (i, word) in words.iter().enumerate() { if i == words.len() - 1 { result.push_str(&format!("and {word}")); @@ -286,7 +286,7 @@ fn format_word_list(words: &[CompactStr]) -> String { result.push_str(&format!("{word}, ")); } } - result + Cow::Owned(result) } } } diff --git a/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs b/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs index 6e7b5deb7..599672ee1 100644 --- a/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs +++ b/crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs @@ -316,19 +316,19 @@ impl ExplicitFunctionReturnType { let Some(name) = def.key.name() else { return false }; def.key.is_identifier() && !def.computed - && self.allowed_names.contains(name.as_str()) + && self.allowed_names.contains(name.as_ref()) } AstKind::PropertyDefinition(def) => { let Some(name) = def.key.name() else { return false }; def.key.is_identifier() && !def.computed - && self.allowed_names.contains(name.as_str()) + && self.allowed_names.contains(name.as_ref()) } AstKind::ObjectProperty(prop) => { let Some(name) = prop.key.name() else { return false }; prop.key.is_identifier() && !prop.computed - && self.allowed_names.contains(name.as_str()) + && self.allowed_names.contains(name.as_ref()) } _ => false, } diff --git a/crates/oxc_semantic/src/class/builder.rs b/crates/oxc_semantic/src/class/builder.rs index bb77fca4f..8e1067aac 100644 --- a/crates/oxc_semantic/src/class/builder.rs +++ b/crates/oxc_semantic/src/class/builder.rs @@ -63,7 +63,7 @@ impl ClassTableBuilder { self.classes.add_element( class_id, Element::new( - name, + name.into(), property.key.span(), property.r#static, is_private, @@ -83,7 +83,7 @@ impl ClassTableBuilder { self.classes.add_element( class_id, Element::new( - name, + name.into(), property.key.span(), property.r#static, is_private, @@ -124,18 +124,14 @@ impl ClassTableBuilder { return; } let is_private = method.key.is_private_identifier(); - let name = if is_private { - method.key.private_name().map(|name| name.to_compact_str()) - } else { - method.key.static_name() - }; + let name = method.key.name(); if let Some(name) = name { if let Some(class_id) = self.current_class_id { self.classes.add_element( class_id, Element::new( - name, + name.into(), method.key.span(), method.r#static, is_private, diff --git a/crates/oxc_span/src/atom.rs b/crates/oxc_span/src/atom.rs index 40e5f42ec..6985001f5 100644 --- a/crates/oxc_span/src/atom.rs +++ b/crates/oxc_span/src/atom.rs @@ -109,6 +109,13 @@ impl<'a> From> for String { } } +impl<'a> From> for Cow<'a, str> { + #[inline] + fn from(value: Atom<'a>) -> Self { + Cow::Borrowed(value.as_str()) + } +} + impl<'a> Deref for Atom<'a> { type Target = str; @@ -147,6 +154,17 @@ impl<'a> PartialEq for Atom<'a> { } } +impl<'a> PartialEq> for Cow<'_, str> { + fn eq(&self, other: &Atom<'a>) -> bool { + self.as_ref() == other.as_str() + } +} +impl<'a> PartialEq<&Atom<'a>> for Cow<'_, str> { + fn eq(&self, other: &&Atom<'a>) -> bool { + self.as_ref() == other.as_str() + } +} + impl<'a> hash::Hash for Atom<'a> { fn hash(&self, hasher: &mut H) { self.as_str().hash(hasher); @@ -265,6 +283,26 @@ impl From for CompactStr { } } +impl<'s> From<&'s CompactStr> for Cow<'s, str> { + fn from(value: &'s CompactStr) -> Self { + Self::Borrowed(value.as_str()) + } +} + +impl From for Cow<'_, str> { + fn from(value: CompactStr) -> Self { + value.0.into() + } +} +impl From> for CompactStr { + fn from(value: Cow<'_, str>) -> Self { + match value { + Cow::Borrowed(s) => CompactStr::new(s), + Cow::Owned(s) => CompactStr::from(s), + } + } +} + impl Deref for CompactStr { type Target = str; @@ -309,6 +347,12 @@ impl PartialEq for CompactStr { } } +impl PartialEq for Cow<'_, str> { + fn eq(&self, other: &CompactStr) -> bool { + self.as_ref() == other.as_str() + } +} + impl Index for CompactStr { type Output = str; diff --git a/tasks/prettier_conformance/src/spec.rs b/tasks/prettier_conformance/src/spec.rs index e68695e24..f54c93bd9 100644 --- a/tasks/prettier_conformance/src/spec.rs +++ b/tasks/prettier_conformance/src/spec.rs @@ -77,12 +77,12 @@ impl VisitMut<'_> for SpecParser { options.single_quote = literal.value; } } - Expression::NumericLiteral(literal) => match name.as_str() { + Expression::NumericLiteral(literal) => match name.as_ref() { "printWidth" => options.print_width = literal.value as usize, "tabWidth" => options.tab_width = literal.value as usize, _ => {} }, - Expression::StringLiteral(literal) => match name.as_str() { + Expression::StringLiteral(literal) => match name.as_ref() { "trailingComma" => { options.trailing_comma = TrailingComma::from_str(literal.value.as_str()).unwrap();