Enhancement: Suggest `cargo update` when it could fix a compilation error
Problem
Two crates using the same library can fail to compile if one library version is updated in the Cargo.toml, but the other is not. If you do not run cargo update <other crate> then rustc will give you mysterious errors that tell you that the structs are mis-matched but do not hint that the problem is solvable via a cargo update.
I'm unsure if this is an enhancement that would fall in rustc or in cargo, but reporting here.
Proposed Solution
Desired
error[E0631]: type mismatch in function arguments
--> src/main.rs:7:89
|
3 | Cmd(fun_run::CmdError)
| --- found signature defined here
...
7 | print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(Errorz::Cmd).unwrap();
| ------- ^^^^^^^^^^^ expected due to this
| |
| required by a bound introduced by this call
|
= note: expected function signature `fn(fun_run::CmdError) -> _` imported by crate `fun_run`
found function signature `fn(CmdError) -> _` imported by crate `bullet_stream` that depends on `fun_run` (`>=0.5, <1`). Run `cargo update bullet_stream` to update it's locked sub-dependencies
note: required by a bound in `Result::<T, E>::map_err`
--> /Users/rschneeman/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:853:26
help: consider running `cargo update bullet_stream`
Also, it would be nice if cargo tree could identify dependencies that COULD be unified but aren't due to their locked status. It wouldn't be as nice as inside of the compiler error, but I was lead to cargo tree for debugging this problem. Perhaps we could update cargo tree to hint at possible cargo update options, and then compiler error could hint to run cargo tree (instead of trying to reverse the logic of how that dependency resolved to that type from that crate and that version, which seems hard as I type it out).
Notes
Full output of a reproduction:
See more
$ cat src/main.rs
use bullet_stream::global::print;
enum Errorz {
Cmd(fun_run::CmdError)
}
fn main() {
print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(Errorz::Cmd).unwrap();
}
$ cat Cargo.toml
[package]
name = "lol"
version = "0.1.0"
edition = "2024"
[dependencies]
bullet_stream = { version = "0.8.0", features = ["fun_run"]}
fun_run = "0.5.0"
$ cat Cargo.lock
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "bullet_stream"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc54f3252b0ff30201f78a466a017afef4da0ec6c669efcabef8d4cec7c0d58"
dependencies = [
"fun_run",
]
[[package]]
name = "fun_run"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60475c9540e62902b2b2e292be87f61df540c7e0f0bdb92416fd5ae792450049"
dependencies = [
"regex",
]
[[package]]
name = "lol"
version = "0.1.0"
dependencies = [
"bullet_stream",
"fun_run",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
$ cargo tree
lol v0.1.0 (/private/tmp/c3c7baffda7a90a43a79ebd5d642b33c/lol)
├── bullet_stream v0.8.0
│ └── fun_run v0.5.0
│ └── regex v1.11.1
│ ├── aho-corasick v1.1.3
│ │ └── memchr v2.7.4
│ ├── memchr v2.7.4
│ ├── regex-automata v0.4.9
│ │ ├── aho-corasick v1.1.3 (*)
│ │ ├── memchr v2.7.4
│ │ └── regex-syntax v0.8.5
│ └── regex-syntax v0.8.5
└── fun_run v0.5.0 (*)
$ echo "Update Cargo.toml"
Update Cargo.toml
$ cat Cargo.toml
[package]
name = "lol"
version = "0.1.0"
edition = "2024"
[dependencies]
bullet_stream = { version = "0.8.0", features = ["fun_run"]}
fun_run = "0.6.0"
$ cargo tree
lol v0.1.0 (/private/tmp/c3c7baffda7a90a43a79ebd5d642b33c/lol)
├── bullet_stream v0.8.0
│ └── fun_run v0.5.0
│ └── regex v1.11.1
│ ├── aho-corasick v1.1.3
│ │ └── memchr v2.7.4
│ ├── memchr v2.7.4
│ ├── regex-automata v0.4.9
│ │ ├── aho-corasick v1.1.3 (*)
│ │ ├── memchr v2.7.4
│ │ └── regex-syntax v0.8.5
│ └── regex-syntax v0.8.5
└── fun_run v0.6.0
└── regex v1.11.1 (*)
$ cargo build
Compiling lol v0.1.0 (/private/tmp/c3c7baffda7a90a43a79ebd5d642b33c/lol)
error[E0631]: type mismatch in function arguments
--> src/main.rs:7:90
|
3 | Cmd(fun_run::CmdError)
| --- found signature defined here
...
7 | print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(Errorz::Cmd).unwrap();
| ------- ^^^^^^^^^^^ expected due to this
| |
| required by a bound introduced by this call
|
= note: expected function signature `fn(fun_run::CmdError) -> _`
found function signature `fn(CmdError) -> _`
note: required by a bound in `Result::<T, E>::map_err`
--> /Users/rschneeman/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:853:26
|
853 | pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> Result<T, F> {
| ^^^^^^^^^^^^^^ required by this bound in `Result::<T, E>::map_err`
help: consider wrapping the function in a closure
|
7 | print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(|arg0: fun_run::CmdError| Errorz::Cmd(/* CmdError */)).unwrap();
| +++++++++++++++++++++++++ ++++++++++++++++
For more information about this error, try `rustc --explain E0631`.
error: could not compile `lol` (bin "lol") due to 1 previous error
$ cargo build
Compiling lol v0.1.0 (/private/tmp/c3c7baffda7a90a43a79ebd5d642b33c/lol)
error[E0631]: type mismatch in function arguments
--> src/main.rs:7:90
|
3 | Cmd(fun_run::CmdError)
| --- found signature defined here
...
7 | print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(Errorz::Cmd).unwrap();
| ------- ^^^^^^^^^^^ expected due to this
| |
| required by a bound introduced by this call
|
= note: expected function signature `fn(fun_run::CmdError) -> _`
found function signature `fn(CmdError) -> _`
note: required by a bound in `Result::<T, E>::map_err`
--> /Users/rschneeman/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:853:26
|
853 | pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> Result<T, F> {
| ^^^^^^^^^^^^^^ required by this bound in `Result::<T, E>::map_err`
help: consider wrapping the function in a closure
|
7 | print::sub_stream_cmd(std::process::Command::new("echo").arg("hello world")).map_err(|arg0: fun_run::CmdError| Errorz::Cmd(/* CmdError */)).unwrap();
| +++++++++++++++++++++++++ ++++++++++++++++
For more information about this error, try `rustc --explain E0631`.
error: could not compile `lol` (bin "lol") due to 1 previous error
I thought the compiler has made improvements to similar errors, calling out the two different crates involved. @estebank did that get merged or is this another case that needs to be covered?
Proposed Solution
My gut says it would be a lot of work to get the error messages to the level of quality suggested, either requiring telling rustc a lot more information about Cargo than it should know or rustc passing back a lot of information for Cargo to re-render the error.
Another avenue in this is to lower the need for this. Something we've considered once we have stabilized public/private dependencies is to align versions across dependencies. See https://rust-lang.github.io/rfcs/3516-public-private-dependencies.html#caller-declared-relations
Also, it would be nice if cargo tree could identify dependencies that COULD be unified but aren't due to their locked status. It wouldn't be as nice as inside of the compiler error, but I was lead to cargo tree for debugging this problem. Perhaps we could update cargo tree to hint at possible cargo update options, and then compiler error could hint to run cargo tree (instead of trying to reverse the logic of how that dependency resolved to that type from that crate and that version, which seems hard as I type it out).
If we did something like this, it'd need to be opt-in as that could get pretty noisy for people looking at it for other purposes.
We do have cargo tree --duplicates which is kind of like what you are talking about.
$ cargo tree -d --target all
windows-sys v0.52.0
├── anstyle-query v1.1.0
│ └── anstream v0.6.14
│ ├── anstyle-svg v0.1.7
│ │ └── snapbox v0.6.21
│ │ └── trycmd v0.15.9
│ │ [dev-dependencies]
│ │ └── clap v4.5.39 (/home/epage/src/personal/clap)
│ │ [dev-dependencies]
│ │ └── clap v4.5.39 (/home/epage/src/personal/clap)
│ ├── clap_builder v4.5.39 (/home/epage/src/personal/clap/clap_builder)
│ │ └── clap v4.5.39 (/home/epage/src/personal/clap)
│ ├── snapbox v0.6.21 (*)
│ ├── snapbox-macros v0.3.10
│ │ └── snapbox v0.6.21 (*)
│ └── trycmd v0.15.9 (*)
├── anstyle-wincon v3.0.3
│ └── anstream v0.6.14 (*)
├── os_pipe v1.2.0
│ └── snapbox v0.6.21 (*)
└── winapi-util v0.1.8
└── termcolor v1.4.1
└── trybuild v1.0.104
[dev-dependencies]
└── clap v4.5.39 (/home/epage/src/personal/clap)
windows-sys v0.59.0
├── jiff v0.2.11
│ [dev-dependencies]
│ └── clap v4.5.39 (/home/epage/src/personal/clap)
└── snapbox v0.6.21 (*)
I wonder if the compiler has enough information to suggest cargo tree -d fun_run. @estebank ?
Another option is for a lint, like #13899.
did that get merged or is this another case that needs to be covered?
That got merged and covered E0277 and E0308, but not E0631.
I wonder if the compiler has enough information to suggest
cargo tree -d fun_run
Today we look for the existence of the env var CARGO_CRATE_NAME to determine that rustc got invoked via cargo to customize E0433. We could add to the existing output a note to invoke cargo tree -d dep_name.