Skip to content

Commit

Permalink
perf(rust): use cow_utils instead (#5664)
Browse files Browse the repository at this point in the history
Related to #5586 and #5662

---------

Co-authored-by: Boshen <[email protected]>
  • Loading branch information
shulaoda and Boshen authored Sep 11, 2024
1 parent a729b64 commit d18c896
Show file tree
Hide file tree
Showing 40 changed files with 146 additions and 78 deletions.
8 changes: 6 additions & 2 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
ignore-interior-mutability = ["oxc_linter::rule::RuleWithSeverity"]

disallowed-methods = [
{ path = "str::to_ascii_lowercase", reason = "Avoid memory allocation. Use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead." },
{ path = "str::to_ascii_uppercase", reason = "Avoid memory allocation. Use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." },
{ path = "str::to_ascii_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead." },
{ path = "str::to_ascii_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead." },
{ path = "str::to_lowercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_lowercase` instead." },
{ path = "str::to_uppercase", reason = "To avoid memory allocation, use `cow_utils::CowUtils::cow_to_uppercase` instead." },
{ path = "str::replace", reason = "To avoid memory allocation, use `cow_utils::CowUtils::replace` instead." },
{ path = "str::replacen", reason = "To avoid memory allocation, use `cow_utils::CowUtils::replacen` instead." },
]
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/oxc_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ workspace = true
doctest = false

[dependencies]
cow-utils = { workspace = true }
oxc_allocator = { workspace = true }
oxc_ast = { workspace = true }
oxc_index = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{borrow::Cow, ops::Not};

use cow_utils::CowUtils;
use oxc_allocator::{Box, Vec};
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
Expand Down Expand Up @@ -1232,7 +1233,7 @@ impl<'a> Gen for RegExpLiteral<'a> {
);
// Avoid forming a single-line comment or "</script" sequence
if Some('/') == last
|| (Some('<') == last && pattern_text.to_lowercase().starts_with("script"))
|| (Some('<') == last && pattern_text.cow_to_lowercase().starts_with("script"))
{
p.print_hard_space();
}
Expand Down
25 changes: 13 additions & 12 deletions crates/oxc_linter/src/fixer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ impl<'a> Fixer<'a> {

#[cfg(test)]
mod test {
use cow_utils::CowUtils;
use std::borrow::Cow;

use oxc_diagnostics::OxcDiagnostic;
Expand Down Expand Up @@ -417,7 +418,7 @@ mod test {
get_fix_result(vec![create_message(insert_at_middle(), Some(INSERT_AT_MIDDLE))]);
assert_eq!(
result.fixed_code,
TEST_CODE.replace("6 *", &format!("{}{}", INSERT_AT_MIDDLE.content, "6 *"))
TEST_CODE.cow_replace("6 *", &format!("{}{}", INSERT_AT_MIDDLE.content, "6 *"))
);
assert_eq!(result.messages.len(), 0);
}
Expand All @@ -435,7 +436,7 @@ mod test {
format!(
"{}{}{}",
INSERT_AT_START.content,
TEST_CODE.replace("6 *", &format!("{}{}", INSERT_AT_MIDDLE.content, "6 *")),
TEST_CODE.cow_replace("6 *", &format!("{}{}", INSERT_AT_MIDDLE.content, "6 *")),
INSERT_AT_END.content
)
);
Expand All @@ -452,23 +453,23 @@ mod test {
#[test]
fn replace_at_the_start() {
let result = get_fix_result(vec![create_message(replace_var(), Some(REPLACE_VAR))]);
assert_eq!(result.fixed_code, TEST_CODE.replace("var", "let"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("var", "let"));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}

#[test]
fn replace_at_the_middle() {
let result = get_fix_result(vec![create_message(replace_id(), Some(REPLACE_ID))]);
assert_eq!(result.fixed_code, TEST_CODE.replace("answer", "foo"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("answer", "foo"));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}

#[test]
fn replace_at_the_end() {
let result = get_fix_result(vec![create_message(replace_num(), Some(REPLACE_NUM))]);
assert_eq!(result.fixed_code, TEST_CODE.replace('6', "5"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace('6', "5"));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}
Expand All @@ -489,7 +490,7 @@ mod test {
#[test]
fn remove_at_the_start() {
let result = get_fix_result(vec![create_message(remove_start(), Some(REMOVE_START))]);
assert_eq!(result.fixed_code, TEST_CODE.replace("var ", ""));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("var ", ""));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}
Expand All @@ -500,15 +501,15 @@ mod test {
remove_middle(Span::default()),
Some(REMOVE_MIDDLE),
)]);
assert_eq!(result.fixed_code, TEST_CODE.replace("answer", "a"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("answer", "a"));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}

#[test]
fn remove_at_the_end() {
let result = get_fix_result(vec![create_message(remove_end(), Some(REMOVE_END))]);
assert_eq!(result.fixed_code, TEST_CODE.replace(" * 7", ""));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace(" * 7", ""));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}
Expand All @@ -531,7 +532,7 @@ mod test {
create_message(remove_middle(Span::default()), Some(REMOVE_MIDDLE)),
create_message(replace_id(), Some(REPLACE_ID)),
]);
assert_eq!(result.fixed_code, TEST_CODE.replace("answer", "foo"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("answer", "foo"));
assert_eq!(result.messages.len(), 1);
assert_eq!(result.messages[0].error.to_string(), "removemiddle");
assert!(result.fixed);
Expand All @@ -543,7 +544,7 @@ mod test {
create_message(remove_start(), Some(REMOVE_START)),
create_message(replace_id(), Some(REPLACE_ID)),
]);
assert_eq!(result.fixed_code, TEST_CODE.replace("var answer", "foo"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("var answer", "foo"));
assert_eq!(result.messages.len(), 0);
assert!(result.fixed);
}
Expand All @@ -555,7 +556,7 @@ mod test {
create_message(replace_id(), Some(REPLACE_ID)),
create_message(no_fix(Span::default()), None),
]);
assert_eq!(result.fixed_code, TEST_CODE.replace("answer", "foo"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("answer", "foo"));
assert_eq!(result.messages.len(), 2);
assert_eq!(result.messages[0].error.to_string(), "nofix");
assert_eq!(result.messages[1].error.to_string(), "removemiddle");
Expand Down Expand Up @@ -591,7 +592,7 @@ mod test {
Message::new(no_fix_2(Span::new(1, 7)), None),
Message::new(no_fix_1(Span::new(1, 3)), None),
]);
assert_eq!(result.fixed_code, TEST_CODE.replace("answer", "foo"));
assert_eq!(result.fixed_code, TEST_CODE.cow_replace("answer", "foo"));
assert_eq!(result.messages.len(), 2);
assert_eq!(result.messages[0].error.to_string(), "nofix1");
assert_eq!(result.messages[1].error.to_string(), "nofix2");
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl NoLossOfPrecision {
} else {
format!("{value:o}")
};
!raw.ends_with(&suffix.to_uppercase())
!raw.ends_with(&suffix.cow_to_uppercase().as_ref())
}

fn base_ten_loses_precision(node: &'_ NumericLiteral) -> bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_diagnostics::OxcDiagnostic;
use oxc_semantic::SymbolFlags;
use oxc_span::{GetSpan, Span};
Expand Down Expand Up @@ -32,7 +33,7 @@ pub fn used_ignored(symbol: &Symbol<'_, '_>) -> OxcDiagnostic {

OxcDiagnostic::warn(format!("{pronoun} '{name}' is marked as ignored but is used."))
.with_label(symbol.span().label(format!("'{name}' is declared here")))
.with_help(format!("Consider renaming this {}.", pronoun.to_lowercase()))
.with_help(format!("Consider renaming this {}.", pronoun.cow_to_lowercase()))
}
/// Variable 'x' is declared but never used.
pub fn declared(symbol: &Symbol<'_, '_>) -> OxcDiagnostic {
Expand Down
13 changes: 7 additions & 6 deletions crates/oxc_linter/src/rules/eslint/sort_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
str::FromStr,
};

use cow_utils::CowUtils;
use itertools::Itertools;
use oxc_ast::{
ast::{ImportDeclaration, ImportDeclarationSpecifier, Statement},
Expand Down Expand Up @@ -210,10 +211,10 @@ impl SortImports {
let mut previous_local_member_name = get_first_local_member_name(previous);

if self.ignore_case {
current_local_member_name =
current_local_member_name.map(|name| name.to_lowercase().into());
previous_local_member_name =
previous_local_member_name.map(|name| name.to_lowercase().into());
current_local_member_name = current_local_member_name
.map(|name| Cow::Owned(name.cow_to_lowercase().into_owned()));
previous_local_member_name = previous_local_member_name
.map(|name| Cow::Owned(name.cow_to_lowercase().into_owned()));
}

// "memberSyntaxSortOrder": ["none", "all", "multiple", "single"]
Expand Down Expand Up @@ -283,7 +284,7 @@ impl SortImports {
let b = window[1].local.name.as_str();

if self.ignore_case {
a.to_lowercase() > b.to_lowercase()
a.cow_to_lowercase() > b.cow_to_lowercase()
} else {
a > b
}
Expand Down Expand Up @@ -330,7 +331,7 @@ impl SortImports {
let b = b.local.name.as_str();

if self.ignore_case {
a.to_lowercase().cmp(&b.to_lowercase())
a.cow_to_lowercase().cmp(&b.cow_to_lowercase())
} else {
a.cmp(b)
}
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_linter/src/rules/eslint/sort_vars.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{borrow::Cow, cmp::Ordering};

use cow_utils::CowUtils;
use oxc_ast::{
ast::{BindingPatternKind, VariableDeclarator},
AstKind,
Expand Down Expand Up @@ -93,7 +94,7 @@ impl SortVars {
};

if self.ignore_case {
return Cow::Owned(ident.name.to_lowercase());
return ident.name.as_str().cow_to_lowercase();
}

Cow::Borrowed(ident.name.as_str()) // avoid string allocs in the default case
Expand Down
14 changes: 8 additions & 6 deletions crates/oxc_linter/src/rules/import/no_cycle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(clippy::cast_possible_truncation)]
use std::{ffi::OsStr, path::Component, sync::Arc};

use cow_utils::CowUtils;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{CompactStr, Span};
Expand Down Expand Up @@ -142,12 +143,13 @@ impl Rule for NoCycle {
let help = stack
.iter()
.map(|(specifier, path)| {
let path = path
.strip_prefix(&cwd)
.unwrap_or(path)
.to_string_lossy()
.replace('\\', "/");
format!("-> {specifier} - {path}")
format!(
"-> {specifier} - {}",
path.strip_prefix(&cwd)
.unwrap_or(path)
.to_string_lossy()
.cow_replace('\\', "/")
)
})
.collect::<Vec<_>>()
.join("\n");
Expand Down
9 changes: 8 additions & 1 deletion crates/oxc_linter/src/rules/jest/expect_expect.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_ast::{
ast::{CallExpression, Expression, Statement},
AstKind,
Expand Down Expand Up @@ -273,7 +274,13 @@ fn convert_pattern(pattern: &str) -> String {
// request.**.expect* -> request.[a-z\\d\\.]*.expect[a-z\\d]*
let pattern = pattern
.split('.')
.map(|p| if p == "**" { String::from("[a-z\\d\\.]*") } else { p.replace('*', "[a-z\\d]*") })
.map(|p| {
if p == "**" {
String::from("[a-z\\d\\.]*")
} else {
p.cow_replace('*', "[a-z\\d]*").into_owned()
}
})
.collect::<Vec<_>>()
.join("\\.");

Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_linter/src/rules/jest/valid_title.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap, hash::Hash};

use cow_utils::CowUtils;
use oxc_ast::{
ast::{Argument, BinaryExpression, Expression},
AstKind,
Expand Down Expand Up @@ -281,7 +282,7 @@ fn validate_title(
if !valid_title.disallowed_words.is_empty() {
let Ok(disallowed_words_reg) = regex::Regex::new(&format!(
r#"(?iu)\b(?:{})\b"#,
valid_title.disallowed_words.join("|").replace('.', r"\.")
valid_title.disallowed_words.join("|").cow_replace('.', r"\.")
)) else {
return;
};
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_ast::{ast::JSXAttributeItem, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
Expand Down Expand Up @@ -52,7 +53,8 @@ declare_oxc_lint!(
impl Rule for AriaProps {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXAttributeItem(JSXAttributeItem::Attribute(attr)) = node.kind() {
let name = get_jsx_attribute_name(&attr.name).to_lowercase();
let name = get_jsx_attribute_name(&attr.name);
let name = name.cow_to_lowercase();
if name.starts_with("aria-") && !VALID_ARIA_PROPS.contains(&name) {
let suggestion = COMMON_TYPOS.get(&name).copied();
let diagnostic = aria_props_diagnostic(attr.span, &name, suggestion);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_ast::{ast::JSXAttributeItem, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
Expand Down Expand Up @@ -57,7 +58,8 @@ impl Rule for AriaUnsupportedElements {
JSXAttributeItem::Attribute(attr) => attr,
JSXAttributeItem::SpreadAttribute(_) => continue,
};
let attr_name = get_jsx_attribute_name(&attr.name).to_lowercase();
let attr_name = get_jsx_attribute_name(&attr.name);
let attr_name = attr_name.cow_to_lowercase();
if INVALID_ATTRIBUTES.contains(&attr_name) {
ctx.diagnostic_with_fix(
aria_unsupported_elements_diagnostic(attr.span, &attr_name),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_ast::{
ast::{JSXAttributeItem, JSXOpeningElement},
AstKind,
Expand Down Expand Up @@ -84,8 +85,9 @@ impl Rule for RoleSupportsAriaProps {
let invalid_props = get_invalid_aria_props_for_role(role_value);
for attr in &jsx_el.attributes {
if let JSXAttributeItem::Attribute(attr) = attr {
let name = get_jsx_attribute_name(&attr.name).to_lowercase();
if invalid_props.contains(&&name.as_str()) {
let name = get_jsx_attribute_name(&attr.name);
let name = name.cow_to_lowercase();
if invalid_props.contains(&&name.as_ref()) {
ctx.diagnostic(if is_implicit {
is_implicit_diagnostic(attr.span, &name, role_value, &el_type)
} else {
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_linter/src/rules/react/jsx_key.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cow_utils::CowUtils;
use oxc_ast::{
ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXElement, JSXFragment, Statement},
AstKind,
Expand Down Expand Up @@ -91,7 +92,7 @@ pub fn import_matcher<'a>(
actual_local_name: &'a str,
expected_module_name: &'a str,
) -> bool {
let expected_module_name = expected_module_name.to_lowercase();
let expected_module_name = expected_module_name.cow_to_lowercase();
ctx.semantic().module_record().import_entries.iter().any(|import| {
import.module_request.name().as_str() == expected_module_name
&& import.local_name.name().as_str() == actual_local_name
Expand Down
Loading

0 comments on commit d18c896

Please sign in to comment.