Tweak output and add test cases

This commit is contained in:
Esteban Küber 2020-09-22 19:19:33 -07:00
parent 711760c8ec
commit 5217007a20
3 changed files with 223 additions and 17 deletions

View File

@ -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<Span> = 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 {

View File

@ -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<Foo + Send>) { } //~ ERROR expected trait, found struct `Foo`
type A<T> = Box<dyn Vec<T>>; //~ ERROR expected trait, found struct `Vec`
type TypeAlias<T> = Box<dyn Vec<T>>; //~ 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<Item=Result<T,E> + 'a>) { //~ ERROR expected trait, found
panic!()
}
fn c() -> 'static + A { //~ ERROR expected trait, found
A
}
fn d<'a,T,E>(iter: Iterator<Item='a + Result<T,E>>) { //~ 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<Item='a + Result<T,E> + '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() {}

View File

@ -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<Item='a + Result<T,E> + '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<Foo + Send>) { }
| ^^^ 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<Foo + Send>) { }
| ^^^^^^^^^^
| --- ^^^^ ...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<T> = Box<dyn Vec<T>>;
| ^^^^^^ not a trait
LL | type TypeAlias<T> = Box<dyn Vec<T>>;
| ^^^^^^ 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<Item=Result<T,E> + '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<Item=Result<T,E> + '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<Item=Result<T,E>>) {
| --
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<Item='a + Result<T,E>>) {
| ^^^^^^^^^^^ 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<Item='a + Result<T,E>>) {
| ^^ ----------- 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<Item=Result<T,E>>) {
| --
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<Item='a + Result<T,E> + '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<Item='a + Result<T,E> + '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<Item=Result<T,E>>) {
| -- --
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`.