From d81dfbaed013231c800ac34829d96857d862d38c Mon Sep 17 00:00:00 2001 From: Cam McHenry Date: Mon, 20 Oct 2025 23:45:22 -0400 Subject: [PATCH] perf(linter): allocate strings with some initial capacity --- .../rules/eslint/class_methods_use_this.rs | 15 +++++++------- .../src/rules/eslint/no_loss_of_precision.rs | 2 +- .../src/rules/eslint/sort_imports.rs | 3 ++- .../oxc_linter/src/rules/eslint/sort_keys.rs | 2 +- .../import/consistent_type_specifier_style.rs | 2 +- .../src/rules/react/jsx_fragments.rs | 14 +++++++++++-- .../typescript/consistent_type_definitions.rs | 20 +++++++++---------- .../rules/unicorn/prefer_modern_math_apis.rs | 2 +- 8 files changed, 36 insertions(+), 24 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs b/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs index 035883c66f969..6d0a13d9c1ea9 100644 --- a/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs +++ b/crates/oxc_linter/src/rules/eslint/class_methods_use_this.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, ops::Deref}; +use std::ops::Deref; use itertools::Itertools; use oxc_ast::{ @@ -13,10 +13,9 @@ use oxc_span::{CompactStr, GetSpan, Span}; use crate::{LintContext, rule::Rule}; -fn class_methods_use_this_diagnostic(span: Span, name: Option>) -> OxcDiagnostic { - let method_name_str = name.map_or(String::new(), |name| format!(" `{name}`")); - OxcDiagnostic::warn(format!("Expected method{method_name_str} to have this.")) - .with_help(format!("Consider converting method{method_name_str} to a static method.")) +fn class_methods_use_this_diagnostic(span: Span, name: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Expected method `{name}` to have this.")) + .with_help(format!("Consider converting method `{name}` to a static method.")) .with_label(span) } @@ -219,8 +218,10 @@ impl Rule for ClassMethodsUseThis { } let mut finder = ThisFinder::new(); finder.visit_function_body(function_body); - if !finder.has_this { - ctx.diagnostic(class_methods_use_this_diagnostic(name.span(), name.name())); + if !finder.has_this + && let Some(name_str) = name.name() + { + ctx.diagnostic(class_methods_use_this_diagnostic(name.span(), &name_str)); } } } 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 ab5e3d0103bb2..50921f0a31aff 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 @@ -370,7 +370,7 @@ pub fn to_precision(mut num: f64, precision: usize) -> String { let precision_i32 = precision as i32; // Handle sign - let mut prefix = String::new(); + let mut prefix = String::with_capacity(8); if num < 0.0 { prefix.push('-'); num = -num; diff --git a/crates/oxc_linter/src/rules/eslint/sort_imports.rs b/crates/oxc_linter/src/rules/eslint/sort_imports.rs index 44692b7ebfdbb..0cb05f6df96f7 100644 --- a/crates/oxc_linter/src/rules/eslint/sort_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/sort_imports.rs @@ -322,6 +322,7 @@ impl SortImports { // add a empty string for zip with specifiers paddings.push(""); + let specifiers_len = specifiers.len(); let specifiers = specifiers.iter().sorted_by(|a, b| { let a = a.local.name.as_str(); let b = b.local.name.as_str(); @@ -334,7 +335,7 @@ impl SortImports { }); let sorted_text = specifiers.zip(paddings).fold( - String::new(), + String::with_capacity(specifiers_len * 8), |mut acc, (specifier, padding)| { let _ = acc.write_str(ctx.source_range(specifier.span)); let _ = acc.write_str(padding); diff --git a/crates/oxc_linter/src/rules/eslint/sort_keys.rs b/crates/oxc_linter/src/rules/eslint/sort_keys.rs index e93b9723d5936..351bc3168e1b6 100644 --- a/crates/oxc_linter/src/rules/eslint/sort_keys.rs +++ b/crates/oxc_linter/src/rules/eslint/sort_keys.rs @@ -291,7 +291,7 @@ impl Rule for SortKeys { // trailing comma) to the end, we must remove their trailing comma so // the resulting object doesn't end up with an extra comma before `}`. // Also normalize separators between properties to `, ` for clarity. - let mut sorted_text = String::new(); + let mut sorted_text = String::with_capacity(8 * indices.len()); // initialize with avg 8 chars per prop let trim_end_commas = |s: &str| -> String { let s = s.trim_end(); let mut trimmed = s.to_string(); diff --git a/crates/oxc_linter/src/rules/import/consistent_type_specifier_style.rs b/crates/oxc_linter/src/rules/import/consistent_type_specifier_style.rs index 0b48344084aa9..6ad530f8796ee 100644 --- a/crates/oxc_linter/src/rules/import/consistent_type_specifier_style.rs +++ b/crates/oxc_linter/src/rules/import/consistent_type_specifier_style.rs @@ -115,7 +115,7 @@ impl Rule for ConsistentTypeSpecifierStyle { ctx.diagnostic_with_fix( consistent_type_specifier_style_diagnostic(item.span(), &self.mode), |fixer| { - let mut import_source = String::new(); + let mut import_source = String::with_capacity(128); if !value_specifiers.is_empty() { let value_import_declaration = diff --git a/crates/oxc_linter/src/rules/react/jsx_fragments.rs b/crates/oxc_linter/src/rules/react/jsx_fragments.rs index 4a1224d6c967b..ad41d303a67ad 100644 --- a/crates/oxc_linter/src/rules/react/jsx_fragments.rs +++ b/crates/oxc_linter/src/rules/react/jsx_fragments.rs @@ -130,7 +130,12 @@ impl Rule for JsxFragments { closing_element.span().end, jsx_elem.span().end, )); - let mut replacement = String::new(); + let mut replacement = String::with_capacity( + before_opening_tag.len() + + between_opening_tag_and_closing_tag.len() + + after_closing_tag.len() + + "<>".len(), + ); replacement.push_str(before_opening_tag); replacement.push_str("<>"); replacement.push_str(between_opening_tag_and_closing_tag); @@ -156,7 +161,12 @@ impl Rule for JsxFragments { jsx_frag.closing_fragment.span().end, jsx_frag.span().end, )); - let mut replacement = String::new(); + let mut replacement = String::with_capacity( + before_opening_tag.len() + + between_opening_tag_and_closing_tag.len() + + after_closing_tag.len() + + "".len(), + ); replacement.push_str(before_opening_tag); replacement.push_str(""); replacement.push_str(between_opening_tag_and_closing_tag); diff --git a/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs b/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs index 3e09ebe369157..898f4bb4b9ed2 100644 --- a/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs +++ b/crates/oxc_linter/src/rules/typescript/consistent_type_definitions.rs @@ -174,11 +174,6 @@ impl Rule for ConsistentTypeDefinitions { let body_span = &decl.body.span; let body = &ctx.source_text()[body_span.start as usize..body_span.end as usize]; - let mut extends = String::new(); - for exp in &decl.extends { - write!(extends, " & {}", exp.span.source_text(ctx.source_text())).unwrap(); - } - ctx.diagnostic_with_fix( consistent_type_definitions_diagnostic( "type", @@ -186,6 +181,11 @@ impl Rule for ConsistentTypeDefinitions { Span::sized(decl.span.start, 9), ), |fixer| { + let mut extends = String::with_capacity(8 * decl.extends.len()); + for exp in &decl.extends { + write!(extends, " & {}", exp.span.source_text(ctx.source_text())) + .unwrap(); + } fixer.replace( exp.span, format!("type {name} = {body}{extends}\nexport default {name}"), @@ -220,11 +220,6 @@ impl Rule for ConsistentTypeDefinitions { let body_span = &decl.body.span; let body = &ctx.source_text()[body_span.start as usize..body_span.end as usize]; - let mut extends = String::new(); - for exp in &decl.extends { - write!(extends, " & {}", exp.span.source_text(ctx.source_text())).unwrap(); - } - ctx.diagnostic_with_fix( consistent_type_definitions_diagnostic( "type", @@ -232,6 +227,11 @@ impl Rule for ConsistentTypeDefinitions { Span::sized(start, 9), ), |fixer| { + let mut extends = String::with_capacity(8 * decl.extends.len()); + for exp in &decl.extends { + write!(extends, " & {}", exp.span.source_text(ctx.source_text())) + .unwrap(); + } fixer.replace( Span::new(start, decl.span.end), format!("type {name} = {body}{extends}"), diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs b/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs index 810e208f58ad6..0420121e28f41 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_modern_math_apis.rs @@ -262,7 +262,7 @@ fn is_pow_2_expression(expression: &Expression, ctx: &LintContext<'_>) -> bool { /// removes any newlines from the string /// removes any duplicate spaces fn clean_string(input: &str) -> String { - let mut result = String::new(); + let mut result = String::with_capacity(input.len()); let mut prev_char = ' '; for c in input.chars() {