est31 / cargo-udeps

Find unused dependencies in Cargo.toml
Other
1.68k stars 47 forks source link

Investigate using the unused crate rustc warning #70

Open est31 opened 4 years ago

est31 commented 4 years ago

rustc PR https://github.com/rust-lang/rust/pull/72342

Since the PR, rustc can be configured to emit unused crate warnings.

Suggested by ehuss here.

joshtriplett commented 3 weeks ago

@est31 I've tried that lint, and it seems to work well. I'd love to get a comparison of what cargo-udeps does above and beyond that lint.

est31 commented 3 weeks ago

The lint is enough for cases like buck where for each compilation unit you must specify the entire list of dependencies. For the cargo use case, one fundamentally can't implement something rustc side that works well: there is multiple compilation units that share a possible list of dependencies. A dependency can only be used by one of the compilation units.

For the main [dependencies]:

For the [build-dependencies]:

For the [dev-dependencies]:

cargo-udeps works in the way that it takes the output of all compilation units in a given compilation run and taking the intersection of all unused dependency warnings (or the de morgan equivalent for dependency usages).

cargo-udeps also performs a mapping from the crate name as it appears in the rust program to the name as it appears in Cargo.toml. Personally I think this should be the name that's exposed to the user, as the user needs to remove that name. Most of the time the two names match, but not always.

There is other questions like where the lint should point to: it should ideally point to the place in Cargo.toml that specifies the dependency.

I think if one wants there to be a good check together with cargo, there is two alternatives:

Middle forms are possible, like adding [bin-dependencies] and enabling the lint for the main lib.rs compilation unit, but keeping [dev-dependencies] without linting. In general, dependencies only used by binaries are highly useful anyways.

There is the further complication that target.'cfg(foobar)'.dependencies is not as expressive as #[cfg(foobar)] from what I remember (maybe this has now changed? idk). So in theory some cfg'd out code could use a dependency but one can't make the dependency also gated behind that cfg, at least not in all instances. I think it's okay to live with this limitation and just silence the lint for that crate, but it's an important thing to keep in mind.

joshtriplett commented 3 weeks ago

have cargo collect the info from all compilation units and then combine it -- this is what cargo-udeps is doing. still, one can't upstream it as there is not a single way to compile all of a package's compilation units in a single cargo invocation, so you always miss information from some compilation unit.

Does --all-targets not suffice for this?

joshtriplett commented 3 weeks ago

move cargo to a 1:1 model like buck

Frankly, I'd love to have this as an option someday. It'd be a massive tradeoff, but a huge win for a number of common cases. I'd love to have built-in udeps support before that, though.

est31 commented 3 weeks ago

Does --all-targets not suffice for this?

It doesn't, neither cargo check --all-targets nor cargo test --all-targets. I tried out all possible combinations once, and this was the resulting table: https://github.com/rust-lang/cargo/pull/8437#issuecomment-653842297 . The cargo test --all-targets command gets closest, but that one still doesn't check doctests.

joshtriplett commented 3 weeks ago

@est31 Ah, I didn't realize that. We should fix that; cargo check --all-targets / cargo build --all-targets seems like it should build everything. If we can't do that for backwards-compatibility reasons, then we should add a new option that does.

Would you consider filing (and nominating) an issue on cargo for building everything, with a link to the table you provided above?

est31 commented 2 weeks ago

I've just recreated the table on the latest rustc, and it is like before (I've added some extra lines):

cargo command lib.rs main.rs lib.rs cfg test tests/ benches/ doctests
c yes yes no no no no
c --all-targets yes yes yes yes yes no
t --no-run yes yes yes yes no no
t yes yes yes yes no yes
t --all-targets yes yes yes yes yes no
t --no-run --all-targets yes yes yes yes yes no
t --doc yes no no no no yes
clippy --all-targets yes yes yes yes yes no

So it seems that --all-targets works really well both for cargo check as well as for cargo test. The only problem is doctests, where there has been no change since I made the linked comment in 2020.

For missing doctest support, there is already two issues in cargo, one for cargo check and one for cargo test. I've linked them in the original table but they were behind the no so not easy to spot. Do you think it makes sense to file separate issues given these two already exist?

Maybe one could unify the two issues into one: --all-targets should always include all targets, regardless of whether it's cargo test, cargo clippy or cargo check.

If the cargo team could make --all-targets also check and run the docests, I think one could restart efforts from https://github.com/rust-lang/cargo/pull/8437 to upstream the unused crate warning.