Skip to content

linter: add native rule timings #22138

@camc314

Description

@camc314

Revisit built-in Oxlint rule timings using compile-time gating via const generics, so timing instrumentation has no measurable cost on the normal linting path.

This follows from #19745, which tracks JS plugin rule timings, and from the earlier closed PR #19689, where broader per-rule timing support regressed linter benchmarks by ~15%. The goal here is to explore whether native Rust rule timings can be implemented without the normal-path performance cost that made the previous approach unsuitable.

Motivation

When an Oxlint run is unexpectedly slow, contributors and users currently have two options:

  1. Use external profilers with a release-with-debug build.
  2. Manually narrow rule sets to infer which rule is expensive.

External profilers are still the most accurate option, but they are not ergonomic for quick triage. A built-in timing mode would make it much easier to answer:

  • Which native rules are consuming the most time?
  • Is the slowdown from Oxlint itself, a JS plugin rule, type-aware linting, or project/config shape?
  • Which rules should be investigated for performance regressions?

Proposal

Add an opt-in timing mode for native Oxlint rules where the timing decision is encoded as a const generic, for example:

fn run_rules<const TIMING: bool>(...) {
    if TIMING {
        // timed path
    } else {
        // normal path
    }
}

or through a similarly structured helper / runner split.

The important requirement is that the default path monomorphizes to code with no timing branches, no timer construction, no timing storage, and no synchronization overhead.

This would be exposed at the CLI level.

Expected Behavior

When timing mode is disabled:

  • No behavior change.
  • No timing table is generated.
  • No measurable runtime regression in linter benchmarks.
  • No meaningful code size regression in the hot path.

When timing mode is enabled:

  • Oxlint records aggregate native rule execution time across all linted files.
  • Output includes a human-readable table sorted by total rule time.
  • Counts/relative percentages should be clearly defined.
  • The timing output should be useful for triage, not necessarily a replacement for profiler-grade analysis.

Example output shape:

Rule                         Time (ms)   Relative
-------------------------------------------------
typescript/no-unused-vars       123.40      31.2%
import/no-cycle                  88.10      22.3%
eslint/no-dupe-keys              12.50       3.1%

Prior Art / Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Effort

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions