id: SMH-RESEARCH-JTJLLCMH type: research title: "Cargo and Clippy Lint Scoping"
Cargo and Clippy Lint Scoping
Idiomatic ways to scope lint levels by target (production, tests, guests), verified against current docs and this repository.
Mechanisms, verified
| mechanism | scope unit | idiomatic for | verified |
|---|---|---|---|
[workspace.lints] + [lints] workspace = true |
package (all its targets) | stable levels owned in Cargo.toml; cargo-native since 1.74 | cargo workspaces |
[lints.clippy] per package |
package | per-crate exceptions without attrs | clippy configuration |
attributes #[deny] / #[expect] |
item, module, file | visible, reasoned, local exceptions | clippy docs |
rustc args after -- (-A clippy::x) |
invocation | phase splits (--tests, --lib) and one-shot overrides; last level wins over earlier flags |
rustc lint levels |
.cargo/config.toml rustflags |
every rustc invocation | workspace-wide zero-warning policy incl. builds | current smith setup |
clippy.toml / .clippy.toml |
whole run | VALUES only (thresholds), never levels | clippy docs |
Nightly-only: cargo's own lint group (unused_dependencies etc.) — not usable on stable.
Facts discovered in this repository
CLIPPY_CONF_DIRis real and documented: priority CLIPPY_CONF_DIR → CARGO_MANIFEST_DIR → cwd, then parent walk.- Empirically: without the env var,
clippy.tomlunder.config/is NOT discovered — rawcargo clippyand rust-analyzer silently lose threshold configuration. With the env var (xtask gate), it parses. - Clippy levels cannot be scoped by target type (
--tests) through any cargo-native table; that gap is what invocation splitting covers. #[expect]on items coexists with command-line-A: expectations stay fulfilled when the lint would fire without the allowance. File-level#![expect]headers on lints the phase allows become redundant but were not observed to error; either way, phase-covered expectations should be deleted.
Idiomatic shape for smith
policy levels .cargo/config.toml rustflags (unchanged) — the
zero-warning law applies to every compilation
thresholds .config/clippy.toml + CLIPPY_CONF_DIR in xtask
per-crate levels [lints] tables when a crate needs a standing
exception (none today)
local exceptions #[expect(..., reason)] attributes (status quo)
target phases xtask clippy split: --lib/--bins strict,
--tests with assertion allowances, guests with
pedantic+missing_docs allowances
Phase allowances after -- are idiomatic for CI orchestration; they live in one auditable place and cannot leak into source.
Unresolved questions
- Raw-run configuration loss: developers and rust-analyzer invoke clippy without CLIPPY_CONF_DIR and get default thresholds. Options: symlink
.clippy.tomlat root into.config/(root file, law tension), law exception for clippy discovery, or accept gate-only configuration and document it. - Precedence between
[lints]table levels and config rustflags is undocumented; if both are adopted, test empirically before mixing. - Whether test allowances should eventually move into a
[lints]-based scheme if cargo grows per-target lint tables.
Sources
- Cargo lints inheritance — https://doc.rust-lang.org/cargo/reference/workspaces.html
- Clippy configuration discovery — https://doc.rust-lang.org/clippy/configuration.html
- Cargo's own (nightly) lint groups — https://doc.rust-lang.org/cargo/reference/lints.html