Skip to content

perf(linter/typescript/consistent-type-imports): remove redundant Vec per violation#24370

Merged
camc314 merged 2 commits into
oxc-project:mainfrom
macalinao:perf/consistent-type-imports
Jul 11, 2026
Merged

perf(linter/typescript/consistent-type-imports): remove redundant Vec per violation#24370
camc314 merged 2 commits into
oxc-project:mainfrom
macalinao:perf/consistent-type-imports

Conversation

@macalinao

Copy link
Copy Markdown
Contributor

What

When typescript/consistent-type-imports reports an import whose specifiers are only used as
types, run built two Vecs per violation:

let type_names = type_references_without_type_qualifier
    .iter()
    .map(|specifier| specifier.name())          // Vec<Cow<'a, str>>
    .collect::<Vec<_>>();

let type_imports = format_word_list(&type_names);
let type_names =
    type_names.iter().map(std::convert::AsRef::as_ref).collect::<Vec<_>>();  // Vec<&str>

ImportDeclarationSpecifier::name() always returns Cow::Borrowed(self.local().name.as_str()), so
the first Vec<Cow> is never anything but borrowed data, and the second collect allocates an
entire additional Vec on every violation just to convert Cow → &str for the fixer. Both the
message builder (format_word_list) and the fixer only need &str.

Change

Collect a single Vec<&str> directly from the arena-backed names and drop the second collect;
format_word_list takes &[&str]:

     let type_names = type_references_without_type_qualifier
         .iter()
-        .map(|specifier| specifier.name())
+        .map(|specifier| specifier.local().name.as_str())
         .collect::<Vec<_>>();

     let type_imports = format_word_list(&type_names);
-    let type_names =
-        type_names.iter().map(std::convert::AsRef::as_ref).collect::<Vec<_>>();
-fn format_word_list<'a>(words: &[Cow<'a, str>]) -> Cow<'a, str> {
+fn format_word_list<'a>(words: &[&'a str]) -> Cow<'a, str> {
     match words.len() {
         0 => Cow::Borrowed(""),
-        1 => words[0].clone(),
+        1 => Cow::Borrowed(words[0]),
         2 => Cow::Owned(format!("{} and {}", words[0], words[1])),

Impact

One Vec allocation removed per reported import, and the remaining Vec shrinks from
Cow<str> (24 B/elem) to &str (16 B/elem). Measured on a synthetic 9k-line TS file with 3,000
type-only import declarations (typescript/consistent-type-imports as the only enabled rule),
counting system-allocator activity during Linter::run only:

metric baseline this branch Δ
allocs/iter 21,006 18,006 −3,000 (−14%)
bytes/iter 2,555,972 2,339,972 −216,000

The −3,000 allocs/iter exactly equals the number of reported imports (one redundant Vec each).

Wall clock

This is a memory/allocation optimization, not a speed one — same class as #23751 (whose own
wall-clock delta was only −0.7% to −4%). In an isolated single-rule Criterion benchmark
(release build, tasks/benchmark, the repo's deterministic NeverGrowInPlace allocator),
the change trends slightly faster (~−5% median over 40 paired runs, fixed faster in 57%), but the effect is small and near the noise floor of a shared
machine, so no end-to-end speedup should be assumed. The reliable, deterministic win is the
reduced allocation count above (production uses mimalloc, where these small allocations are
cheap).

Verification

  • cargo test -p oxc_linter --lib consistent_type_imports passes.

This PR was assisted by Claude Code.

… per violation

The rule collected the type names twice (Vec<Cow> then Vec<&str>). `name()` is always
borrowed, so collect a single Vec<&str> and have `format_word_list` take `&[&str]`.
@macalinao macalinao marked this pull request as ready for review July 10, 2026 23:00
@macalinao macalinao requested a review from camc314 as a code owner July 10, 2026 23:00
@camc314 camc314 self-assigned this Jul 11, 2026
@camc314 camc314 added the A-linter Area - Linter label Jul 11, 2026

@camc314 camc314 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@camc314 camc314 merged commit f3ab04c into oxc-project:main Jul 11, 2026
28 checks passed
@codspeed-hq

codspeed-hq Bot commented Jul 11, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 5 untouched benchmarks
⏩ 66 skipped benchmarks1


Comparing macalinao:perf/consistent-type-imports (a03b933) with main (ddab89a)2

Open in CodSpeed

Footnotes

  1. 66 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (8da0402) during the generation of this report, so ddab89a was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Boshen added a commit that referenced this pull request Jul 14, 2026
# Oxlint
### 🚀 Features

- 0433a83 linter/eslint/no-inner-declarations: Add `namespaces` option
(#24044) (Boshen)
- 92f154a oxlint,oxfmt: Auto-discover `.mts` config files (#24357)
(camc314)
- 8c1d74b linter/import/no-duplicates: Add autofix logic (#24273) (Cole
Ellison)

### 🐛 Bug Fixes

- 0b086de linter/jest/prefer-lowercase-title: False positive when
`lowercaseFirstCharacterOnly` is false (#24414) (Connor Shea)
- 097cb95 linter: Allow `vite-plus/test` and `@effect as Vitest source
(#24196) (Liang)
- 8337835 linter: Error on `ignorePatterns` that cannot match files
aoutside the config directory (#24341) (leaysgur)
- 9ba30e5 linter/oxc/bad-replace-all-arg: Add note to enhance diagnostic
(#24346) (camc314)
- 2ce5a33 linter: Resolve `ignorePatterns` relative to the config dir
(#24339) (leaysgur)
- ab90eed linter/eslint/no-loop-func: Do not error on catch variables
(#24316) (Chris Opperwall)
- b67f0a6 linter/eslint/no-unused-vars: Count default parameter updates
as usage (#24323) (camc314)
- d193f8e linter: Detect Junie agent env vars (#24277) (Jeevan Mohan
Pawar)
- 2aecf60 linter/eslint/no-unreachable: Handle `break` in switch stmts
correctly (#24260) (camc314)

### ⚡ Performance

- 7f80cac linter/vue/prop-name-casing: Precompile `ignoreProps` regex
pattern (#24413) (connorshea)
- 6272051 linter/typescript/no-require-imports: Compile allow patterns
once (#24417) (connorshea)
- fb1edf1 linter: Compute comment fix span only for directive comments
(#24419) (connorshea)
- 33805b9 linter/jsdoc/require-param: Compile checkTypesPattern regex
once (#24420) (connorshea)
- 8de6fca linter/jest/valid-title: Compile disallowedWords regex once
(#24412) (Connor Shea)
- 4a0d8dc linter/eslint/no-underscore-dangle: Avoid String clone per
identifier (#24371) (Ian Macalinao)
- f3ab04c linter/typescript/consistent-type-imports: Remove redundant
Vec per violation (#24370) (Ian Macalinao)
- 4d2d78d linter/typescript/prefer-ts-expect-error: Avoid String clone
per comment (#24369) (Ian Macalinao)
# Oxfmt
### 🚀 Features

- 3a7fe74 formatter_css: Update oxc-css-parser to 0.0.7 (#24434)
(leaysgur)
- 0173cd3 formatter_css: Format Less :extend and merge props (#24358)
(leaysgur)
- 92f154a oxlint,oxfmt: Auto-discover `.mts` config files (#24357)
(camc314)
- df250df formatter: Support `quoteProps` for TS enum and methods
(#24309) (leaysgur)
- a9a5cd6 formatter_core: Expose `SourceText::as_str()` (#24281)
(leaysgur)

### 🐛 Bug Fixes

- 162bddf formatter: Add required parens for conditional type in type
parameter constraint (#24450) (leaysgur)
- 2d22a91 formatter: Determine type cast target from span instead of
lexical scan (#24447) (leaysgur)
- 25306e9 formatter: Do not add extra parens with type cast comment
(#24444) (leaysgur)
- bd6edfe formatter: Break arrow signature that exactly fills the line
when cond body may hug (#24440) (leaysgur)
- a99ef41 formatter: Keep quotes on method signature named new (#24432)
(leaysgur)
- fcc28df formatter_css: Keep glued-braket-value tight (#24352)
(leaysgur)
- 8337835 linter: Error on `ignorePatterns` that cannot match files
aoutside the config directory (#24341) (leaysgur)
- b7c7e15 formatter: Add parens for import and private field in new
callee chain (#24320) (leaysgur)
- 0c8f6e4 formatter: Update detect_code_removal for #24309 (#24314)
(leaysgur)
- a85aad0 formatter: Fix member-chain and non-null parens (#24312)
(leaysgur)
- 1c29c73 formatter: Preserve `TSNonNullExpression` in chain expression
(#24311) (leaysgur)
- 8933c0e formatter: Keep comment inside of empty `switch` block
(#24308) (leaysgur)
- ec26af2 formatter: Preserve blank lines between JSX attrs (#24290)
(leaysgur)
- 70bd54d formatter: Keep arrow function body comment (#24287)
(leaysgur)
- 415fe1e oxfmt: Error on ignorePatterns that cannot match files outside
the config directory (#24286) (leaysgur)
- eeabc4a formatter_css: Bail on EOF-recovered parse errors (#24282)
(leaysgur)
- 42ec8de formatter: Keep comments inside surviving parens and
suppressed statement terminators (#24253) (leaysgur)
- 1343779 formatter: Keep comment inline for empty statements (#24249)
(leaysgur)
- b996579 formatter: Print ; before trailing comments part 2 (#24246)
(leaysgur)
- 4f86e8c formatter: Print `;` before trailing comments (#24244)
(leaysgur)
- 01252e4 formatter: Add or remove parens for `let` declaration (#24215)
(leaysgur)

### ⚡ Performance

- eeb1913 formatter_core: Avoid per-call `Vec` work-stack in soft-line
removal (#23775) (Marius Schulz)
- a2f255b formatter: Use `SmallVec` for `MemberChain` collections
(#23776) (Marius Schulz)

### 📚 Documentation

- b52d0f5 formatter: Add TODO comment about unsound code (#24372)
(overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-linter Area - Linter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants