Cargo Shear ✂️ 🐑
Detect and fix issues in Rust projects:
- Unused dependencies in
Cargo.toml
- Misplaced dependencies (dev/build dependencies in wrong sections)
- Unlinked source files (Rust files not reachable from any module tree)
> [!NOTE]
> This tool is considered feature-complete. We continue to welcome contributions that focus on bug fixes, dependency upgrades, and UI/UX improvements.
Installation
# Install from pre-built binaries.
cargo binstall cargo-shear
# Build from source.
cargo install cargo-shear
# Install from brew.
brew install cargo-shear
Usage
Check for issues without making changes:
cargo shear
Automatically fix unused dependencies:
cargo shear --fix
Treat warnings as errors (exit with failure code):
cargo shear --deny-warnings
This is useful for CI/CD pipelines to enforce strict checking of warnings such as empty files, unlinked files, and unused optional dependencies.
Generate machine-readable JSON output:
cargo shear --format=json
This is particularly useful for CI/CD pipelines and custom tooling that need to programmatically process the results.
Detect mismatches between [lib] target settings and source content:
cargo shear --check-test-targets
When set, cargo-shear warns when test = false is paired with source that contains tests (or doctest = false with source that contains doc tests), and — within a workspace — when test / doctest are left at their default of true for lib targets that contain none. Disabled by default; pair with --fix to automatically reconcile the flags.
Limitations
> [!IMPORTANT]
> cargo shear cannot detect "hidden" imports from macro expansions without the --expand flag (nightly only).
> This is because cargo shear uses rust-analyzer's parser to parse files and does not expand macros by default.
To expand macros:
cargo shear --expand --fix
The --expand flag uses cargo expand, which requires nightly and is significantly slower.
> [!IMPORTANT]
> Misplaced dependency detection only works for integration tests, benchmarks, and examples.
> Unit tests dependencies within #[cfg(test)] cannot be detected as misplaced.
Configuration
Ignore false positives
False positives can be ignored by adding them to the package's Cargo.toml:
[package.metadata.cargo-shear]
ignored = ["crate-name"]
Ignore unlinked files
Unlinked files can be ignored using glob patterns:
[package.metadata.cargo-shear]
ignored-paths = ["src/proto/*.rs", "examples/old/*"]
Both options work in workspace Cargo.toml as well:
[workspace.metadata.cargo-shear]
ignored = ["crate-name"]
ignored-paths = ["*/proto/*.rs"]
Ignore scope
- Package ignore (
[package.metadata.cargo-shear]) applies only to that package.
- Workspace ignore (
[workspace.metadata.cargo-shear]) applies to every member
and to the workspace dependency itself. ignored-paths globs are matched relative
to the workspace root.
An ignore that suppresses nothing is reported as redundant so it can be removed. A
workspace ignore is considered redundant only when no member needs it — a dependency
that is used in one crate but unused in another stays covered by the workspace ignore.
cargo-hakari workspace-hack crates
cargo-hakari generates a workspace-hack crate that declares many dependencies it never imports (to unify Cargo features) and is depended on by every workspace member without being imported. cargo shear detects such a crate automatically — via the ### BEGIN HAKARI SECTION marker in its Cargo.toml — and skips both the crate itself and the dependency edges pointing at it, so no ignored configuration is needed.
Otherwise please report the issue as a bug.
CI
> [!NOTE]
> cargo shear uses static analysis and operates on source code without compiling.
> This means it only needs to run once on a single platform (e.g., Linux) to detect issues across all target platforms, including those with platform-specific dependencies and conditional compilation.
>
> The only exception is when using the --expand flag, which invokes cargo build and may produce platform-specific results.
- name: Install cargo-binstall
uses: cargo-bins/cargo-binstall@main
- name: Install cargo-shear
run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
JSON Output for CI Integration
For CI systems that require structured output, use the --format=json flag:
- name: Check for unused dependencies
run: cargo shear --format=json > shear-results.json
The JSON output includes:
- summary: Counts of errors, warnings, and fixes
- findings: Detailed information about each issue including:
code: The diagnostic code (e.g., shear/unused_dependency)
severity: Error or warning level
message: Human-readable description
file: Path to the file with the issue
location: Byte offset and length within the file
help: Suggested fix
fixable: Boolean indicating if issue can be auto-fixed with --fix
Exit Code (for CI)
| Exit Code | Without --fix | With --fix |
|---|
| 0 | No issues found | No issues found, no changes made |
| 1 | Issues found | Issues found and fixed |
| 2 | Error during processing | Error during processing |
Strict Mode with --deny-warnings
By default, warnings (such as empty files, unlinked files, and unused optional dependencies) exit with code 0. Use the --deny-warnings flag to treat warnings as errors for stricter CI enforcement:
| Exit Code | Without --deny-warnings | With --deny-warnings |
|---|
| 0 | No errors (warnings allowed) | No errors or warnings |
| 1 | Errors found | Errors or warnings found |
| 2 | Error during processing | Error during processing |
GitHub Actions Example:
- name: cargo-shear
shell: bash
run: |
if ! cargo shear --fix; then
cargo check
fi
Strict CI Example with --deny-warnings:
- name: cargo-shear (strict)
run: cargo shear --deny-warnings
Technique
- Use the
cargo_metadata crate to list all dependencies specified in [workspace.dependencies] and [dependencies]
- Iterate through all package targets (
lib, bin, example, test and bench) to locate all Rust files
- Use rust-analyzer's parser (
ra_ap_syntax) to parse these Rust files and extract imports
- Alternatively, use the
--expand option with cargo expand to first expand macros and then parse the expanded code (though this is significantly slower)
- Find the difference between the imports and the package dependencies
Prior Art
- est31/cargo-udeps
- it collects dependency usage by compiling your project and find them from the
target/ directory
- does not seem to work anymore with the latest versions of
cargo
- does not work with cargo workspaces
- bnjbvr/cargo-machete
- it collects dependency usage by running regex patterns on source code
- does not detect all usages of a dependency
- does not remove unused dependencies from the workspace root
- cargo and clippy
Trophy Cases