diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a3948cc00d6f..ca13d749e524 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -19,7 +19,7 @@ use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use tracing::debug; @@ -446,12 +446,58 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_label(base_span, fallback_label); if let PathSource::Trait(AliasPossibility::Maybe) = source { - if let Some([start, .., end]) = self.diagnostic_metadata.current_trait_object { - err.span_help( - start.span().to(end.span()), - "`+` can be used to constrain a \"trait object\" type with lifetimes or \ - auto-traits, structs and enums can't be bound in that way", + if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + let spans: Vec = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_span) + .collect(); + + let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = bounds.iter().map(|bound| bound.span()).last().unwrap(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {} bound{}", + if bounds.len() <= 2 { "this" } else { "these" }, + if bounds.len() <= 2 { "" } else { "s" }, + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label( + base_span, + "expected this type to be a trait...".to_string(), ); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ + auto-traits; structs and enums can't be bound in that way", + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Trait(tr, _) => tr.span == base_span, + }) { + let mut sugg = vec![]; + if base_span != start_span { + sugg.push((start_span.until(base_span), String::new())); + } + if base_span != end_span { + sugg.push((base_span.shrink_to_hi().to(end_span), String::new())); + } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); + } } } match self.diagnostic_metadata.current_let_binding { diff --git a/src/test/ui/traits/trait-bounds-not-on-struct.rs b/src/test/ui/traits/trait-bounds-not-on-struct.rs index c6e93e755250..8633e9d7a4ce 100644 --- a/src/test/ui/traits/trait-bounds-not-on-struct.rs +++ b/src/test/ui/traits/trait-bounds-not-on-struct.rs @@ -1,9 +1,38 @@ +// We don't need those errors. Ideally we would silence them, but to do so we need to move the +// lint from being an early-lint during parsing to a late-lint, because it needs to be aware of +// the types involved. #![allow(bare_trait_objects)] struct Foo; fn foo(_x: Box) { } //~ ERROR expected trait, found struct `Foo` -type A = Box>; //~ ERROR expected trait, found struct `Vec` +type TypeAlias = Box>; //~ ERROR expected trait, found struct `Vec` -fn main() { } +struct A; +fn a() -> A + 'static { //~ ERROR expected trait, found + A +} +fn b<'a,T,E>(iter: Iterator + 'a>) { //~ ERROR expected trait, found + panic!() +} +fn c() -> 'static + A { //~ ERROR expected trait, found + A +} +fn d<'a,T,E>(iter: Iterator>) { //~ ERROR expected trait, found + panic!() +} +fn e() -> 'static + A + 'static { //~ ERROR expected trait, found +//~^ ERROR only a single explicit lifetime bound is permitted + A +} +fn f<'a,T,E>(iter: Iterator + 'a>) { //~ ERROR expected trait, found +//~^ ERROR only a single explicit lifetime bound is permitted + panic!() +} +struct Traitor; +trait Trait {} +fn g() -> Traitor + 'static { //~ ERROR expected trait, found struct `Traitor` + A +} +fn main() {} diff --git a/src/test/ui/traits/trait-bounds-not-on-struct.stderr b/src/test/ui/traits/trait-bounds-not-on-struct.stderr index 3d338ef3736c..526dac2db46c 100644 --- a/src/test/ui/traits/trait-bounds-not-on-struct.stderr +++ b/src/test/ui/traits/trait-bounds-not-on-struct.stderr @@ -1,21 +1,152 @@ +error[E0226]: only a single explicit lifetime bound is permitted + --> $DIR/trait-bounds-not-on-struct.rs:25:25 + | +LL | fn e() -> 'static + A + 'static { + | ^^^^^^^ + +error[E0226]: only a single explicit lifetime bound is permitted + --> $DIR/trait-bounds-not-on-struct.rs:29:53 + | +LL | fn f<'a,T,E>(iter: Iterator + 'a>) { + | ^^ + error[E0404]: expected trait, found struct `Foo` - --> $DIR/trait-bounds-not-on-struct.rs:5:16 + --> $DIR/trait-bounds-not-on-struct.rs:8:16 | LL | fn foo(_x: Box) { } | ^^^ not a trait | -help: `+` can be used to constrain a "trait object" type with lifetimes or auto-traits, structs and enums can't be bound in that way - --> $DIR/trait-bounds-not-on-struct.rs:5:16 +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:8:22 | LL | fn foo(_x: Box) { } - | ^^^^^^^^^^ + | --- ^^^^ ...because of this bound + | | + | expected this type to be a trait... error[E0404]: expected trait, found struct `Vec` - --> $DIR/trait-bounds-not-on-struct.rs:7:21 + --> $DIR/trait-bounds-not-on-struct.rs:10:29 | -LL | type A = Box>; - | ^^^^^^ not a trait +LL | type TypeAlias = Box>; + | ^^^^^^ not a trait -error: aborting due to 2 previous errors +error[E0404]: expected trait, found struct `A` + --> $DIR/trait-bounds-not-on-struct.rs:13:11 + | +LL | fn a() -> A + 'static { + | ^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:13:15 + | +LL | fn a() -> A + 'static { + | - ^^^^^^^ ...because of this bound + | | + | expected this type to be a trait... +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn a() -> A { + | -- -For more information about this error, try `rustc --explain E0404`. +error[E0404]: expected trait, found enum `Result` + --> $DIR/trait-bounds-not-on-struct.rs:16:34 + | +LL | fn b<'a,T,E>(iter: Iterator + 'a>) { + | ^^^^^^^^^^^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:16:48 + | +LL | fn b<'a,T,E>(iter: Iterator + 'a>) { + | ----------- ^^ ...because of this bound + | | + | expected this type to be a trait... +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn b<'a,T,E>(iter: Iterator>) { + | -- + +error[E0404]: expected trait, found struct `A` + --> $DIR/trait-bounds-not-on-struct.rs:19:21 + | +LL | fn c() -> 'static + A { + | ^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:19:11 + | +LL | fn c() -> 'static + A { + | ^^^^^^^ - expected this type to be a trait... + | | + | ...because of this bound +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn c() -> A { + | -- + +error[E0404]: expected trait, found enum `Result` + --> $DIR/trait-bounds-not-on-struct.rs:22:39 + | +LL | fn d<'a,T,E>(iter: Iterator>) { + | ^^^^^^^^^^^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:22:34 + | +LL | fn d<'a,T,E>(iter: Iterator>) { + | ^^ ----------- expected this type to be a trait... + | | + | ...because of this bound +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn d<'a,T,E>(iter: Iterator>) { + | -- + +error[E0404]: expected trait, found struct `A` + --> $DIR/trait-bounds-not-on-struct.rs:25:21 + | +LL | fn e() -> 'static + A + 'static { + | ^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:25:11 + | +LL | fn e() -> 'static + A + 'static { + | ^^^^^^^ - ^^^^^^^ ...because of these bounds + | | + | expected this type to be a trait... +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn e() -> A { + | --- + +error[E0404]: expected trait, found enum `Result` + --> $DIR/trait-bounds-not-on-struct.rs:29:39 + | +LL | fn f<'a,T,E>(iter: Iterator + 'a>) { + | ^^^^^^^^^^^ not a trait + | +help: `+` is used to constrain a "trait object" type with lifetimes or auto-traits; structs and enums can't be bound in that way + --> $DIR/trait-bounds-not-on-struct.rs:29:34 + | +LL | fn f<'a,T,E>(iter: Iterator + 'a>) { + | ^^ ----------- ^^ ...because of these bounds + | | + | expected this type to be a trait... +help: if you meant to use a type and not a trait here, remove the bounds + | +LL | fn f<'a,T,E>(iter: Iterator>) { + | -- -- + +error[E0404]: expected trait, found struct `Traitor` + --> $DIR/trait-bounds-not-on-struct.rs:35:11 + | +LL | trait Trait {} + | ----------- similarly named trait `Trait` defined here +LL | fn g() -> Traitor + 'static { + | ^^^^^^^ help: a trait with a similar name exists: `Trait` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0226, E0404. +For more information about an error, try `rustc --explain E0226`.