-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add flag to warn about unused dependencies #8437
Conversation
r? @ehuss (rust_highfive has picked a reviewer for you, use r? to override) |
bfa9e29
to
3fa84f7
Compare
8052e20
to
6133918
Compare
This is a table of some commands and what they actually compile/check:
So practically in almost every instance cargo can't warn about dev-dependencies without making false positives possible. |
1ae02d1
to
f5f2c81
Compare
If cargo can't support dependency warnings as a side effect of normal builds, does dependency checking need to be its own explicit operation? Eg |
@jsgf in fact I think a command that compiles every part of a crate would be useful even outside of the unused dependencies scenario, to ensure that no code is breaking the build. Currently there is no such command. But I think that should better be discussed separately from this PR, as the PR implements checks for normal and build dependencies as well, and those checks are both more helpful (people depending on your crate don't compile your dev deps, only the normal and build deps), as well as more easily triggered (cargo build/check/test without any params all trigger the warnings). A rejection of such a mode, or any technical difficulties with implementing it shouldn't slow down the introduction of warnings for normal and build deps. |
This is a pretty slick way to detect unused dependencies, thanks for prototyping this! One thing I figured I'd comment on is that warnings is one thing Cargo has historically not handled well. Generally with warnings you want the ability to do something like "turn them all to errors" or "allow them for this project or temporarily". This is handled really well in the compiler with More concretely, I think that new warnings could fall short in a few situations:
Since this is being developed in tandem with rustc I think it would be great to basically just figure out how to get rustc to emit these warnings one way or another. Either that or let rustc make all the decisions about whether Cargo should be allowing or denying warnings. Or... something along those lines, I don't know of a great solution myself, but I wanted to at least write this up since I think it will be important to get right. |
Yeah, before the feature is stabilized and enabled by default there should be a way to turn off the warnings at least. I'm not certain myself how it should look like so I thought it should be done after the PR but we can already discuss it now.
There's already support for this in the PR! It works just like _bar = { package = "bar", version = "0.1.0" } But there is also a way to do it from Rust code via
In fact this has already been implemented for buck use cases in rust-lang/rust#72342 , but for cargo it is not suiting, as cargo doesn't have a 1:1 correspondence between dependencies and compilation units. In order to detect that an entry in the
rustc would have to tell this info to cargo in some way so that cargo could unify the output from multiple units and tell the result. I could add info on whether to allow, deny, or warn to the json output communicated by rustc. |
☔ The latest upstream changes (presumably #8427) made this pull request unmergeable. Please resolve the merge conflicts. |
One idea we had for warning control in yesterday's Cargo meeting is that rustc could emit warnings/errors for unused crates (e.g. unused This strategy would require Cargo to be able to identify lints precisely for unused crates (it's either already in the JSON structure or presumably we could add it). It would also require the addition of a "non fatal error" in rustc where if the lint is Unifying this across many crates would look something like:
That will require more changes in rustc for sure, but it seems quite appealing in terms of being able to control lint levels in Cargo. |
@alexcrichton if I have this code: fn hello() {
}
mod foo {
}
mod bar {
} Then why should a Also see the PR description of rust-lang/rust#73945 for further issues with that approach under the "Why not pass -Wunused-crate-dependencies?" section. For controlling lint levels, I think that cargo should think about ways of allowing/denying lints for all units in a crate or even workspace, as that's usually what people want when they control lints in their crate lib.rs root (#5034). Control over the unused dependencies lint could build on that. Depending on the name, if it starts with |
We explicitly are trying to avoid a system of managing lints in Cargo. It's not perfect to use rustc for this, but IMO it's way better than inventing something entirely new for Cargo. |
Who is "we" in that sentence? You? The entire cargo team? Most of the complexity of a "lints for cargo" system would already be covered by #5034 , so not really be a cost introduced by the unused dependencies lint.
It would make the unused dependencies lint different from all other lints. Furthermore, cargo wouldn't just always re-emit that warning/error unchanged if it wants to support "this should rather be a dev-dependency" or (in the future) "this should rather be a bin-dependency" like messages (wich this PR already does). So the entire distinction between warning/error/etc would have to be supported by cargo. Another issue would be that it would make doctests more complicated. Rustdoc has to collect the reports of all doc snippets and combine them. The more simple the format is, the easier this combination. It also offloads a lot of the ugly hacks to rustc so not sure whether the rustc team will support that (or the rustdoc team). THAT being said, if all three teams are aware of what the proposal entails and okay with that, I'm ok with it as well. |
The "we" is the Cargo team. If you'd like to block this on #5034 that's your choice, but AFAIK there are no plans to implement that. What I mentioned was a suggestion, if you don't want to pursue it, that's ok. The Cargo team's requirements have been laid out here and we're offering one way to do it, but if you'd rather do something else that checks the boxes that should work too. |
Sounds fair! I'll think a bit about other approaches that satisfy the demands of the cargo team. Note though that I won't have that much time to work on it in the next few months. The next thing I'll focus on will probably be json output for rustdoc errors. The mode will be required for implementation methods. |
☔ The latest upstream changes (presumably #8701) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
☔ The latest upstream changes (presumably #8777) made this pull request unmergeable. Please resolve the merge conflicts. Note that reviewers usually do not review pull requests until merge conflicts are resolved! Once you resolve the conflicts, you should change the labels applied by bors to indicate that your PR is ready for review. Post this as a comment to change the labels:
|
…crum Add an unstable --json=unused-externs flag to print unused externs This adds an unstable flag to print a list of the extern names not used by cargo. This PR will enable cargo to collect unused dependencies from all units and provide warnings. The companion PR to cargo is: rust-lang/cargo#8437 The goal is eventual stabilization of this flag in rustc as well as in cargo. Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603 The feature builds upon the internal datastructures added by rust-lang#72342 Externs are uniquely identified by name and the information is sufficient for cargo. If the mode is enabled, rustc will print json messages like: ``` {"unused_extern_names":["byteorder","openssl","webpki"]} ``` For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them. ### Q: Why not pass -Wunused-crate-dependencies? A: See [ehuss's comment here](rust-lang#57274 (comment)) TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo. Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end. Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead. ### Q: Make rustc emit used or unused externs? A: Emitting used externs has the advantage that it simplifies cargo's collection job. However, emitting unused externs creates less data to be communicated between rustc and cargo. Often you want to paste a cargo command obtained from `cargo build -vv` for doing something completely unrelated. The message is emitted always, even if no warning or error is emitted. At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs. ### Q: One json msg per extern or a collective json msg? A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers. Also it helps the cargo implementation to know that there aren't more unused deps coming. ### Q: Why use names of externs instead of e.g. paths? A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg. Names are sufficient because you *must* pass a name when passing an `--extern` arg. Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system. You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`, but rustc will only ever use one of those paths. Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path. So paths are ill-suited for identification. ### Q: What about 2015 edition crates? A: They are fully supported. Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways). So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar. The lint won't fire if your sole use in the crate is through a `extern crate foo;` statement, but that's not its job. For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint which can be enabled by `#![warn(unused_extern_crates)]` or similar. cc `@jsgf` `@ehuss` `@petrochenkov` `@estebank`
…crum Add an unstable --json=unused-externs flag to print unused externs This adds an unstable flag to print a list of the extern names not used by cargo. This PR will enable cargo to collect unused dependencies from all units and provide warnings. The companion PR to cargo is: rust-lang/cargo#8437 The goal is eventual stabilization of this flag in rustc as well as in cargo. Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603 The feature builds upon the internal datastructures added by rust-lang#72342 Externs are uniquely identified by name and the information is sufficient for cargo. If the mode is enabled, rustc will print json messages like: ``` {"unused_extern_names":["byteorder","openssl","webpki"]} ``` For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them. ### Q: Why not pass -Wunused-crate-dependencies? A: See [ehuss's comment here](rust-lang#57274 (comment)) TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo. Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end. Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead. ### Q: Make rustc emit used or unused externs? A: Emitting used externs has the advantage that it simplifies cargo's collection job. However, emitting unused externs creates less data to be communicated between rustc and cargo. Often you want to paste a cargo command obtained from `cargo build -vv` for doing something completely unrelated. The message is emitted always, even if no warning or error is emitted. At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs. ### Q: One json msg per extern or a collective json msg? A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers. Also it helps the cargo implementation to know that there aren't more unused deps coming. ### Q: Why use names of externs instead of e.g. paths? A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg. Names are sufficient because you *must* pass a name when passing an `--extern` arg. Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system. You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`, but rustc will only ever use one of those paths. Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path. So paths are ill-suited for identification. ### Q: What about 2015 edition crates? A: They are fully supported. Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways). So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar. The lint won't fire if your sole use in the crate is through a `extern crate foo;` statement, but that's not its job. For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint which can be enabled by `#![warn(unused_extern_crates)]` or similar. cc ``@jsgf`` ``@ehuss`` ``@petrochenkov`` ``@estebank``
…crum Add an unstable --json=unused-externs flag to print unused externs This adds an unstable flag to print a list of the extern names not used by cargo. This PR will enable cargo to collect unused dependencies from all units and provide warnings. The companion PR to cargo is: rust-lang/cargo#8437 The goal is eventual stabilization of this flag in rustc as well as in cargo. Discussion of this feature is mostly contained inside these threads: rust-lang#57274 rust-lang#72342 rust-lang#72603 The feature builds upon the internal datastructures added by rust-lang#72342 Externs are uniquely identified by name and the information is sufficient for cargo. If the mode is enabled, rustc will print json messages like: ``` {"unused_extern_names":["byteorder","openssl","webpki"]} ``` For a crate that got passed byteorder, openssl and webpki dependencies but needed none of them. ### Q: Why not pass -Wunused-crate-dependencies? A: See [ehuss's comment here](rust-lang#57274 (comment)) TLDR: it's cleaner. Rust's warning system wasn't built to be filtered or edited by cargo. Even a basic implementation of the feature would have to change the "n warnings emitted" line that rustc prints at the end. Cargo ideally wants to synthesize its own warnings anyways. For example, it would be hard for rustc to emit warnings like "dependency foo is only used by dev targets", suggesting to make it a dev-dependency instead. ### Q: Make rustc emit used or unused externs? A: Emitting used externs has the advantage that it simplifies cargo's collection job. However, emitting unused externs creates less data to be communicated between rustc and cargo. Often you want to paste a cargo command obtained from `cargo build -vv` for doing something completely unrelated. The message is emitted always, even if no warning or error is emitted. At that point, even this tiny difference in "noise" matters. That's why I went with emitting unused externs. ### Q: One json msg per extern or a collective json msg? A: Same as above, the data format should be concise. Having 30 lines for the 30 crates a crate uses would be disturbing to readers. Also it helps the cargo implementation to know that there aren't more unused deps coming. ### Q: Why use names of externs instead of e.g. paths? A: Names are both sufficient as well as neccessary to uniquely identify a passed `--extern` arg. Names are sufficient because you *must* pass a name when passing an `--extern` arg. Passing a path is optional on the other hand so rustc might also figure out a crate's location from the file system. You can also put multiple paths for the same extern name, via e.g. `--extern hello=/usr/lib/hello.rmeta --extern hello=/usr/local/lib/hello.rmeta`, but rustc will only ever use one of those paths. Also, paths don't identify a dependency uniquely as it is possible to have multiple different extern names point to the same path. So paths are ill-suited for identification. ### Q: What about 2015 edition crates? A: They are fully supported. Even on the 2015 edition, an explicit `--extern` flag is is required to enable `extern crate foo;` to work (outside of sysroot crates, which this flag doesn't warn about anyways). So the lint would still fire on 2015 edition crates if you haven't included a dependency specified in Cargo.toml using `extern crate foo;` or similar. The lint won't fire if your sole use in the crate is through a `extern crate foo;` statement, but that's not its job. For detecting unused `extern crate foo` statements, there is the `unused_extern_crates` lint which can be enabled by `#![warn(unused_extern_crates)]` or similar. cc ```@jsgf``` ```@ehuss``` ```@petrochenkov``` ```@estebank```
The companion PR has been merged, so I believe this is no longer blocked! |
@camelid I'll rebase the PR soon! |
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.
This commit adds an experimental flag to cargo, -Zwarn-unused-deps, with the goal of getting it eventually stabilized and enabled by default. The lint builds upon the --json=unused-externs flag of rustc that is being proposed for this purpose. This commit makes cargo pass the flag if -Zwarn-unused-deps is enabled to compilations of units it prints warnings for. During compilation, code collects the unused dependencies from all units, converts them into used dependencies, continuously extending the record of used dependencies for any type of DepKind: [dependencies], [dev-dependencies] and [build-dependencies]. Once the compilation has ended, this record is used to obtain those dependencies in a class that weren't used by any unit, and warn about them. The goal is to stabilize the flag and enable it by default once the lint has become robust and the robustness is proven. Then, cargo shall opportunistically warn about unused dependencies when it has compiled (or loaded the unused externs from cache of) all units that could possibly use dependencies of the kind. Roughly, it's like this: * cargo always compiles build.rs * cargo check compiles all units that would use [dependencies] * cargo test --no-run --all-targets compiles all units that can use [dev-dependencies]... except for the benches * cargo check --all-targets compiles all units that can use [dev-dependencies]... except for the doctests
Specifying via the command line is not possible for now because cargo currently has to pass -W via the command line, but once the lint is set to warn by default, this won't be needed :).
☔ The latest upstream changes (presumably #9369) made this pull request unmergeable. Please resolve the merge conflicts. |
I'm gonna close this due to inactivity. Feel free to reopen or create a new PR when you've got time to work on this again. Thanks! |
The main blocker at this point is having a line with only "yes" inside in this table: #8437 (comment) |
This commit adds an experimental flag to cargo, -Zwarn-unused-deps.
If the flag is enabled, cargo will warn about unused dependencies. rustc issue for the feature: rust-lang/rust#57274
The goal is to stabilize it eventually, enabling it by default.
The lint builds upon the --json=unused-externs flag of rustc
that is being proposed for this purpose in the companion PR rust-lang/rust#73945 .
This PR makes cargo pass the flag if -Zwarn-unused-deps is enabled to compilations of units it prints warnings for.
During compilation, cargo collects the unused dependencies
from all units, converts them into used dependencies,
continuously extending the record of used dependencies for
any type of DepKind: [dependencies], [dev-dependencies] and
[build-dependencies].
Once the compilation has ended, this record is used to
obtain those dependencies in a class that weren't used by any unit,
and warn about them.
The goal is to stabilize the flag and enable it by default
once the lint has become robust and the robustness is proven.
Then, cargo shall opportunistically warn about unused dependencies
when it has compiled (or loaded the unused externs from cache of)
all units that could possibly use dependencies of the kind.
Roughly, it's like this:
[dev-dependencies]
TODO
The PR is still WIP but I've opened it to get initial feedback. TODO list:
#[cfg(test)] use extern_crate as _;
cc @ehuss @jsgf