Skip to content

linter: Improve no_unused_vars fixer to handle all cases #16832

@camc314

Description

@camc314

Overview

The no_unused_vars rule has a partial but functional fixer implementation. Currently, all fixes are classified as "dangerous suggestions" because they modify code semantics. This meta issue tracks improvements to achieve near-complete auto-fix coverage.

Goal: Enable auto-fixing for basically every no_unused_vars diagnostic.

Current State

Supported

Category Example Fix
Single import import foo from './foo' Delete entire import
Namespace import import * as foo from './foo' Delete entire import
Named import (single) import { Foo } from './foo' Delete entire import
Named import (multi) import { foo, bar } from './foo'; bar() Remove unused specifier
Type import import { type foo, bar } from './foo'; bar() Remove unused specifier
Simple declaration let a = 1 Delete declaration
Multi-declarator let a = 1, b = 2 (a unused) Remove unused declarator
Rename with refs let x = 1; x = 2 Rename to _x
Object destructure const { a, b } = obj; f(b) Remove unused property
Array destructure const [a, b] = arr; f(b) Replace with hole [, b]
For-of/for-in loops for (const unused of arr) {} Rename to _unused
Catch parameters catch (e) {} Remove the unused catch parameter
Function parameters function f(unused) {} Rename to _unused

Unsupported

Category Example Proposed Fix Tracking Issue
Binding rest elements const [...rest] = arr Rename to _rest #TBD
Type parameters type Foo<T> = {} Rename to _T #TBD
TS Namespaces namespace Foo {} Delete or rename #TBD
TS Interfaces interface Foo {} Delete or rename #TBD
Complex varsIgnorePattern Patterns other than ^_ Support more patterns #TBD
using declarations using x = getResource() TBD (side effects) #TBD

Known Bugs to Fix

1. Multi-declarator incomplete cleanup

When multiple declarators are all unused, only the first is deleted:

// Input
let a = 1, b = 2;  // both unused

// Current output (incorrect)
let b = 2;

// Expected output
// (entire statement deleted)

Location: fix_vars.rs

2. Destructures in multi-declarator statements

// Input
const l = "", { e } = r;  // l unused

// Current output (incorrect)
const { e } = r;

// Should handle this case properly

Location: fix_vars.rs

Implementation Roadmap

Phase 1: Low-hanging fruit

  • Function parameter renaming (_param pattern)
  • Catch parameter renaming (_e pattern)
  • Fix multi-declarator cascade deletion bug

Phase 2: TypeScript support

  • Type parameter renaming (_T pattern)
  • TS interface removal/renaming
  • TS namespace removal/renaming

Phase 3: Advanced patterns

  • Expand varsIgnorePattern support beyond ^_
  • Binding rest element handling
  • using declaration handling (careful with side effects)

Phase 4: Edge cases

  • Destructures in multi-declarator statements
  • Complex nested destructuring patterns
  • Default parameter values in destructuring

Notes

Why fixes are "dangerous"

All fixes are marked as dangerous_suggestion because:

  1. Removing code changes program semantics
  2. Initializers may have side effects
  3. Some "unused" variables are intentional (e.g., documentation, future use)

Current safety checks

  • Root-scope function expressions are skipped (often intentional)
  • Await expressions are skipped (side effects)
  • Rename conflicts are resolved by appending numbers (_x, _x0, _x1)

Contributing

Each sub-task should be its own PR. When implementing a new fixer:

  1. Add the fix logic in the appropriate fixers/*.rs file
  2. Update the match arm in mod.rs to call ctx.diagnostic_with_suggestion() instead of ctx.diagnostic()
  3. Add comprehensive tests in tests/oxc.rs
  4. Ensure the fix is marked .dangerously()
  5. Handle edge cases (side effects, naming conflicts, etc.)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-linterArea - LinterC-bugCategory - Bug

    Type

    Priority

    None yet

    Effort

    None yet

    Start date

    None yet

    Target date

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions