Skip to content

Commit a207923

Browse files
committed
perf: replace some CompactStr usages with Cows (#4377)
Reduce memory allocations in semantic and linter by using `Cow<'a, str>` over `CompactStr`
1 parent 7a3e925 commit a207923

File tree

11 files changed

+96
-55
lines changed

11 files changed

+96
-55
lines changed

crates/oxc_ast/src/ast_impl/js.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::ast::*;
22

3-
use std::{cell::Cell, fmt, hash::Hash};
3+
use std::{borrow::Cow, cell::Cell, fmt, hash::Hash};
44

55
use oxc_allocator::{Box, FromIn, Vec};
6-
use oxc_span::{Atom, CompactStr, GetSpan, SourceType, Span};
6+
use oxc_span::{Atom, GetSpan, SourceType, Span};
77
use oxc_syntax::{
88
operator::UnaryOperator,
99
reference::{ReferenceFlag, ReferenceId},
@@ -332,20 +332,20 @@ impl<'a> ObjectExpression<'a> {
332332
}
333333

334334
impl<'a> PropertyKey<'a> {
335-
pub fn static_name(&self) -> Option<CompactStr> {
335+
pub fn static_name(&self) -> Option<Cow<'a, str>> {
336336
match self {
337-
Self::StaticIdentifier(ident) => Some(ident.name.to_compact_str()),
338-
Self::StringLiteral(lit) => Some(lit.value.to_compact_str()),
339-
Self::RegExpLiteral(lit) => Some(lit.regex.to_string().into()),
340-
Self::NumericLiteral(lit) => Some(lit.value.to_string().into()),
341-
Self::BigIntLiteral(lit) => Some(lit.raw.to_compact_str()),
342-
Self::NullLiteral(_) => Some("null".into()),
337+
Self::StaticIdentifier(ident) => Some(Cow::Borrowed(ident.name.as_str())),
338+
Self::StringLiteral(lit) => Some(Cow::Borrowed(lit.value.as_str())),
339+
Self::RegExpLiteral(lit) => Some(Cow::Owned(lit.regex.to_string())),
340+
Self::NumericLiteral(lit) => Some(Cow::Owned(lit.value.to_string())),
341+
Self::BigIntLiteral(lit) => Some(Cow::Borrowed(lit.raw.as_str())),
342+
Self::NullLiteral(_) => Some(Cow::Borrowed("null")),
343343
Self::TemplateLiteral(lit) => lit
344344
.expressions
345345
.is_empty()
346346
.then(|| lit.quasi())
347347
.flatten()
348-
.map(|quasi| quasi.to_compact_str()),
348+
.map(std::convert::Into::into),
349349
_ => None,
350350
}
351351
}
@@ -369,9 +369,9 @@ impl<'a> PropertyKey<'a> {
369369
}
370370
}
371371

372-
pub fn name(&self) -> Option<CompactStr> {
372+
pub fn name(&self) -> Option<Cow<'a, str>> {
373373
if self.is_private_identifier() {
374-
self.private_name().map(|name| name.to_compact_str())
374+
self.private_name().map(|name| Cow::Borrowed(name.as_str()))
375375
} else {
376376
self.static_name()
377377
}
@@ -1237,7 +1237,7 @@ impl<'a> ClassElement<'a> {
12371237
}
12381238
}
12391239

1240-
pub fn static_name(&self) -> Option<CompactStr> {
1240+
pub fn static_name(&self) -> Option<Cow<'a, str>> {
12411241
match self {
12421242
Self::TSIndexSignature(_) | Self::StaticBlock(_) => None,
12431243
Self::MethodDefinition(def) => def.key.static_name(),
@@ -1424,8 +1424,8 @@ impl<'a> ImportDeclarationSpecifier<'a> {
14241424
ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => &specifier.local,
14251425
}
14261426
}
1427-
pub fn name(&self) -> CompactStr {
1428-
self.local().name.to_compact_str()
1427+
pub fn name(&self) -> Cow<'a, str> {
1428+
Cow::Borrowed(self.local().name.as_str())
14291429
}
14301430
}
14311431

crates/oxc_isolated_declarations/src/module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl<'a> IsolatedDeclarations<'a> {
104104
self.scope.has_reference(&specifier.local.name)
105105
}
106106
ImportDeclarationSpecifier::ImportNamespaceSpecifier(_) => {
107-
self.scope.has_reference(specifier.name().as_str())
107+
self.scope.has_reference(&specifier.name())
108108
}
109109
});
110110
if specifiers.is_empty() {

crates/oxc_linter/src/rules/eslint/sort_imports.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
borrow::Cow,
23
fmt::{Display, Write},
34
str::FromStr,
45
};
@@ -10,7 +11,7 @@ use oxc_ast::{
1011
};
1112
use oxc_diagnostics::OxcDiagnostic;
1213
use oxc_macros::declare_oxc_lint;
13-
use oxc_span::{CompactStr, Span};
14+
use oxc_span::Span;
1415

1516
use crate::{context::LintContext, rule::Rule};
1617

@@ -208,9 +209,9 @@ impl SortImports {
208209

209210
if self.ignore_case {
210211
current_local_member_name =
211-
current_local_member_name.map(|name| name.to_lowercase()).map(CompactStr::from);
212+
current_local_member_name.map(|name| name.to_lowercase().into());
212213
previous_local_member_name =
213-
previous_local_member_name.map(|name| name.to_lowercase()).map(CompactStr::from);
214+
previous_local_member_name.map(|name| name.to_lowercase().into());
214215
}
215216

216217
// "memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
@@ -240,8 +241,6 @@ impl SortImports {
240241
if let Some((current_name, previous_name)) =
241242
current_local_member_name.zip(previous_local_member_name)
242243
{
243-
let current_name = current_name.as_str();
244-
let previous_name = previous_name.as_str();
245244
if current_name < previous_name {
246245
ctx.diagnostic(sort_imports_alphabetically_diagnostic(current.span));
247246
}
@@ -443,7 +442,7 @@ impl Display for ImportKind {
443442
}
444443
}
445444

446-
fn get_first_local_member_name(decl: &ImportDeclaration) -> Option<CompactStr> {
445+
fn get_first_local_member_name<'a>(decl: &ImportDeclaration<'a>) -> Option<Cow<'a, str>> {
447446
let specifiers = decl.specifiers.as_ref()?;
448447
specifiers.first().map(ImportDeclarationSpecifier::name)
449448
}

crates/oxc_linter/src/rules/react/no_danger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl Rule for NoDanger {
6464
for prop in &obj_expr.properties {
6565
if let ObjectPropertyKind::ObjectProperty(obj_prop) = prop {
6666
if let Some(prop_name) = obj_prop.key.static_name() {
67-
if prop_name.as_str() == "dangerouslySetInnerHTML" {
67+
if prop_name == "dangerouslySetInnerHTML" {
6868
ctx.diagnostic(no_danger_diagnostic(obj_prop.key.span()));
6969
}
7070
}

crates/oxc_linter/src/rules/react/rules_of_hooks.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use oxc_ast::{
24
ast::{ArrowFunctionExpression, Function},
35
AstKind,
@@ -8,7 +10,6 @@ use oxc_cfg::{
810
};
911
use oxc_macros::declare_oxc_lint;
1012
use oxc_semantic::{AstNodeId, AstNodes};
11-
use oxc_span::CompactStr;
1213
use oxc_syntax::operator::AssignmentOperator;
1314

1415
use crate::{
@@ -191,7 +192,7 @@ impl Rule for RulesOfHooks {
191192
// useState(0);
192193
// }
193194
// }
194-
if ident.is_some_and(|name| !is_react_component_or_hook_name(name.as_str()))
195+
if ident.is_some_and(|name| !is_react_component_or_hook_name(&name))
195196
|| is_export_default(nodes, parent_func.id())
196197
{
197198
return ctx.diagnostic(diagnostics::function_error(
@@ -352,7 +353,9 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) -
352353
(
353354
node.id(),
354355
match node.kind() {
355-
AstKind::Function(func) => func.id.as_ref().map(|it| it.name.to_compact_str()),
356+
AstKind::Function(func) => {
357+
func.id.as_ref().map(|it| Cow::Borrowed(it.name.as_str()))
358+
}
356359
AstKind::ArrowFunctionExpression(_) => {
357360
get_declaration_identifier(nodes, node.id())
358361
}
@@ -362,21 +365,20 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) -
362365
})
363366
.any(|(id, ident)| {
364367
ident.is_some_and(|name| {
365-
is_react_component_or_hook_name(name.as_str())
366-
|| is_memo_or_forward_ref_callback(nodes, id)
368+
is_react_component_or_hook_name(&name) || is_memo_or_forward_ref_callback(nodes, id)
367369
})
368370
})
369371
}
370372

371373
fn get_declaration_identifier<'a>(
372374
nodes: &'a AstNodes<'a>,
373375
node_id: AstNodeId,
374-
) -> Option<CompactStr> {
376+
) -> Option<Cow<'a, str>> {
375377
nodes.ancestors(node_id).map(|id| nodes.kind(id)).find_map(|kind| {
376378
match kind {
377379
// const useHook = () => {};
378380
AstKind::VariableDeclaration(decl) if decl.declarations.len() == 1 => {
379-
decl.declarations[0].id.get_identifier().map(|id| id.to_compact_str())
381+
decl.declarations[0].id.get_identifier().map(|id| Cow::Borrowed(id.as_str()))
380382
}
381383
// useHook = () => {};
382384
AstKind::AssignmentExpression(expr)
@@ -387,7 +389,7 @@ fn get_declaration_identifier<'a>(
387389
// const {useHook = () => {}} = {};
388390
// ({useHook = () => {}} = {});
389391
AstKind::AssignmentPattern(patt) => {
390-
patt.left.get_identifier().map(|id| id.to_compact_str())
392+
patt.left.get_identifier().map(|id| Cow::Borrowed(id.as_str()))
391393
}
392394
// { useHook: () => {} }
393395
// { useHook() {} }

crates/oxc_linter/src/rules/typescript/adjacent_overload_signatures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl GetMethod for ClassElement<'_> {
127127
fn get_method(&self) -> Option<Method> {
128128
match self {
129129
ClassElement::MethodDefinition(def) => def.key.static_name().map(|name| Method {
130-
name,
130+
name: name.into(),
131131
r#static: def.r#static,
132132
call_signature: false,
133133
kind: get_kind_from_key(&def.key),
@@ -142,7 +142,7 @@ impl GetMethod for TSSignature<'_> {
142142
fn get_method(&self) -> Option<Method> {
143143
match self {
144144
TSSignature::TSMethodSignature(sig) => sig.key.static_name().map(|name| Method {
145-
name: name.clone(),
145+
name: name.into(),
146146
r#static: false,
147147
call_signature: false,
148148
kind: get_kind_from_key(&sig.key),

crates/oxc_linter/src/rules/typescript/consistent_type_imports.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{error::Error, ops::Deref};
1+
use std::{borrow::Cow, error::Error, ops::Deref};
22

33
use itertools::Itertools;
44
use oxc_ast::{
@@ -11,7 +11,7 @@ use oxc_ast::{
1111
use oxc_diagnostics::OxcDiagnostic;
1212
use oxc_macros::declare_oxc_lint;
1313
use oxc_semantic::{Reference, SymbolId};
14-
use oxc_span::{CompactStr, GetSpan, Span};
14+
use oxc_span::{GetSpan, Span};
1515

1616
use crate::{
1717
context::LintContext,
@@ -231,7 +231,7 @@ impl Rule for ConsistentTypeImports {
231231

232232
// ['foo', 'bar', 'baz' ] => "foo, bar, and baz".
233233
let type_imports = format_word_list(&type_names);
234-
let type_names = type_names.iter().map(CompactStr::as_str).collect::<Vec<_>>();
234+
let type_names = type_names.iter().map(std::convert::AsRef::as_ref).collect::<Vec<_>>();
235235

236236
let fixer_fn = |fixer: RuleFixer<'_, 'a>| {
237237
let fix_options = FixOptions {
@@ -272,21 +272,21 @@ impl Rule for ConsistentTypeImports {
272272
// the `and` clause inserted before the last item.
273273
//
274274
// Example: ['foo', 'bar', 'baz' ] returns the string "foo, bar, and baz".
275-
fn format_word_list(words: &[CompactStr]) -> String {
275+
fn format_word_list<'a>(words: &[Cow<'a, str>]) -> Cow<'a, str> {
276276
match words.len() {
277-
0 => String::new(),
278-
1 => words[0].to_string(),
279-
2 => format!("{} and {}", words[0], words[1]),
277+
0 => Cow::Borrowed(""),
278+
1 => words[0].clone(),
279+
2 => Cow::Owned(format!("{} and {}", words[0], words[1])),
280280
_ => {
281-
let mut result = String::new();
281+
let mut result = String::with_capacity(words.len() * 2);
282282
for (i, word) in words.iter().enumerate() {
283283
if i == words.len() - 1 {
284284
result.push_str(&format!("and {word}"));
285285
} else {
286286
result.push_str(&format!("{word}, "));
287287
}
288288
}
289-
result
289+
Cow::Owned(result)
290290
}
291291
}
292292
}

crates/oxc_linter/src/rules/typescript/explicit_function_return_type.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,19 +316,19 @@ impl ExplicitFunctionReturnType {
316316
let Some(name) = def.key.name() else { return false };
317317
def.key.is_identifier()
318318
&& !def.computed
319-
&& self.allowed_names.contains(name.as_str())
319+
&& self.allowed_names.contains(name.as_ref())
320320
}
321321
AstKind::PropertyDefinition(def) => {
322322
let Some(name) = def.key.name() else { return false };
323323
def.key.is_identifier()
324324
&& !def.computed
325-
&& self.allowed_names.contains(name.as_str())
325+
&& self.allowed_names.contains(name.as_ref())
326326
}
327327
AstKind::ObjectProperty(prop) => {
328328
let Some(name) = prop.key.name() else { return false };
329329
prop.key.is_identifier()
330330
&& !prop.computed
331-
&& self.allowed_names.contains(name.as_str())
331+
&& self.allowed_names.contains(name.as_ref())
332332
}
333333
_ => false,
334334
}

crates/oxc_semantic/src/class/builder.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl ClassTableBuilder {
6363
self.classes.add_element(
6464
class_id,
6565
Element::new(
66-
name,
66+
name.into(),
6767
property.key.span(),
6868
property.r#static,
6969
is_private,
@@ -83,7 +83,7 @@ impl ClassTableBuilder {
8383
self.classes.add_element(
8484
class_id,
8585
Element::new(
86-
name,
86+
name.into(),
8787
property.key.span(),
8888
property.r#static,
8989
is_private,
@@ -124,18 +124,14 @@ impl ClassTableBuilder {
124124
return;
125125
}
126126
let is_private = method.key.is_private_identifier();
127-
let name = if is_private {
128-
method.key.private_name().map(|name| name.to_compact_str())
129-
} else {
130-
method.key.static_name()
131-
};
127+
let name = method.key.name();
132128

133129
if let Some(name) = name {
134130
if let Some(class_id) = self.current_class_id {
135131
self.classes.add_element(
136132
class_id,
137133
Element::new(
138-
name,
134+
name.into(),
139135
method.key.span(),
140136
method.r#static,
141137
is_private,

0 commit comments

Comments
 (0)