feat(lints): Emit unused_dependencies lint#16600
feat(lints): Emit unused_dependencies lint#16600epage wants to merge 10 commits intorust-lang:masterfrom
Conversation
|
r? @ehuss rustbot has assigned @ehuss. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| [WARNING] unused dependency | ||
| --> bar/Cargo.toml:9:1 | ||
| | | ||
| 9 | build-dep.workspace = true | ||
| | ^^^^^^^^^ | ||
| | | ||
| = [NOTE] `cargo::unused_dependencies` is set to `warn` by default | ||
| [HELP] remove the dependency | ||
| | | ||
| 9 - build-dep.workspace = true | ||
| 9 + .workspace = true |
There was a problem hiding this comment.
Our selecting of key-values that use workspace inheritance is not great
| [WARNING] unused dependency | ||
| --> Cargo.toml:9:13 | ||
| | | ||
| 9 | unused = "0.1.0" | ||
| | ^^^^^^^^^^^^^^^^ | ||
| | | ||
| = [NOTE] `cargo::unused_dependencies` is set to `warn` by default | ||
| [HELP] remove the dependency | ||
| | | ||
| 9 - unused = "0.1.0" | ||
| | | ||
| [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s |
There was a problem hiding this comment.
As this happens during compilation, ideally we tie into the existing warning/error count and work with build.warnings but haven't gotten all of that to work yet.
| [WARNING] unused dependency | ||
| --> Cargo.toml:9:13 | ||
| | | ||
| 9 | unused = "0.1.0" | ||
| | ^^^^^^^^^^^^^^^^ | ||
| | | ||
| = [NOTE] `cargo::unused_dependencies` is set to `warn` by default | ||
| [HELP] remove the dependency | ||
| | | ||
| 9 - unused = "0.1.0" | ||
| | | ||
| [UPDATING] `dummy-registry` index | ||
| [LOCKING] 1 package to latest compatible version | ||
| [DOWNLOADING] crates ... | ||
| [DOWNLOADED] unused v0.1.0 (registry `dummy-registry`) | ||
| [CHECKING] foo v0.1.0 ([ROOT]/foo) | ||
| [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s |
There was a problem hiding this comment.
Note that this looks different than the others because there is no build.rs so there is no Unit to report the message, so we lint this during the regular lint pass.
| [dependencies] | ||
| unused = "0.1.0" | ||
| lib_used = "0.1.0" | ||
| bins_used = "0.1.0" |
There was a problem hiding this comment.
side note: I would love to find a way to tell that we should lint that bins_used should be moved to a feature that the bins require so we can help people cut down on accidentally including bin deps with their libs.
| [dependencies] | ||
| used_dev = "0.1.0" | ||
|
|
||
| [lints.cargo] | ||
| unused_dependencies = "warn" | ||
| "#, | ||
| ) | ||
| .file( | ||
| "src/main.rs", | ||
| r#" | ||
| fn main() {} | ||
| "#, | ||
| ) | ||
| .file( | ||
| "tests/foo.rs", | ||
| r#" | ||
| #[test] | ||
| fn foo { | ||
| use used_dev as _; | ||
| } | ||
| "#, |
There was a problem hiding this comment.
Ideally this would provide a hint that used_dev should be moved to dev-dependencies. Based on the code, it seems like #8437 supported that but haven't dug into what all it takes to report it.
| [HELP] remove the dependency | ||
| | | ||
| 11 - bar.path = "../bar" | ||
| 11 + .path = "../bar" | ||
| | |
There was a problem hiding this comment.
More problems with how we get spans
|
CC @est31 |
| use crate::lints::get_key_value_span; | ||
| use crate::lints::rel_cwd_manifest_path; | ||
|
|
||
| pub const LINT: Lint = Lint { |
There was a problem hiding this comment.
I didn't do anything in particular for artifact dependencies. I assume that I would just put it on the artifact-deps tracking issue as something to investigate.
| use crate::lints::get_key_value_span; | ||
| use crate::lints::rel_cwd_manifest_path; | ||
|
|
||
| pub const LINT: Lint = Lint { |
There was a problem hiding this comment.
One false positive I found is with cargo itself: we don't directly use libgit2-sys but we indirectly use it and activate a feature of it. We do that with our features table but that could just as well be done by just setting a feature on the dependency
There was a problem hiding this comment.
For a potential workaround, see https://github.com/rust-lang/cargo/pull/16600/changes#r2776252783
There was a problem hiding this comment.
We talked about this in the cargo team meeting.
Options
- A lint config
- Doesn't compose well with workspace inheritance without the ability to extend what we inherit (Packages overriding inherited lints in Cargo.toml + adding lints #13157)
- distant from the affected data
_prefix- Matches rust
- We were concerned about using
_to mark hidden features (Don't display "hidden" features incargo add#10794) - Gets weird with kebab-case dependencies
- Affects sort order
- A
used = truefield with maybeused.reason = "feature activation"- Similar to
#[allow]or#[expect]in keeping it local - TOML syntax however doesn't allow general lint control
- Extending our schema for general lint control could be messy
- Question is whether having one-off lint control for this is justified
- Similar to
There was a problem hiding this comment.
https://github.com/est31/cargo-udeps?tab=readme-ov-file#ignoring-some-of-the-dependencies
[package.metadata.cargo-udeps.ignore]
normal = ["if_chain"]
#development = []
#build = []
[dependencies]
if_chain = "1.0.0" # Used only in doc-tests, which `cargo-udeps` cannot check.There was a problem hiding this comment.
https://github.com/bnjbvr/cargo-machete?tab=readme-ov-file#false-positives
[dependencies]
prost = "0.10" # Used in code generated by build.rs output, which cargo-machete cannot check
# in an individual package Cargo.toml
[package.metadata.cargo-machete]
ignored = ["prost"]
# in a workspace Cargo.toml
[workspace.metadata.cargo-machete]
ignored = ["prost"]There was a problem hiding this comment.
https://github.com/Boshen/cargo-shear
[package.metadata.cargo-shear]
ignored = ["crate-name"]| continue; | ||
| } | ||
|
|
||
| for (ext, dependency) in &state.externs { |
There was a problem hiding this comment.
#8437 would automatically skip any dependency of the form:
_foo = { package = "foo", ...}I removed that but we can consider adding it back in.
There was a problem hiding this comment.
See #16600 (comment) for a potential use case
| let toml_lints = pkg | ||
| .manifest() | ||
| .normalized_toml() | ||
| .lints | ||
| .clone() | ||
| .map(|lints| lints.lints) | ||
| .unwrap_or(manifest::TomlLints::default()); | ||
| let cargo_lints = toml_lints | ||
| .get("cargo") | ||
| .cloned() | ||
| .unwrap_or(manifest::TomlToolLints::default()); |
There was a problem hiding this comment.
During our regular lint pass, we calculate this and then pass it into each lint rule. Here, we have to re-calculate it and would likely need to do it again for each additional lint.
|
side-question (if is acceptable): rust-lang/rust#143856 (comment) |
b60a278 to
bb24b5e
Compare
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| if build_runner.bcx.gctx.cli_unstable().cargo_lints { | ||
| base.arg("-Wunused_crate_dependencies"); |
There was a problem hiding this comment.
Is this intentionally placed after the call to extra_args_for? I'm just wondering about the interaction of things like cargo rustc -- -Dwarnings or other such flags that could conflict with this.
There was a problem hiding this comment.
My intention was to have this very last so that any user provided modification of unused_crate_dependencies would be overridden unless it was in source code which we can't do anything about.
If there are some particular cases you'd like covered to see how they behave, I can add them.
This refactors the drain_the_queue function to return a tuple containing Result. That way, it's still not possible to use ? or try! to handle errors, but for readers of the function declaration it's clearer now that the error actually indicates one. Bonus: it makes the calling code of drain_the_queue simpler.
Fixes rust-lang#15813 Built-on rust-lang#8437 This does nothing for dev-dependencies because there isn't really a way to select all targets today without - tracking selected dep kinds to check on a per-package basis - checking the status of every bench to see if it can work as a test because `cargo test` (no args) with benches set to test is the only command today that can exercise all dev-dependencies as it is the only one that will compile tests and doctests. See also - https://blog.rust-lang.org/inside-rust/2024/10/01/this-development-cycle-in-cargo-1.82/#detecting-unused-dependencies - https://blog.rust-lang.org/inside-rust/2024/10/01/this-development-cycle-in-cargo-1.82/#all-targets-and-doc-tests - https://blog.rust-lang.org/inside-rust/2024/10/31/this-development-cycle-in-cargo-1.83/#target-and-target
I've added doc comments where I thought they may be helpful |
There was a problem hiding this comment.
I'm assuming the changes in this file are mostly unrelated. Do we want to alter these tests so they don't trigger cargo::unused_dependencies?
| if lint_level.is_error() { | ||
| *error_count += 1; | ||
| } | ||
| gctx.shell().print_report(&report, lint_level.force())?; |
There was a problem hiding this comment.
Just a side note, I'm not sure if ya'll have considered having the emitter responsible for tracking the error_count so that it wouldn't be necessary to manually manage the count (similar to DiagCtxt in rustc).
There was a problem hiding this comment.
That is something worth exploring as we evolve the linting systec. I'd prefer it not to be on GlobalContext. As we'd need to track which package we are in, we can also use it to determine when the emitted reason should be shown.
Thanks for looking! I appreciate the extra eyes on the compilation process and do expect hands on experience will help with what we want the experience to be. Tempted to start this in the nursery group just so we don't have to wait for it to be fully polished (e.g. dealing with false positives). |
|
Thanks @epage for doing this. Nice work, especially love that it prints spans in Cargo.toml. |
What does this PR try to resolve?
Fixes #15813
This checks only build and normal dependencies (see below for more details). I would expect we'd open a new issue to track dev-dependencies. I don't consider this a false-positive because there technically isn't a way to ask for everything that uses dev-dependencies to be built.
This only checks selected packages, and not all local packages, and only if the build target selection flags are exhaustive without consideration for the selected packages. See #16600 (comment)
How to test and review this PR?
Built-on #8437
This does nothing for dev-dependencies because there isn't really a way
to select all targets today without
because
cargo test(no args) with benches set to test is the onlycommand today that can exercise all dev-dependencies as it is the only
one that will compile tests and doctests.
See also
As for the commits, I did something unusual for myself in that I made changes with dead code so it is easier to understand what goes into one step in this process without seeing the entire process at once and getting confused.