buildRustCrate: add useClippy parameter to lint with clippy-driver (#521993)

This commit is contained in:
Ilan Joselevich 2026-06-16 16:08:42 +00:00 committed by GitHub
commit 1c56877f68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 132 additions and 4 deletions

View file

@ -885,8 +885,7 @@ general. A number of other parameters can be overridden:
empty, or `"forbid"` (no cap) when `lints` is set. Because `rustc`
only honours the first `--cap-lints` it receives, this cannot be
changed via `extraRustcOpts`; use this attribute instead. Useful
when overriding the `rust` attribute to point at `clippy-driver`,
since clippy lints are also capped by this flag:
with `useClippy`, since clippy lints are also capped by this flag:
```nix
(hello { }).override { capLints = "warn"; }
@ -912,6 +911,34 @@ general. A number of other parameters can be overridden:
}
```
- Whether to compile the crate with `clippy-driver` instead of `rustc`.
Build scripts (`build.rs`) keep plain `rustc`. The default `capLints`
of `"allow"` suppresses all lints including clippy's, so this is
usually paired with `capLints` and lint flags via `extraRustcOpts`:
```nix
(hello { }).override {
useClippy = true;
capLints = "warn";
extraRustcOpts = [
"-Dwarnings"
"-Wclippy::all"
];
}
```
When using a Rust toolchain that bundles its own `clippy-driver`
(rust-overlay, Fenix), pass it via `clippy` so the sysroot matches:
```nix
(hello { }).override {
rust = myToolchain;
clippy = myToolchain;
useClippy = true;
capLints = "warn";
}
```
- Phases, just like in any other derivation, can be specified using
the following attributes: `preUnpack`, `postUnpack`, `prePatch`,
`patches`, `postPatch`, `preConfigure` (in the case of a Rust crate,

View file

@ -25,6 +25,7 @@
buildTests,
codegenUnits,
capLints,
useClippy,
}:
let
@ -94,6 +95,7 @@ in
runHook preBuild
# configure & source common build functions
RUSTC_DRIVER="${if useClippy then "clippy-driver" else "rustc"}"
LIB_RUSTC_OPTS="${libRustcOpts}"
BIN_RUSTC_OPTS="${binRustcOpts}"
LIB_EXT="${stdenv.hostPlatform.extensions.library}"

View file

@ -12,6 +12,7 @@
pkgsBuildBuild,
rustc,
cargo,
clippy,
jq,
libiconv,
# Controls codegen parallelization for all crates.
@ -161,6 +162,31 @@ lib.makeOverridable
#
# Default: pkgs.cargo
cargo ? cargo,
# Whether to compile the crate's library, binary, and test targets with
# `clippy-driver` instead of `rustc`. Build scripts (`build.rs`) keep
# plain `rustc` — they are typically auto-generated and clippy findings
# there are not actionable.
#
# `clippy-driver` wraps `rustc_driver` with extra lint passes and emits
# link-compatible `.rlib`/`.rmeta`, so dependency crates built with plain
# `rustc` are still usable; only the crate being linted needs this flag.
#
# Note that the default `capLints` of `"allow"` suppresses ALL lints,
# including clippy's. Set `capLints = "warn"` (or `"forbid"`) or supply
# a `lints` table — otherwise `useClippy` is a silent no-op. Lint flags
# such as `-D warnings` or `-W clippy::pedantic` go through the regular
# `extraRustcOpts` (clippy-driver forwards rustc flags unchanged).
#
# Example: true
# Default: false
useClippy,
# The clippy package providing `clippy-driver`. Only consulted when
# `useClippy = true`. Override this together with `rust` when using a
# toolchain (rust-overlay, Fenix) that bundles its own `clippy-driver`,
# so the sysroot matches.
#
# Default: pkgs.clippy
clippy ? clippy,
# Whether to build a release version (`true`) or a debug
# version (`false`). Debug versions are faster to build
# but might be much slower at runtime.
@ -430,6 +456,7 @@ lib.makeOverridable
cargo
jq
]
++ lib.optional useClippy clippy
++ lib.optionals stdenv.hasCC [ stdenv.cc ]
++ lib.optionals stdenv.buildPlatform.isDarwin [ libiconv ]
++ (crate.nativeBuildInputs or [ ])
@ -577,6 +604,7 @@ lib.makeOverridable
buildTests
codegenUnits
capLints
useClippy
;
};
dontStrip = !release;
@ -612,6 +640,8 @@ lib.makeOverridable
{
rust = crate_.rust or rustc;
cargo = crate_.cargo or cargo;
useClippy = crate_.useClippy or false;
clippy = crate_.clippy or clippy;
release = crate_.release or true;
verbose = crate_.verbose or true;
extraRustcOpts = [ ];

View file

@ -10,7 +10,7 @@ build_lib() {
lib_src=$1
echo_build_heading $lib_src ${libName}
noisily env "${CARGO_BIN_EXE_ENV[@]}" rustc \
noisily env "${CARGO_BIN_EXE_ENV[@]}" "${RUSTC_DRIVER:-rustc}" \
--crate-name $CRATE_NAME \
$lib_src \
--out-dir target/lib \
@ -42,7 +42,7 @@ build_bin() {
main_file=$2
fi
echo_build_heading $crate_name $main_file
noisily env "${CARGO_BIN_EXE_ENV[@]}" rustc \
noisily env "${CARGO_BIN_EXE_ENV[@]}" "${RUSTC_DRIVER:-rustc}" \
--crate-name $crate_name_ \
$main_file \
--crate-type bin \

View file

@ -1128,6 +1128,75 @@ rec {
touch $out
'';
# `useClippy = true` plus a denied clippy lint should fail the build,
# proving clippy-driver (not plain rustc) compiled the crate. The
# `clippy::` prefix in the diagnostic is the fingerprint: rustc has no
# such lint group.
useClippyDenyFails =
let
crate = mkHostCrate {
crateName = "useClippyDenyFails";
useClippy = true;
lints.clippy.eq_op = "deny";
src = mkFile "src/lib.rs" ''
pub fn check() -> bool {
1 == 1
}
'';
};
failed = testers.testBuildFailure crate;
in
runCommand "assert-useClippyDenyFails" { inherit failed; } ''
grep -q 'clippy::eq.op' "$failed/testBuildFailure.log"
grep -q 'equal expressions' "$failed/testBuildFailure.log"
touch $out
'';
# `useClippy = true` with the default `capLints` (which resolves to
# `"allow"` when `lints` is empty) must still build: the cap silences
# clippy lints just like rustc lints. Same source as the failing test
# above — only the `lints` table differs.
useClippyDefaultCapAllows = mkHostCrate {
crateName = "useClippyDefaultCapAllows";
useClippy = true;
src = mkFile "src/lib.rs" ''
pub fn check() -> bool {
1 == 1
}
'';
};
# A library compiled by clippy-driver must produce an `.rlib` that a
# plain-rustc dependent can link against and run. This is the property
# that makes `useClippy` safe to flip per-crate.
useClippyRlibLinkCompat =
let
libCrate = mkHostCrate {
crateName = "clippylib";
useClippy = true;
src = mkFile "src/lib.rs" ''
pub fn test() -> i32 {
23
}
'';
};
binCrate = mkHostCrate {
crateName = "clippybin";
dependencies = [ libCrate ];
src = mkBinExtern "src/main.rs" "clippylib";
};
in
runCommand "run-useClippyRlibLinkCompat" { nativeBuildInputs = [ binCrate ]; } (
if stdenv.hostPlatform == stdenv.buildPlatform then
''
${binCrate}/bin/clippybin && touch $out
''
else
''
test -x '${binCrate}/bin/clippybin' && touch $out
''
);
rcgenTest =
let
pkg = rcgenCrates.rootCrate.build;