Codebase intelligence for Rust workspaces.
cargo-fallow finds unused dependencies, pub items that only their own crate uses, duplicated code, complex functions and boundary violations.
Its mutation tests show the code changes that your tests do not detect.
Docs · Install · Terminal · Pull requests · Mutation testing · Agents
Run it in the root of any Cargo workspace:
cargo fallowcargo fallow is the Rust version of fallow.
It reads cargo metadata, parses your source with ra_ap_syntax (the parser
of rust-analyzer), and builds a module graph of the workspace. The static
checks work from this graph and the syntax tree, so you do not have to
compile the workspace first.
Important
cargo-fallow is at version 0.x. The JSON output can change in a minor release. The CHANGELOG lists each change.
| Question | Check | Command |
|---|---|---|
| Which dependencies does no code use? | Unused, misplaced and feature-only dependencies, and unused features, with an auto-fix that keeps the format of Cargo.toml |
cargo fallow deps, cargo fallow fix |
Which pub items are too visible? |
Dead public items and items that only their own crate uses, with an optional rust-analyzer pass for method calls and macro output | cargo fallow visibility |
Which .rs files does no target compile? |
.rs files outside the module tree of each target |
cargo fallow files |
| What is copied? | Clone groups on normalized token streams, in four modes from strict to semantic |
cargo fallow dupes |
| Where is the code hard to change? | Functions over the cyclomatic or the cognitive limit, a CRAP score from LCOV coverage, and a 0 to 100 health score | cargo fallow complexity |
| Do crates and modules follow your dependency rules? | Crate, module, source, item and glob re-export rules in Cargo.toml |
cargo fallow boundaries |
| Do large files grow? | Line limits for files and functions, and a growth gate against a base ref | cargo fallow size |
| Which public API does a change remove? | Added, removed and changed items of each published library against a base ref | cargo fallow api --base <REF> |
| Do the tests detect a code change? | Mutation testing with test selection from a coverage map (opt-in, runs the tests) | cargo fallow mutants |
cargo fallow check runs all the static checks and writes one report with a
health score. It reads cargo metadata one time and parses each file one time
for all the checks. To see what a rule means and how to fix a finding, run
cargo fallow explain <rule>.
Install the crate from crates.io:
cargo install cargo-fallow --lockedEach GitHub release also has prebuilt binaries for Linux, macOS and Windows, with a checksum and a signature for each archive.
--locked uses the dependency versions in Cargo.lock, which are the
versions that CI tests. The package installs one binary, cargo-fallow, and
you run it as cargo fallow.
Warning
Do not run cargo install fallow. That command installs
fallow for TypeScript and JavaScript.
The cargo-fallow package does not install a binary named fallow.
mutants and visibility --precise need more tools:
cargo fallow mutantsneedscargo-nextestand thellvm-toolscomponent (rustup component add llvm-tools).cargo fallow visibility --preciseneeds a build with theprecisefeature and therust-srccomponent. See Precise mode.
cargo fallow # each static check, one report
cargo fallow check --changed-since origin/main # only the findings in changed files
cargo fallow check -p my-core --only deps,visibility # one member, two checks
cargo fallow fix --dry-run # show the edits, then run `fix --yes`
cargo fallow explain unused-dependency # the meaning and the fix of a ruleThe report shows the health score first, then the findings of each check, and then the gates. Here is part of the report for the cargo-fallow repository:
cargo fallow check: 1 member, 19 targets
Checks: parse, deps, files, visibility, boundaries, size, complexity, dupes
Health (rust): 97.6 (A), formula version 2
== deps ==
cargo fallow deps: 1 member
No issues found
== visibility ==
cargo fallow visibility (fast mode)
src/deps/report.rs:41:15 unnecessary-public associated_const `deps::report::Rule::ALL`: pub -> pub(crate) (only tests use it)
src/mutation/coverage.rs:479:8 unnecessary-public function `mutation::coverage::parse_test_map`: pub -> pub(crate) (only tests use it)
5 findings: 0 dead-public, 5 unnecessary-public, 0 unnecessary-restricted-visibility
== complexity ==
cargo fallow complexity: 108 files, 3811 functions, limits cyclomatic 20 and cognitive 15
src/deps/manifest.rs:70 entries cyclomatic 20 cognitive 35 (high, 60 lines)
src/visibility/mod.rs:532 decide cyclomatic 28 cognitive 31 (high, 91 lines)
Gates:
error-severity-findings: pass, not enforced
visibility-findings: fail (5 files), not enforced
...
Each report lists the gates. A failed gate changes the exit code only when
you add --fail-on-issues. All commands use the same exit codes:
| Exit code | Meaning |
|---|---|
0 |
No enforced gate failed. |
1 |
An enforced gate failed. |
2 |
The input was not valid, or the run could not complete. With --format json, a JSON error document goes to stdout. |
In a script that reads the JSON output, treat 0 and 1 as a complete run.
Do not hide exit code 2 with || true.
This repository is also a GitHub Action. The action runs one cargo fallow
command and writes the result to the job summary. It adds an annotation for
each finding, and it can post a comment on the pull request.
name: cargo-fallow
on: pull_request
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<sha>
with:
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@<sha>
with:
toolchain: stable
- uses: fallow-rs/cargo-fallow@<sha>
with:
command: check
auto-changed-since: 'true'With auto-changed-since, the report has only the findings in the files
that the pull request changed. Old findings then do not fail the job. Pin each
action to a full commit SHA.
| GitHub surface | Turn it on with | Job permission |
|---|---|---|
| Job summary and annotations | On by default | None |
| Sticky pull request comment | comment: 'true' |
pull-requests: write |
| Code scanning | sarif: 'true' |
security-events: write |
To get a starting workflow, run cargo fallow init --ci github. The
action guide lists all inputs and outputs. If you do not
use the action, --format github-annotations and --format github-summary
write the same annotations and summary. For GitLab, the
CI template writes the code quality report of the merge
request widget, and can post one merge request note.
You do not have to fix each finding before you turn on the gates.
- Run
cargo fallow init. The command prints the configuration tables, with each limit set to the current maximum in the workspace. It does not change a file. - Run
cargo fallow init --write. The command adds the tables to the rootCargo.tomland saves the current findings as a baseline. After this,cargo fallow check --fail-on-issuespasses, and only new findings fail it. - In pull requests, add
--changed-since <ref>. The report then has only the findings in the changed files, and you do not need a baseline. - Lower the limits over time. To remove unused dependencies and narrow the
visibility of
pubitems, runcargo fallow fix.
The configuration is in Cargo.toml. You do not need a separate
configuration file:
[workspace.metadata.fallow]
baseline = "fallow-baseline.json"
[workspace.metadata.fallow.complexity]
max-cyclomatic = 20
max-cognitive = 15
[workspace.metadata.fallow.deps]
ignored = ["getrandom"] # turns on a feature for a workspace buildTo keep an intentional finding, add a suppression comment:
// fallow-ignore-next-line visibility -- kept for a plugin
pub fn hook() {}To change the severity of a rule, set it to error, warn or off in
[workspace.metadata.fallow.rules]. The adoption guide
describes rule severity, baselines, --package, --changed-since and all
suppression kinds.
cargo fallow mutants makes small changes (mutants) in the source of one
package and runs the tests against each mutant. The report lists each mutant
that no test detects.
cargo fallow mutants --package my-crate
cargo fallow mutants --in-diff origin/main --fail-on-survivors
cargo fallow mutants --inspect 3f6c1d2a9b804e57cargo fallow mutants: my-crate 0.1.0
6 mutants, score 50.0%: 2 killed, 2 survived, 2 no coverage
Survived (2)
src/a.rs
:8:5 relational-operator `a == 2` -> `!=` [3f6c1d2a9b804e57]
- One build contains most of the mutants. At run time, a switch selects the active mutant, so these mutants do not need a compile of their own.
- A coverage map tells which tests execute the lines of a mutant. Only those tests run, and the fastest test runs first.
- With
--in-diff <ref>, you test only the mutants on changed lines. - All work happens in a copy of the workspace, so your checkout does not
change. cargo-fallow kills a test process above 4096 MiB (
--memory-limitsets another limit). Ifcargo fallowdies, a watchdog kills the tests. - Each result goes to a journal. When a run stops early, the next run with the same source and options reads the journal and tests only the other mutants.
Mutation testing describes the tiers, each status, the test selection and the limits.
- If the source does not change, the report does not change. Finding ids do not use line numbers, so an edit above a finding keeps its id.
- When cargo-fallow cannot tell from the syntax if code uses an item, it
counts the item as used. Examples are a method call on an unknown type, a
name in macro tokens, and an
include!fromOUT_DIR. - A
checkrun does not changeCargo.lock, also whendepsresolves the dependencies. - cargo-fallow parses each file on a thread with a stack sized for that file,
so a deeply nested file cannot crash the run. A file that does not parse
fails the
parse-errorgate. - Each JSON document matches the JSON Schema. A test in CI fails when a report and the schema differ.
- cargo-fallow computes finding ids and the health score the same way as fallow for JavaScript. Most gate names are also the same.
In a script or a coding agent, add --format json --quiet. Each command then
writes one JSON document to stdout:
kindis the name of the command, andschema_versionis the version of the document shape.- Each finding has
id,check,rule,severity,locationand anactions[]array. Whenauto_fixableistrue,cargo fallow fixcan apply the action. - With
--explain, the document has a_meta.rulesobject. It gives the meaning and the fix of each rule that has a finding. cargo fallow schemaprints the JSON Schema of each document.
For grep, use --format compact. It writes one path:line:col: rule: message
line for each finding. Output formats describes all formats.
To set up a repository for coding agents in one step, run:
cargo fallow agent install --dry-run # show the files first
cargo fallow agent install # the same as `cargo fallow init --agents`
cargo fallow agent statusagent install writes a short guide in AGENTS.md, the cargo-fallow skill
for Claude Code and Codex, and the commit gate. To install only the gate, run
cargo fallow hooks install --target agent.
The gate runs before each git commit and git push of Claude Code or
Codex. It blocks when cargo fallow check finds a new finding in the changed
files, and it lists each finding. A finding that the base commit already has
does not block. --target git installs the same check as a git pre-commit
hook. Hooks has the details.
The skill in skills/cargo-fallow/SKILL.md
tells a coding agent how to run each command, read the JSON and act on a
block of the gate. Copy it to .claude/skills/cargo-fallow/ or
.agents/skills/cargo-fallow/. A test checks each command and flag in the
skill against cargo fallow --help.
| Page | Contents |
|---|---|
| The check command | Flags of check, and the health score |
| Adoption | Changed files, packages, rule severity, baselines, init and suppressions |
| Unused dependencies | Rules of deps, and the cases where it counts a dependency as used |
| Fix | What cargo fallow fix changes, and how it checks the result |
| Unused files | How files finds the files that no target compiles |
| Visibility | Kinds of visibility, and precise mode |
| Structure checks | boundaries, size, complexity and dupes |
| API changes | api --base <REF> |
| Mutation testing | mutants: tiers, status, test selection and safety |
| Trace | --trace of visibility, deps and dupes, to see why a check reports one item or does not |
| Output formats | Each format, the JSON Schema, explain and the exit codes |
| Hooks | The agent gate of Claude Code and Codex, and the git pre-commit hook |
| GitHub Action | Inputs, outputs and install methods of the action |
To report a false positive or a missing case, open an issue. CONTRIBUTING.md describes the build, the quality gates and the DCO sign-off. To report a vulnerability, follow SECURITY.md.
MIT. See LICENSE.