diff --git a/.clippy.toml b/.clippy.toml index 281aac36299b8..ec32d7f271626 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -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." }, ] diff --git a/Cargo.lock b/Cargo.lock index b95fc3b508a33..e5219370b6d36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1525,6 +1525,7 @@ version = "0.28.0" dependencies = [ "base64", "bitflags 2.6.0", + "cow-utils", "daachorse", "insta", "nonmax", @@ -1692,6 +1693,7 @@ dependencies = [ name = "oxc_minifier" version = "0.28.0" dependencies = [ + "cow-utils", "insta", "num-bigint", "num-traits", @@ -1738,6 +1740,7 @@ version = "0.28.0" dependencies = [ "assert-unchecked", "bitflags 2.6.0", + "cow-utils", "memchr", "num-bigint", "num-traits", @@ -1871,6 +1874,7 @@ version = "0.28.0" dependencies = [ "base64-simd", "cfg-if", + "cow-utils", "rayon", "rustc-hash", "serde", @@ -1927,6 +1931,7 @@ dependencies = [ name = "oxc_transform_conformance" version = "0.0.0" dependencies = [ + "cow-utils", "indexmap", "oxc", "oxc_tasks_common", diff --git a/crates/oxc_codegen/Cargo.toml b/crates/oxc_codegen/Cargo.toml index 17f7f5588497e..096b33b8023f3 100644 --- a/crates/oxc_codegen/Cargo.toml +++ b/crates/oxc_codegen/Cargo.toml @@ -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 } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 57a9f0c2e161b..4a6ada2ab1765 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -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::*; @@ -1232,7 +1233,7 @@ impl<'a> Gen for RegExpLiteral<'a> { ); // Avoid forming a single-line comment or " Fixer<'a> { #[cfg(test)] mod test { + use cow_utils::CowUtils; use std::borrow::Cow; use oxc_diagnostics::OxcDiagnostic; @@ -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); } @@ -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 ) ); @@ -452,7 +453,7 @@ 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); } @@ -460,7 +461,7 @@ mod test { #[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); } @@ -468,7 +469,7 @@ mod test { #[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); } @@ -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); } @@ -500,7 +501,7 @@ 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); } @@ -508,7 +509,7 @@ mod test { #[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); } @@ -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); @@ -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); } @@ -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"); @@ -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"); diff --git a/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs b/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs index b5434ead7e86f..252ba13651f1a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs +++ b/crates/oxc_linter/src/rules/eslint/no_loss_of_precision.rs @@ -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 { diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/diagnostic.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/diagnostic.rs index 0f5fff6b8e4c2..0efb8538654d5 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/diagnostic.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/diagnostic.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_diagnostics::OxcDiagnostic; use oxc_semantic::SymbolFlags; use oxc_span::{GetSpan, Span}; @@ -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 { diff --git a/crates/oxc_linter/src/rules/eslint/sort_imports.rs b/crates/oxc_linter/src/rules/eslint/sort_imports.rs index 5d9fb80c5079d..b5473af98bb80 100644 --- a/crates/oxc_linter/src/rules/eslint/sort_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/sort_imports.rs @@ -4,6 +4,7 @@ use std::{ str::FromStr, }; +use cow_utils::CowUtils; use itertools::Itertools; use oxc_ast::{ ast::{ImportDeclaration, ImportDeclarationSpecifier, Statement}, @@ -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"] @@ -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 } @@ -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) } diff --git a/crates/oxc_linter/src/rules/eslint/sort_vars.rs b/crates/oxc_linter/src/rules/eslint/sort_vars.rs index e3db7707c00da..3fc2fe23dd262 100644 --- a/crates/oxc_linter/src/rules/eslint/sort_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/sort_vars.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, cmp::Ordering}; +use cow_utils::CowUtils; use oxc_ast::{ ast::{BindingPatternKind, VariableDeclarator}, AstKind, @@ -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 diff --git a/crates/oxc_linter/src/rules/import/no_cycle.rs b/crates/oxc_linter/src/rules/import/no_cycle.rs index f96e7fa587a87..aaf291be327a8 100644 --- a/crates/oxc_linter/src/rules/import/no_cycle.rs +++ b/crates/oxc_linter/src/rules/import/no_cycle.rs @@ -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}; @@ -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::>() .join("\n"); diff --git a/crates/oxc_linter/src/rules/jest/expect_expect.rs b/crates/oxc_linter/src/rules/jest/expect_expect.rs index 204671136a544..cbb5d45eaa01a 100644 --- a/crates/oxc_linter/src/rules/jest/expect_expect.rs +++ b/crates/oxc_linter/src/rules/jest/expect_expect.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ ast::{CallExpression, Expression, Statement}, AstKind, @@ -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::>() .join("\\."); diff --git a/crates/oxc_linter/src/rules/jest/valid_title.rs b/crates/oxc_linter/src/rules/jest/valid_title.rs index 6e812f4a50a14..3ae406d804d28 100644 --- a/crates/oxc_linter/src/rules/jest/valid_title.rs +++ b/crates/oxc_linter/src/rules/jest/valid_title.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, hash::Hash}; +use cow_utils::CowUtils; use oxc_ast::{ ast::{Argument, BinaryExpression, Expression}, AstKind, @@ -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; }; diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs index 3c4671eef32be..8ec8bfc051059 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ast::JSXAttributeItem, AstKind}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -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); diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs index 709dc3f6f41ff..83728d989652f 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ast::JSXAttributeItem, AstKind}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -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), diff --git a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs index cbcb867050054..d81d4547ab223 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ ast::{JSXAttributeItem, JSXOpeningElement}, AstKind, @@ -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 { diff --git a/crates/oxc_linter/src/rules/react/jsx_key.rs b/crates/oxc_linter/src/rules/react/jsx_key.rs index 866f92872e9fd..7f23ec8ec66ea 100644 --- a/crates/oxc_linter/src/rules/react/jsx_key.rs +++ b/crates/oxc_linter/src/rules/react/jsx_key.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXElement, JSXFragment, Statement}, AstKind, @@ -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 diff --git a/crates/oxc_linter/src/rules/react/no_unknown_property.rs b/crates/oxc_linter/src/rules/react/no_unknown_property.rs index 0afb07bd50298..cb2618809658b 100644 --- a/crates/oxc_linter/src/rules/react/no_unknown_property.rs +++ b/crates/oxc_linter/src/rules/react/no_unknown_property.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, collections::hash_map::HashMap}; +use cow_utils::CowUtils; use itertools::Itertools; use once_cell::sync::Lazy; use oxc_ast::{ @@ -421,7 +422,10 @@ const DOM_PROPERTIES_IGNORE_CASE: [&str; 5] = [ ]; static DOM_PROPERTIES_LOWER_MAP: Lazy> = Lazy::new(|| { - DOM_PROPERTIES_NAMES.iter().map(|it| (it.to_lowercase(), *it)).collect::>() + DOM_PROPERTIES_NAMES + .iter() + .map(|it| (it.cow_to_lowercase().into_owned(), *it)) + .collect::>() }); /// @@ -433,7 +437,7 @@ static DOM_PROPERTIES_LOWER_MAP: Lazy> = Lazy::new fn is_valid_data_attr(name: &str) -> bool { static DATA_ATTR_REGEX: Lazy = Lazy::new(|| Regex::new(r"^data(-?[^:]*)$").unwrap()); - !name.to_lowercase().starts_with("data-xml") && DATA_ATTR_REGEX.is_match(name) + !name.cow_to_lowercase().starts_with("data-xml") && DATA_ATTR_REGEX.is_match(name) } fn normalize_attribute_case(name: &str) -> &str { @@ -496,7 +500,10 @@ impl Rule for NoUnknownProperty { }; if is_valid_data_attr(&actual_name) { if self.0.require_data_lowercase && has_uppercase(&actual_name) { - ctx.diagnostic(data_lowercase_required(span, &actual_name.to_lowercase())); + ctx.diagnostic(data_lowercase_required( + span, + &actual_name.cow_to_lowercase(), + )); } return; }; @@ -520,7 +527,7 @@ impl Rule for NoUnknownProperty { } DOM_PROPERTIES_LOWER_MAP - .get(&name.to_lowercase()) + .get(&name.cow_to_lowercase().into_owned()) .or_else(|| DOM_ATTRIBUTES_TO_CAMEL.get(name)) .map_or_else( || { diff --git a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs index c22db1bea7c02..2c4ae6c3fb1b4 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::CommentKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -178,7 +179,7 @@ impl Rule for BanTsComment { |fixer| { fixer.replace( comm.span, - raw.replace("@ts-ignore", "@ts-expect-error"), + raw.cow_replace("@ts-ignore", "@ts-expect-error"), ) }, ); diff --git a/crates/oxc_linter/src/rules/typescript/ban_types.rs b/crates/oxc_linter/src/rules/typescript/ban_types.rs index 9e1f5952d79e9..81c18b79588f0 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_types.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_types.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -65,7 +66,7 @@ impl Rule for BanTypes { "String" | "Boolean" | "Number" | "Symbol" | "BigInt" => { ctx.diagnostic(type_diagnostic( name.as_str(), - &name.to_lowercase(), + &name.as_str().cow_to_lowercase(), typ.span, )); } diff --git a/crates/oxc_linter/src/rules/typescript/no_wrapper_object_types.rs b/crates/oxc_linter/src/rules/typescript/no_wrapper_object_types.rs index 325264cc62b30..f8788f29de9c0 100644 --- a/crates/oxc_linter/src/rules/typescript/no_wrapper_object_types.rs +++ b/crates/oxc_linter/src/rules/typescript/no_wrapper_object_types.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::{ ast::{Expression, TSTypeName}, AstKind, @@ -91,7 +92,7 @@ impl Rule for NoWrapperObjectTypes { if can_fix { ctx.diagnostic_with_fix(no_wrapper_object_types(ident_span), |fixer| { - fixer.replace(ident_span, ident_name.to_lowercase()) + fixer.replace(ident_span, ident_name.cow_to_lowercase()) }); } else { ctx.diagnostic(no_wrapper_object_types(ident_span)); diff --git a/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs b/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs index 808505afb1340..251ab57ef46ff 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_ts_expect_error.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::CommentKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -58,7 +59,7 @@ impl Rule for PreferTsExpectError { ctx.diagnostic_with_fix(prefer_ts_expect_error_diagnostic(comment_span), |fixer| { fixer.replace( comment_span, - format!("//{}", raw.replace("@ts-ignore", "@ts-expect-error")), + format!("//{}", raw.cow_replace("@ts-ignore", "@ts-expect-error")), ) }); } else { @@ -66,7 +67,7 @@ impl Rule for PreferTsExpectError { ctx.diagnostic_with_fix(prefer_ts_expect_error_diagnostic(comment_span), |fixer| { fixer.replace( comment_span, - format!("/*{}*/", raw.replace("@ts-ignore", "@ts-expect-error")), + format!("/*{}*/", raw.cow_replace("@ts-ignore", "@ts-expect-error")), ) }); } diff --git a/crates/oxc_linter/src/rules/unicorn/number_literal_case.rs b/crates/oxc_linter/src/rules/unicorn/number_literal_case.rs index 8ea26a6658d3a..2239b6d246fba 100644 --- a/crates/oxc_linter/src/rules/unicorn/number_literal_case.rs +++ b/crates/oxc_linter/src/rules/unicorn/number_literal_case.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -101,7 +102,7 @@ fn check_number_literal(number_literal: &str, raw_span: Span) -> Option<(OxcDiag Span::new(raw_span.start + 1, raw_span.start + 2), if number_literal.starts_with("0B") { "0b" } else { "0o" }, ), - number_literal.to_lowercase(), + number_literal.cow_to_lowercase().into_owned(), )); } if number_literal.starts_with("0X") || number_literal.starts_with("0x") { @@ -131,14 +132,14 @@ fn check_number_literal(number_literal: &str, raw_span: Span) -> Option<(OxcDiag let char_position = raw_span.start + index as u32; return Some(( uppercase_exponential_notation(Span::new(char_position, char_position + 1)), - number_literal.to_lowercase(), + number_literal.cow_to_lowercase().into_owned(), )); } None } fn digits_to_uppercase(digits: &str) -> String { - let mut result = digits.to_uppercase(); + let mut result = digits.cow_to_uppercase().into_owned(); if result.ends_with('N') { result.truncate(result.len() - 1); result.push('n'); diff --git a/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs b/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs index 3609a7dbfbe65..d03d3a17f70ed 100644 --- a/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs +++ b/crates/oxc_linter/src/rules/unicorn/numeric_separators_style.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use lazy_static::lazy_static; use oxc_ast::{ ast::{BigIntLiteral, NumericLiteral}, @@ -177,7 +178,7 @@ impl NumericSeparatorsStyle { fn format_binary(&self, raw_number: &str) -> String { let prefix = &raw_number[0..2]; - let mut to_format = raw_number[2..].replace('_', ""); + let mut to_format = raw_number[2..].cow_replace('_', "").into_owned(); add_separators(&mut to_format, &SeparatorDir::Right, &self.binary); to_format.insert_str(0, prefix); @@ -187,7 +188,7 @@ impl NumericSeparatorsStyle { fn format_hex(&self, number_raw: &str) -> String { let prefix = &number_raw[0..2]; - let mut to_format = number_raw[2..].replace('_', ""); + let mut to_format = number_raw[2..].cow_replace('_', "").into_owned(); add_separators(&mut to_format, &SeparatorDir::Right, &self.hexadecimal); to_format.insert_str(0, prefix); @@ -204,7 +205,7 @@ impl NumericSeparatorsStyle { let prefix = &number_raw[0..2]; - let mut to_format = number_raw[2..].replace('_', ""); + let mut to_format = number_raw[2..].cow_replace('_', "").into_owned(); add_separators(&mut to_format, &SeparatorDir::Right, &self.octal); to_format.insert_str(0, prefix); @@ -222,7 +223,7 @@ impl NumericSeparatorsStyle { let mut out = String::new(); { - let number = caps.get(1).unwrap().as_str().replace('_', ""); + let number = caps.get(1).unwrap().as_str().cow_replace('_', "").into_owned(); if let Some((whole, decimal)) = number.split_once('.') { if !whole.is_empty() { @@ -251,7 +252,7 @@ impl NumericSeparatorsStyle { out.push_str(sign.as_str()); } if let Some(power) = caps.get(4) { - let mut s = power.as_str().replace('_', ""); + let mut s = power.as_str().cow_replace('_', "").into_owned(); add_separators(&mut s, &SeparatorDir::Right, &self.number); out.push_str(&s); } diff --git a/crates/oxc_linter/src/tester.rs b/crates/oxc_linter/src/tester.rs index 9876d39720823..2b3ed64daf7ed 100644 --- a/crates/oxc_linter/src/tester.rs +++ b/crates/oxc_linter/src/tester.rs @@ -3,6 +3,7 @@ use std::{ path::{Path, PathBuf}, }; +use cow_utils::CowUtils; use oxc_allocator::Allocator; use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, GraphicalTheme, NamedSource}; use serde::Deserialize; @@ -185,7 +186,8 @@ impl Tester { expect_pass: Vec, expect_fail: Vec, ) -> Self { - let rule_path = PathBuf::from(rule_name.replace('-', "_")).with_extension("tsx"); + let rule_path = + PathBuf::from(rule_name.cow_replace('-', "_").into_owned()).with_extension("tsx"); let expect_pass = expect_pass.into_iter().map(Into::into).collect::>(); let expect_fail = expect_fail.into_iter().map(Into::into).collect::>(); let current_working_directory = @@ -294,7 +296,7 @@ impl Tester { } fn snapshot(&self) { - let name = self.rule_name.replace('-', "_"); + let name = self.rule_name.cow_replace('-', "_"); let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); @@ -304,7 +306,7 @@ impl Tester { } settings.bind(|| { - insta::assert_snapshot!(name, self.snapshot); + insta::assert_snapshot!(name.as_ref(), self.snapshot); }); } diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index 2a892b093de4f..64d343ce905be 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -21,6 +21,7 @@ test = false doctest = false [dependencies] +cow-utils = { workspace = true } oxc_allocator = { workspace = true } oxc_ast = { workspace = true } oxc_codegen = { workspace = true } diff --git a/crates/oxc_minifier/src/plugins/inject_global_variables.rs b/crates/oxc_minifier/src/plugins/inject_global_variables.rs index f05fa33e7f874..41c3be918cf3c 100644 --- a/crates/oxc_minifier/src/plugins/inject_global_variables.rs +++ b/crates/oxc_minifier/src/plugins/inject_global_variables.rs @@ -1,9 +1,9 @@ -use std::sync::Arc; - +use cow_utils::CowUtils; use oxc_allocator::Allocator; use oxc_ast::{ast::*, visit::walk_mut, AstBuilder, VisitMut}; use oxc_semantic::{ScopeTree, SymbolTable}; use oxc_span::{CompactStr, SPAN}; +use std::sync::Arc; use super::replace_global_defines::{DotDefine, ReplaceGlobalDefines}; @@ -58,7 +58,7 @@ impl InjectImport { fn replace_name(local: &str) -> Option { local .contains('.') - .then(|| CompactStr::from(format!("$inject_{}", local.replace('.', "_")))) + .then(|| CompactStr::from(format!("$inject_{}", local.cow_replace('.', "_")))) } } diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 1301aa12c44d3..0d0a2b6a66fd4 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -28,6 +28,7 @@ oxc_syntax = { workspace = true } assert-unchecked = { workspace = true } bitflags = { workspace = true } +cow-utils = { workspace = true } num-bigint = { workspace = true } num-traits = { workspace = true } rustc-hash = { workspace = true } diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 63a0c360de6b6..fe02caf59d326 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -1,3 +1,4 @@ +use cow_utils::CowUtils; use oxc_allocator::Box; use oxc_ast::ast::*; use oxc_diagnostics::Result; @@ -517,7 +518,7 @@ impl<'a> ParserImpl<'a> { let cur_src = self.cur_src(); let raw = &cur_src[1..cur_src.len() - end_offset as usize]; let raw = Atom::from(if cooked.is_some() && raw.contains('\r') { - self.ast.str(raw.replace("\r\n", "\n").replace('\r', "\n").as_str()) + self.ast.str(&raw.cow_replace("\r\n", "\n").cow_replace('\r', "\n")) } else { raw }); diff --git a/crates/oxc_parser/src/lexer/number.rs b/crates/oxc_parser/src/lexer/number.rs index 887d90c3d9a2f..c69df154f49fd 100644 --- a/crates/oxc_parser/src/lexer/number.rs +++ b/crates/oxc_parser/src/lexer/number.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; +use cow_utils::CowUtils; use num_bigint::BigInt; use num_traits::Num; @@ -40,7 +41,7 @@ pub fn parse_int(s: &str, kind: Kind, has_sep: bool) -> Result Result { - let s = if has_sep { Cow::Owned(s.replace('_', "")) } else { Cow::Borrowed(s) }; + let s = if has_sep { s.cow_replace('_', "") } else { Cow::Borrowed(s) }; debug_assert!(!s.contains('_')); s.parse::().map_err(|_| "invalid float") } @@ -93,7 +94,7 @@ fn parse_decimal_with_underscores(s: &str) -> f64 { debug_assert!(!s.is_empty()); if s.len() > MAX_FAST_DECIMAL_LEN { - return parse_decimal_slow(&s.replace('_', "")); + return parse_decimal_slow(&s.cow_replace('_', "")); } let mut result = 0_u64; @@ -391,7 +392,7 @@ fn parse_hex_with_underscores_slow(s: &str) -> f64 { // ==================================== BIGINT ==================================== pub fn parse_big_int(s: &str, kind: Kind, has_sep: bool) -> Result { - let s = if has_sep { Cow::Owned(s.replace('_', "")) } else { Cow::Borrowed(s) }; + let s = if has_sep { s.cow_replace('_', "") } else { Cow::Borrowed(s) }; debug_assert!(!s.contains('_')); parse_big_int_without_underscores(&s, kind) } diff --git a/crates/oxc_sourcemap/Cargo.toml b/crates/oxc_sourcemap/Cargo.toml index c3ca0890ef458..ecc36c5fdf4a1 100644 --- a/crates/oxc_sourcemap/Cargo.toml +++ b/crates/oxc_sourcemap/Cargo.toml @@ -21,6 +21,7 @@ doctest = false [dependencies] base64-simd = { workspace = true } cfg-if = { workspace = true } +cow-utils = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/crates/oxc_sourcemap/src/sourcemap_visualizer.rs b/crates/oxc_sourcemap/src/sourcemap_visualizer.rs index 6ee463ee6da75..e5b426297f546 100644 --- a/crates/oxc_sourcemap/src/sourcemap_visualizer.rs +++ b/crates/oxc_sourcemap/src/sourcemap_visualizer.rs @@ -1,6 +1,9 @@ +use std::borrow::Cow; + use rustc_hash::FxHashMap; use crate::SourceMap; +use cow_utils::CowUtils; /// The `SourcemapVisualizer` is a helper for sourcemap testing. /// It print the mapping of original content and final content tokens. @@ -151,16 +154,18 @@ impl<'a> SourcemapVisualizer<'a> { tables } - fn str_slice_by_token(buff: &[Vec], start: (u32, u32), end: (u32, u32)) -> String { + fn str_slice_by_token(buff: &[Vec], start: (u32, u32), end: (u32, u32)) -> Cow<'_, str> { if start.0 == end.0 { if start.1 <= end.1 { - return String::from_utf16( - &buff[start.0 as usize][start.1 as usize..end.1 as usize], - ) - .unwrap(); + return Cow::Owned( + String::from_utf16(&buff[start.0 as usize][start.1 as usize..end.1 as usize]) + .unwrap(), + ); } - return String::from_utf16(&buff[start.0 as usize][end.1 as usize..start.1 as usize]) - .unwrap(); + return Cow::Owned( + String::from_utf16(&buff[start.0 as usize][end.1 as usize..start.1 as usize]) + .unwrap(), + ); } let mut s = String::new(); @@ -175,8 +180,10 @@ impl<'a> SourcemapVisualizer<'a> { } } + let replaced: Cow = s.cow_replace("\r", ""); + // Windows: Replace "\r\n" and replace with "\n" - s.replace('\r', "") + Cow::Owned(replaced.into_owned()) } } diff --git a/tasks/ast_tools/src/main.rs b/tasks/ast_tools/src/main.rs index 2bbc84f2e0b77..6e8dcf94ed346 100644 --- a/tasks/ast_tools/src/main.rs +++ b/tasks/ast_tools/src/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::disallowed_methods)] use std::{cell::RefCell, io::Read, path::PathBuf, rc::Rc}; use bpaf::{Bpaf, Parser}; diff --git a/tasks/benchmark/benches/lexer.rs b/tasks/benchmark/benches/lexer.rs index 7a95125f6c461..e0aebdefdcf17 100644 --- a/tasks/benchmark/benches/lexer.rs +++ b/tasks/benchmark/benches/lexer.rs @@ -1,3 +1,4 @@ +#![allow(clippy::disallowed_methods)] use oxc_allocator::Allocator; use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion}; use oxc_parser::lexer::{Kind, Lexer}; diff --git a/tasks/common/src/lib.rs b/tasks/common/src/lib.rs index cf0d48b460394..530494b0ca685 100644 --- a/tasks/common/src/lib.rs +++ b/tasks/common/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::print_stdout)] +#![allow(clippy::print_stdout, clippy::disallowed_methods)] use std::path::{Path, PathBuf}; mod diff; diff --git a/tasks/coverage/src/lib.rs b/tasks/coverage/src/lib.rs index 35ecdda830dd2..56eced1bd9934 100644 --- a/tasks/coverage/src/lib.rs +++ b/tasks/coverage/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::print_stdout, clippy::print_stderr)] +#![allow(clippy::print_stdout, clippy::print_stderr, clippy::disallowed_methods)] // Core mod runtime; mod suite; diff --git a/tasks/prettier_conformance/src/lib.rs b/tasks/prettier_conformance/src/lib.rs index c5fe9710eea66..9dc0009fc4732 100644 --- a/tasks/prettier_conformance/src/lib.rs +++ b/tasks/prettier_conformance/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::print_stdout, clippy::print_stderr)] +#![allow(clippy::print_stdout, clippy::print_stderr, clippy::disallowed_methods)] mod ignore_list; mod spec; diff --git a/tasks/rulegen/src/main.rs b/tasks/rulegen/src/main.rs index 2a0bc4fb47f31..1a5b5641b2a3d 100644 --- a/tasks/rulegen/src/main.rs +++ b/tasks/rulegen/src/main.rs @@ -1,4 +1,4 @@ -#![allow(clippy::print_stdout, clippy::print_stderr)] +#![allow(clippy::print_stdout, clippy::print_stderr, clippy::disallowed_methods)] use std::{ borrow::Cow, collections::HashMap, diff --git a/tasks/transform_conformance/Cargo.toml b/tasks/transform_conformance/Cargo.toml index 968596ad705b9..356c7983ca862 100644 --- a/tasks/transform_conformance/Cargo.toml +++ b/tasks/transform_conformance/Cargo.toml @@ -25,6 +25,7 @@ doctest = false oxc = { workspace = true, features = ["full"] } oxc_tasks_common = { workspace = true } +cow-utils = { workspace = true } indexmap = { workspace = true } pico-args = { workspace = true } walkdir = { workspace = true } diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 6bf7f1b6289cd..15bb97973ea9a 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -3,6 +3,7 @@ use std::{ path::{Path, PathBuf}, }; +use cow_utils::CowUtils; use oxc::{ allocator::Allocator, codegen::CodeGenerator, @@ -288,7 +289,7 @@ impl TestCase for ConformanceTestCase { let output; let passed = if let Some(throws) = &babel_options.throws { - output = throws.to_string().replace(" (1:6)", ""); + output = throws.cow_replace(" (1:6)", "").into_owned(); !output.is_empty() && actual_errors.as_ref().is_some_and(|errors| errors.contains(&output)) } else { diff --git a/tasks/website/src/lib.rs b/tasks/website/src/lib.rs index 505bfc1460852..18887d6521068 100644 --- a/tasks/website/src/lib.rs +++ b/tasks/website/src/lib.rs @@ -1,3 +1,8 @@ -#![allow(clippy::print_stdout, clippy::print_stderr, clippy::missing_panics_doc)] +#![allow( + clippy::print_stdout, + clippy::print_stderr, + clippy::missing_panics_doc, + clippy::disallowed_methods +)] pub mod linter;