Description
Here's a tricky failure distilled from a real-world codebase. I've aggressively minimized the reproduction (including each of the individual dependencies; no remote dependencies remain); everything remaining is seemingly necessary to reproduce the failure (crate, item, and feature names have been preserved for easy comparison with the original).
Overview of the minimized dependency graph:
flowchart TD
mycrate-- "artifact = #quot;bin#quot;\ntarget = #quot;x86_64-unknown-linux-gnu#quot;" -->mybindep
mycrate-->A["structopt-derive\n(proc macro)"]
mybindep-->B["clap_derive\n(proc macro)"]
B["clap_derive\n(proc macro)"]-->proc-macro-error
A["structopt-derive\n(proc macro)"]-- "features = [#quot;parsing#quot;]" -->syn
A["structopt-derive\n(proc macro)"]-->proc-macro-error
proc-macro-error-->syn
I've published the minimized repo here: https://github.com/bstrie/bindeperror1 .
For completeness, here's a branch with the maximized reproduction in the original codebase: https://github.com/bstrie/enarx/tree/bindeperror1 (revert the last commit to observe the code compiles properly; if testing fixes against this branch be sure to remove the rust-toolchain file).
For posterity I've inlined the minimized repo at the bottom of this issue.
The code in question should compile. Instead, cargo build
produces the following error:
error[E0599]: the method `unwrap_or_abort` exists for enum `Result<(), syn::Error>`, but its trait bounds were not satisfied
--> structopt-derive/src/lib.rs:4:30
|
4 | Ok::<(), syn::Error>(()).unwrap_or_abort()
| ^^^^^^^^^^^^^^^
|
::: /home/ben/code/scrap/bindeperror1/syn/src/lib.rs:1:1
|
1 | pub struct Error;
| ----------------- doesn't satisfy `syn::Error: Into<proc_macro_error::Diagnostic>`
|
::: /home/ben/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:504:1
|
504 | pub enum Result<T, E> {
| --------------------- doesn't satisfy `Result<(), syn::Error>: ResultExt<()>`
|
= note: the following trait bounds were not satisfied:
`syn::Error: Into<proc_macro_error::Diagnostic>`
which is required by `Result<(), syn::Error>: ResultExt<()>`
warning: unused import: `proc_macro_error::ResultExt`
--> structopt-derive/src/lib.rs:1:5
|
1 | use proc_macro_error::ResultExt;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
For more information about this error, try `rustc --explain E0599`.
Interestingly, all of the following will cause the code to compile successfully:
- Removing the
target
key in mycrate's Cargo.toml, i.e.:causes the code to compile. This is extremely curious because the host machine is- mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-gnu" } + mybindep = { path = "mybindep", artifact = "bin" }
x86_64-unknown-linux-gnu
, so conceptually this should be a no-op. Furthermore, since these crates are proc macros, they should already be targeting the host regardless of the value of thetarget
field. Any other value in thetarget
field (e.g.x86_64-unknown-linux-musl
) produces the same error, so it is the presence of this key that is essential. - Removing the
features
key in structopt-derive's Cargo.toml, i.e.:causes the code to compile. Note that in our minimized syn crate this feature is completely vestigial and has no behavior at all.- syn = { path = "../syn", features = ["parsing"] } + syn = { path = "../syn" }
- Making both mycrate and mybindep rely on the same proc macro crate. This demonstrates that there's nothing problematic about structopt-derive or clap_derive in isolation, and that this error only occurs when they're both present.
Output of cargo version --verbose
:
cargo 1.61.0-nightly (109bfbd 2022-03-17)
release: 1.61.0-nightly
commit-hash: 109bfbd055325ef87a6e7f63d67da7e838f8300b
commit-date: 2022-03-17
host: x86_64-unknown-linux-gnu
libgit2: 1.4.2 (sys:0.14.2 vendored)
libcurl: 7.80.0-DEV (sys:0.4.51+curl-7.80.0 vendored ssl:OpenSSL/1.1.1m)
os: Pop!_OS 21.10 (impish) [64-bit]
cc @Byron
Code for reference:
# Cargo.toml
[package]
name = "mycrate"
version = "0.0.0"
edition = "2021"
[dependencies]
structopt-derive = { path = "structopt-derive" }
mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-gnu" }
// src/main.rs
fn main() {
env!("CARGO_BIN_FILE_MYBINDEP");
}
# mybindep/Cargo.toml
[package]
name = "mybindep"
version = "0.0.0"
edition = "2021"
[dependencies]
clap_derive = { path = "../clap_derive" }
// mybindep/src/main.rs
fn main() {}
# clap_derive/Cargo.toml
[package]
name = "clap_derive"
version = "0.0.0"
edition = "2021"
[dependencies]
proc-macro-error = { path = "../proc-macro-error" }
[lib]
proc-macro = true
// clap_derive/src/lib.rs
# structopt-derive/Cargo.toml
[package]
name = "structopt-derive"
version = "0.0.0"
edition = "2021"
[dependencies]
syn = { path = "../syn", features = ["parsing"] }
proc-macro-error = { path = "../proc-macro-error" }
[lib]
proc-macro = true
// structopt-derive/src/lib.rs
use proc_macro_error::ResultExt;
fn _parse_structopt_attributes() {
Ok::<(), syn::Error>(()).unwrap_or_abort()
}
# proc-macro-error/Cargo.toml
[package]
name = "proc-macro-error"
version = "0.0.0"
edition = "2021"
[dependencies]
syn = { path = "../syn" }
// proc-macro-error/src/lib.rs
pub trait ResultExt<T> {
fn unwrap_or_abort(self) -> T;
}
impl<T, E: Into<Diagnostic>> ResultExt<T> for Result<T, E> {
fn unwrap_or_abort(self) -> T {
panic!()
}
}
pub struct Diagnostic;
impl From<syn::Error> for Diagnostic {
fn from(_: syn::Error) -> Self {
panic!()
}
}
# syn/Cargo.toml
[package]
name = "syn"
version = "0.0.0"
edition = "2021"
[features]
parsing = []
// syn/src/lib.rs
pub struct Error;