mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
perf: replace some CompactStr usages with Cows (#4377)
Reduce memory allocations in semantic and linter by using `Cow<'a, str>` over `CompactStr`
This commit is contained in:
parent
7a3e92591f
commit
a207923af1
11 changed files with 96 additions and 55 deletions
|
|
@ -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<CompactStr> {
|
||||
pub fn static_name(&self) -> Option<Cow<'a, str>> {
|
||||
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<CompactStr> {
|
||||
pub fn name(&self) -> Option<Cow<'a, str>> {
|
||||
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<CompactStr> {
|
||||
pub fn static_name(&self) -> Option<Cow<'a, str>> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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<CompactStr> {
|
||||
fn get_first_local_member_name<'a>(decl: &ImportDeclaration<'a>) -> Option<Cow<'a, str>> {
|
||||
let specifiers = decl.specifiers.as_ref()?;
|
||||
specifiers.first().map(ImportDeclarationSpecifier::name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CompactStr> {
|
||||
) -> Option<Cow<'a, str>> {
|
||||
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() {} }
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ impl GetMethod for ClassElement<'_> {
|
|||
fn get_method(&self) -> Option<Method> {
|
||||
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<Method> {
|
||||
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),
|
||||
|
|
|
|||
|
|
@ -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::<Vec<_>>();
|
||||
let type_names = type_names.iter().map(std::convert::AsRef::as_ref).collect::<Vec<_>>();
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -109,6 +109,13 @@ impl<'a> From<Atom<'a>> for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Atom<'a>> 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<str> for Atom<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Atom<'a>> 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<H: hash::Hasher>(&self, hasher: &mut H) {
|
||||
self.as_str().hash(hasher);
|
||||
|
|
@ -265,6 +283,26 @@ impl From<String> for CompactStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'s> From<&'s CompactStr> for Cow<'s, str> {
|
||||
fn from(value: &'s CompactStr) -> Self {
|
||||
Self::Borrowed(value.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CompactStr> for Cow<'_, str> {
|
||||
fn from(value: CompactStr) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
impl From<Cow<'_, str>> 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<str> for CompactStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq<CompactStr> for Cow<'_, str> {
|
||||
fn eq(&self, other: &CompactStr) -> bool {
|
||||
self.as_ref() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Span> for CompactStr {
|
||||
type Output = str;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue