{"id":51680243,"url":"https://github.com/momiji-rs/oxidecop","last_synced_at":"2026-07-15T12:30:49.658Z","repository":{"id":368865176,"uuid":"1287229916","full_name":"momiji-rs/oxidecop","owner":"momiji-rs","description":"OxideCop: a fast, native, RuboCop-compatible Ruby linter and autocorrector over the Prism parser","archived":false,"fork":false,"pushed_at":"2026-07-02T14:43:11.000Z","size":475,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-02T15:28:25.604Z","etag":null,"topics":["autocorrect","cli","developer-tools","formatter","linter","prism","rubocop","ruby","rust","static-analysis"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/momiji-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-02T13:50:45.000Z","updated_at":"2026-07-02T14:45:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/momiji-rs/oxidecop","commit_stats":null,"previous_names":["momiji-rs/oxidecop"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/momiji-rs/oxidecop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/momiji-rs%2Foxidecop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/momiji-rs%2Foxidecop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/momiji-rs%2Foxidecop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/momiji-rs%2Foxidecop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/momiji-rs","download_url":"https://codeload.github.com/momiji-rs/oxidecop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/momiji-rs%2Foxidecop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35505256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autocorrect","cli","developer-tools","formatter","linter","prism","rubocop","ruby","rust","static-analysis"],"created_at":"2026-07-15T12:30:48.796Z","updated_at":"2026-07-15T12:30:49.652Z","avatar_url":"https://github.com/momiji-rs.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OxideCop\n\nA fast, native, **RuboCop-compatible** Ruby linter and autocorrector — written\nin Rust over the official [Prism](https://github.com/ruby/prism) parser. Think\n*ruff, but for Ruby, and bug-compatible with the RuboCop everyone already uses.*\n\n```sh\ncargo install oxidecop\noxidecop .        # same offenses, same messages, same exit code — just fast\n```\n\n\u003e **Status: early / experimental.** The core idea is proven — the 47\n\u003e implemented cops pass **100% of RuboCop's own representable spec examples**,\n\u003e detection (1051/1051) *and* autocorrection (477/477, `--fix`), and match\n\u003e RuboCop **byte-for-byte on real repos** (Rails, Mastodon, rubygems.org,\n\u003e RuboCop's own source — see\n\u003e [BENCHMARKS.md](https://github.com/momiji-rs/oxidecop/blob/master/BENCHMARKS.md))\n\u003e — but 47 cops is not a shippable linter yet.\n\n## Why\n\nRuboCop is the de-facto Ruby linter, but it runs on CRuby and is dominated by\n**parse time in the interpreted `parser` gem**. A native linter that speaks\nPrism is an order of magnitude faster while producing **byte-identical\noutput**, so it drops into existing projects with zero config changes.\n\nThe bet only pays off if we are **faithful to RuboCop's actual behavior**, not\n\"morally equivalent.\" That is why fidelity is measured against RuboCop's *own*\ntest suite, not by eyeballing:\n\n| | |\n|---|---|\n| Whole-tree lint, Rails (~3,400 files) | **57 ms** |\n| vs [oxicop](https://crates.io/crates/oxicop) (regex-based, no parser) | 2–5× faster — and correct where it reports thousands of false positives |\n| vs [nitrocop](https://github.com/6/nitrocop) (prism-based, 915 cops) | 4–31× faster cold; on RuboCop's own CI-clean repo it reports 17,174 offenses, OxideCop reports the true zero |\n| Warm rerun (nothing changed) | **~29 ms** (stat-keyed result cache, no file reads) |\n\n## Fidelity, measured\n\nRuboCop's spec suite encodes exact expectations inline:\n\n```ruby\nexpect_offense(\u003c\u003c~RUBY)\n  x == nil\n    ^^ Prefer the use of the `nil?` predicate.\nRUBY\n```\n\nThe **oracle** (`oracle/oracle.rb`) parses those fixtures, runs the\nde-annotated source through the `oxidecop` binary under *the example's own*\n`cop_config`, and diffs at three levels: **LOC** (right line:col), **FULL**\n(line:col *and* identical message), and **FIX** (the `--fix` output against\n`expect_correction`).\n\nCurrent leaderboard (`ruby oracle/leaderboard.rb`), against RuboCop v1.88.0:\n\n```\n47 cops implemented — every one at 100% FULL match, 100% FIX (autocorrect).\nTOTAL (representable examples)         1051    108               1051/1051    100%   FIX 477/477\n```\n\n**Read this honestly:** the score is over *representable* examples only.\nRuboCop's specs encode some text dynamically (RSpec loop variables,\n`%{identifier}` substitutions, shared_examples that run under a caller's\nconfig); the oracle renders what is static and **skips** what isn't, rather\nthan scoring a harness limitation as a cop miss — hence the `skip` column.\n\nEnd-to-end, `tools/parity.sh \u003cruby-tree\u003e` lints a tree with both linters\n(both under `--only` with the implemented cop list, which RuboCop\nforce-enables) and diffs the normalized offense lists. Byte-identical on all\nfour corpora: **Rails (12,271 offense lines), Mastodon (4,123), rubygems.org\n(772), and RuboCop's own 1,712-file source tree (0 vs 0)**. CI re-verifies the\noracle at 100% and the parity on every push.\n\n## Usage\n\n```sh\noxidecop lib/ spec/              # lint trees in parallel (nearest .rubocop.yml per file)\noxidecop file.rb my.yml          # explicit config\noxidecop -a .                    # autocorrect in place (like rubocop -a)\noxidecop file.rb --fix           # autocorrect a single file -\u003e stdout\noxidecop --only Style/SymbolProc --format json .\n```\n\nOutput mimics RuboCop's simple formatter (severity letters, `[Correctable]`\ntags, per-cop `Severity` config honored); `--format json` emits\nRuboCop-compatible JSON. Exit code is 1 when offenses were found.\n\nConfig support covers the surface real projects use: `.rubocop.yml` discovery\n(nearest config per file), `inherit_from` chains, `inherit_gem`, `AllCops:\nDisabledByDefault / Exclude / Include`, per-cop `Enabled` / `Exclude` /\n`EnforcedStyle` / `AllowedMethods` / `AllowedPatterns` (including\n`!ruby/regexp`), magic-comment `rubocop:disable` directives (with `-- reason`\ntrailers), and `TargetRubyVersion`. Parameter defaults for **all 606 cops**\nare generated from RuboCop's own `config/default.yml` into one schema table,\nso a newly ported cop's defaults are already correct.\n\nA result cache (stat-keyed, content-hash fallback) makes unchanged-tree reruns\n~2× faster; `--cache false` disables it.\n\n## Repo layout\n\n```\nsrc/main.rs            the runner: argv, file/config discovery, output, --fix loop\nsrc/config.rs          .rubocop.yml subset + the generated per-cop SCHEMA\nsrc/declarative.rs     the DECLARATIVE pattern-cop table (node-pattern rows)\nsrc/nodepattern.rs     RuboCop's node-pattern DSL, parsed and matched over Prism\nsrc/cops/              the visitor + per-department cop logic\nsrc/cops/breakable.rs  Layout/LineLength autocorrection (CheckLineBreakable port)\noracle/                the spec-suite oracle + leaderboard (fidelity measurement)\ntools/parity.sh        whole-tree byte-identical diff vs reference RuboCop\nbench/compare.sh       the speed scoreboard vs competing linters\n```\n\n## How to add / improve a cop\n\n1. **Find RuboCop's real pattern.** Copy the `def_node_matcher` string and\n   `MSG` from RuboCop's source *verbatim* — do not paraphrase. (Guessed\n   patterns are systematically over-broad; the oracle catches this every time.)\n2. **Express it.** A pattern cop -\u003e one row in the `DECLARATIVE` table. A logic\n   cop -\u003e a small `Visit` method. If the DSL can't express the pattern yet,\n   extend `src/nodepattern.rs`.\n3. **Measure.** Map the cop to its spec in `oracle/leaderboard.rb`, run it —\n   the miss list is your exact TODO, down to the failing example.\n4. **Verify end-to-end.** `tools/parity.sh` against a real tree must stay\n   byte-identical.\n\n## Roadmap\n\n- **Widen**: port the next tiers of cops (the schema and node-pattern DSL\n  already cover their config surface).\n- **Distribution**: prebuilt binaries, `brew install oxidecop`.\n- **Close the warm-rerun gap**: cache the directory walk to take no-change\n  reruns from ~29 ms toward ~5 ms.\n\n## License\n\nMIT — see [LICENSE](https://github.com/momiji-rs/oxidecop/blob/master/LICENSE).\nOxideCop is not affiliated with the RuboCop project; RuboCop is the work of\nits authors and contributors, and this project exists to be faithful to it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmomiji-rs%2Foxidecop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmomiji-rs%2Foxidecop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmomiji-rs%2Foxidecop/lists"}