NitroCop
Fast Ruby linter in Rust targeting RuboCop compatibility.
Note
🚧 Early-stage: Detection is high-fidelity on most codebases but edge cases remain. Autocorrect is not yet complete. Expect bugs.
Benchmark on the rubygems.org repo (1,227 files, Ruby 4.0), Apple Silicon:
| Scenario | nitrocop | RuboCop | Speedup |
|---|---|---|---|
| Local dev (50 files changed) | 75ms | 1.30s | 17.3x |
| CI (no cache) | 279ms | 14.86s | 53.4x |
Features
- 910 cops from 6 RuboCop gems (rubocop, rubocop-rails, rubocop-performance, rubocop-rspec, rubocop-rspec_rails, rubocop-factory_bot)
- 98.8% conformance against RuboCop across 1,017 open-source repos
-
Autocorrect (
-a/-A) is partial — work in progress - Reads your existing
.rubocop.yml— no migration needed - Uses Prism (Ruby's official parser) via
ruby-prismcrate - Parallel file processing with rayon
Quick Start (Work in progress 🚧)
Requires Rust 1.85+ (edition 2024).
cargo install nitrocop # not yet published — build from source for nowThen run it in your Ruby project:
nitrocopConfiguration
nitrocop reads .rubocop.yml with full support for:
-
inherit_from— local files, recursive -
inherit_gem— resolves gem paths viabundle info -
inherit_mode— merge/override for arrays -
Department-level config —
RSpec:,Rails:Include/Exclude/Enabled -
AllCops—NewCops,DisabledByDefault,Exclude,Include -
Enabled: pendingtri-state -
Per-cop options —
EnforcedStyle,Max,AllowedMethods,AllowedPatterns, etc.
Config auto-discovery walks up from the target directory to find .rubocop.yml.
Cops
nitrocop supports 910 cops from 6 RuboCop gems.
Compared with RuboCop on 1,017 open-source repos (228k Ruby files).
98.8% of compared issue reports matched (11.8M of 11.9M). 720 of 910 cops matched exactly; 190 differed.
rubocop 1.84.2 (588 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| Layout | 100 | 74 | 26 | 74.0% |
| Lint | 148 | 134 | 14 | 90.5% |
| Style | 287 | 140 | 147 | 48.7% |
| Metrics | 10 | 10 | 0 | ✓ 100.0% |
| Naming | 19 | 19 | 0 | ✓ 100.0% |
| Security | 6 | 6 | 0 | ✓ 100.0% |
| Bundler | 7 | 7 | 0 | ✓ 100.0% |
| Gemspec | 10 | 10 | 0 | ✓ 100.0% |
| Migration | 1 | 1 | 0 | ✓ 100.0% |
| Total | 588 | 401 | 187 | 68.1% |
rubocop-rails 2.34.3 (138 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| Rails | 138 | 138 | 0 | ✓ 100.0% |
rubocop-performance 1.26.1 (52 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| Performance | 52 | 52 | 0 | ✓ 100.0% |
rubocop-rspec 3.9.0 (113 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| RSpec | 113 | 110 | 3 | 97.3% |
rubocop-rspec_rails 2.32.0 (8 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| RSpecRails | 8 | 8 | 0 | ✓ 100.0% |
rubocop-factory_bot 2.28.0 (11 cops)
| Department | Cops | Matched exactly | Differed | Matched exactly % |
|---|---|---|---|---|
| FactoryBot | 11 | 11 | 0 | ✓ 100.0% |
"Matched exactly" means nitrocop produced no extra issues and missed no issues for that cop anywhere in the corpus. See docs/corpus.md for the full corpus breakdown.
Every cop reads its RuboCop YAML config options and has fixture-based test coverage.
Hybrid Mode
Use --rubocop-only to run nitrocop alongside RuboCop for cops it doesn't cover yet:
#!/usr/bin/env bash
# bin/lint — fast hybrid linter
nitrocop "$@"
REMAINING=$(nitrocop --rubocop-only)
if [ -n "$REMAINING" ]; then
bundle exec rubocop --only "$REMAINING" "$@"
fiCLI
nitrocop [OPTIONS] [PATHS]...
Arguments:
[PATHS]... Files or directories to lint [default: .]
Options:
-a, --autocorrect Autocorrect offenses (safe cops only)
-A, --autocorrect-all Autocorrect offenses (all cops, including unsafe)
-c, --config <PATH> Path to .rubocop.yml
-f, --format <FORMAT> Output format: text, json [default: text]
--only <COPS> Run only specified cops (comma-separated)
--except <COPS> Skip specified cops (comma-separated)
--rubocop-only Print cops NOT covered by nitrocop
--stdin <PATH> Read source from stdin, use PATH for display
--debug Print timing and debug info
--list-cops List all registered cops
--ignore-disable-comments Ignore all # rubocop:disable inline comments
--cache <true|false> Enable/disable file-level result caching [default: true]
--cache-clear Clear the result cache and exit
--init Resolve gem paths and write lockfile to cache directory, then exit
--fail-level <SEV> Minimum severity for non-zero exit (convention/warning/error/fatal)
-F, --fail-fast Stop after first file with offenses
--force-exclusion Apply AllCops.Exclude to explicitly-passed files
-L, --list-target-files Print files that would be linted, then exit
--force-default-config Ignore all config files, use built-in defaults
-h, --help Print help
Local Development
cargo check # fast compile check
cargo test # run all tests (2,700+)
cargo run -- . # lint current directory
# Quality checks (must pass — zero tolerance)
cargo test config_audit # all YAML config keys implemented
cargo test prism_pitfalls # no missing node type handling
# Benchmarks
cargo run --release --bin bench_nitrocop # full: setup + bench + conform
cargo run --release --bin bench_nitrocop -- bench # timing onlyHow It Works
-
Config resolution — Walks up from target to find
.rubocop.yml, resolvesinherit_from/inherit_gemchains, merges layers -
File discovery — Uses the
ignorecrate for .gitignore-aware traversal, applies AllCops.Exclude/Include patterns -
Parallel linting — Each rayon worker thread parses files with Prism (
ParseResultis!Send), runs all enabled cops per file -
Cop execution — Three check phases per file:
check_lines(raw text),check_source(bytes + CodeMap),check_node(AST walk via batched dispatch table) - Output — RuboCop-compatible text format or JSON
Limitations
These cops are registered but cannot be exercised under current Ruby versions:
-
Lint/ItWithoutArgumentsInBlock—itis a block parameter in Ruby 3.4+, making this cop obsolete -
Lint/NonDeterministicRequireOrder—Dirresults are sorted since Ruby 3.0 -
Lint/NumberedParameterAssignment— assigning to_1is a syntax error in Ruby 3.4+ -
Lint/UselessElseWithoutRescue— syntax error in Ruby 3.4+ -
Security/YAMLLoad—YAML.loadis safe since Ruby 3.1 (cop has max Ruby 3.0)
These cops are excluded from corpus reporting counts.