Suggest correct location for lifetime parameters in use

This commit is contained in:
Esteban Küber 2019-01-12 19:25:03 -08:00
parent db740313e0
commit 28ea03e114
7 changed files with 82 additions and 47 deletions

View File

@ -5330,23 +5330,28 @@ impl<'a> Parser<'a> {
/// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings,
/// possibly including trailing comma. /// possibly including trailing comma.
fn parse_generic_args(&mut self) fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
-> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
let mut args = Vec::new(); let mut args = Vec::new();
let mut bindings = Vec::new(); let mut bindings = Vec::new();
let mut seen_type = false; let mut seen_type = false;
let mut seen_binding = false; let mut seen_binding = false;
let mut first_type_or_binding_span: Option<Span> = None;
let mut bad_lifetime_pos = vec![];
let mut last_comma_span = None;
let mut suggestions = vec![];
loop { loop {
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument. // Parse lifetime argument.
args.push(GenericArg::Lifetime(self.expect_lifetime())); args.push(GenericArg::Lifetime(self.expect_lifetime()));
if seen_type || seen_binding { if seen_type || seen_binding {
self.struct_span_err( let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
self.prev_span, bad_lifetime_pos.push(self.prev_span);
"lifetime parameters must be declared prior to type parameters" if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
) suggestions.push((remove_sp, String::new()));
.span_label(self.prev_span, "must be declared prior to type parameters") suggestions.push((
.emit(); first_type_or_binding_span.unwrap().shrink_to_lo(),
format!("{}, ", snippet)));
}
} }
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) { } else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
// Parse associated type binding. // Parse associated type binding.
@ -5354,13 +5359,17 @@ impl<'a> Parser<'a> {
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
self.bump(); self.bump();
let ty = self.parse_ty()?; let ty = self.parse_ty()?;
let span = lo.to(self.prev_span);
bindings.push(TypeBinding { bindings.push(TypeBinding {
id: ast::DUMMY_NODE_ID, id: ast::DUMMY_NODE_ID,
ident, ident,
ty, ty,
span: lo.to(self.prev_span), span,
}); });
seen_binding = true; seen_binding = true;
if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(span);
}
} else if self.check_type() { } else if self.check_type() {
// Parse type argument. // Parse type argument.
let ty_param = self.parse_ty()?; let ty_param = self.parse_ty()?;
@ -5375,6 +5384,9 @@ impl<'a> Parser<'a> {
) )
.emit(); .emit();
} }
if first_type_or_binding_span.is_none() {
first_type_or_binding_span = Some(ty_param.span);
}
args.push(GenericArg::Type(ty_param)); args.push(GenericArg::Type(ty_param));
seen_type = true; seen_type = true;
} else { } else {
@ -5383,8 +5395,30 @@ impl<'a> Parser<'a> {
if !self.eat(&token::Comma) { if !self.eat(&token::Comma) {
break break
} else {
last_comma_span = Some(self.prev_span);
} }
} }
if !bad_lifetime_pos.is_empty() {
let mut err = self.struct_span_err(
bad_lifetime_pos.clone(),
"lifetime parameters must be declared prior to type parameters"
);
for sp in &bad_lifetime_pos {
err.span_label(*sp, "must be declared prior to type parameters");
}
if !suggestions.is_empty() {
err.multipart_suggestion_with_applicability(
&format!(
"move the lifetime parameter{} prior to the first type parameter",
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
),
suggestions,
Applicability::MachineApplicable,
);
}
err.emit();
}
Ok((args, bindings)) Ok((args, bindings))
} }

View File

@ -1,8 +1,17 @@
fn main() { // can't run rustfix because it doesn't handle multipart suggestions correctly
(0..4) // compile-flags: -Zborrowck=mir
.map(|x| x * 2) // we need the above to avoid ast borrowck failure in recovered code
.collect::<Vec<'a, usize, 'b>>()
//~^ ERROR lifetime parameters must be declared prior to type parameters struct S<'a, T> {
//~| ERROR use of undeclared lifetime name a: &'a T,
//~| ERROR use of undeclared lifetime name b: &'a T,
} }
fn foo<'a, 'b>(start: &'a usize, end: &'a usize) {
let _x = (*start..*end)
.map(|x| S { a: start, b: end })
.collect::<Vec<S<_, 'a>>>();
//~^ ERROR lifetime parameters must be declared prior to type parameters
}
fn main() {}

View File

@ -1,21 +1,12 @@
error: lifetime parameters must be declared prior to type parameters error: lifetime parameters must be declared prior to type parameters
--> $DIR/issue-14303-fncall.rs:4:31 --> $DIR/issue-14303-fncall.rs:13:29
| |
LL | .collect::<Vec<'a, usize, 'b>>() LL | .collect::<Vec<S<_, 'a>>>();
| ^^ must be declared prior to type parameters | ^^ must be declared prior to type parameters
help: move the lifetime parameter prior to the first type parameter
error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/issue-14303-fncall.rs:4:20
| |
LL | .collect::<Vec<'a, usize, 'b>>() LL | .collect::<Vec<S<'a, _>>>();
| ^^ undeclared lifetime | ^^^ --
error[E0261]: use of undeclared lifetime name `'b` error: aborting due to previous error
--> $DIR/issue-14303-fncall.rs:4:31
|
LL | .collect::<Vec<'a, usize, 'b>>()
| ^^ undeclared lifetime
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0261`.

View File

@ -9,6 +9,5 @@ mod foo {
fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
//~^ ERROR lifetime parameters must be declared prior to type parameters //~^ ERROR lifetime parameters must be declared prior to type parameters
//~| ERROR lifetime parameters must be declared prior to type parameters
fn main() {} fn main() {}

View File

@ -2,13 +2,13 @@ error: lifetime parameters must be declared prior to type parameters
--> $DIR/issue-14303-path.rs:10:40 --> $DIR/issue-14303-path.rs:10:40
| |
LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
| ^^ must be declared prior to type parameters | ^^ ^^ must be declared prior to type parameters
| |
error: lifetime parameters must be declared prior to type parameters | must be declared prior to type parameters
--> $DIR/issue-14303-path.rs:10:44 help: move the lifetime parameters prior to the first type parameter
| |
LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, 'b, 'c, T>) {}
| ^^ must be declared prior to type parameters | ^^^ ^^^ --
error: aborting due to 2 previous errors error: aborting due to previous error

View File

@ -1,8 +1,6 @@
// A few contrived examples where lifetime should (or should not) be parsed as an object type. // A few contrived examples where lifetime should (or should not) be parsed as an object type.
// Lifetimes parsed as types are still rejected later by semantic checks. // Lifetimes parsed as types are still rejected later by semantic checks.
// compile-flags: -Z continue-parse-after-error
struct S<'a, T>(&'a u8, T); struct S<'a, T>(&'a u8, T);
fn main() { fn main() {

View File

@ -1,29 +1,33 @@
error: lifetime parameters must be declared prior to type parameters error: lifetime parameters must be declared prior to type parameters
--> $DIR/trait-object-vs-lifetime.rs:16:25 --> $DIR/trait-object-vs-lifetime.rs:14:25
| |
LL | let _: S<'static +, 'static>; LL | let _: S<'static +, 'static>;
| ^^^^^^^ must be declared prior to type parameters | ^^^^^^^ must be declared prior to type parameters
help: move the lifetime parameter prior to the first type parameter
|
LL | let _: S<'static, 'static +>;
| ^^^^^^^^ --
error[E0224]: at least one non-builtin trait is required for an object type error[E0224]: at least one non-builtin trait is required for an object type
--> $DIR/trait-object-vs-lifetime.rs:11:23 --> $DIR/trait-object-vs-lifetime.rs:9:23
| |
LL | let _: S<'static, 'static +>; LL | let _: S<'static, 'static +>;
| ^^^^^^^^^ | ^^^^^^^^^
error[E0107]: wrong number of lifetime arguments: expected 1, found 2 error[E0107]: wrong number of lifetime arguments: expected 1, found 2
--> $DIR/trait-object-vs-lifetime.rs:13:23 --> $DIR/trait-object-vs-lifetime.rs:11:23
| |
LL | let _: S<'static, 'static>; LL | let _: S<'static, 'static>;
| ^^^^^^^ unexpected lifetime argument | ^^^^^^^ unexpected lifetime argument
error[E0107]: wrong number of type arguments: expected 1, found 0 error[E0107]: wrong number of type arguments: expected 1, found 0
--> $DIR/trait-object-vs-lifetime.rs:13:12 --> $DIR/trait-object-vs-lifetime.rs:11:12
| |
LL | let _: S<'static, 'static>; LL | let _: S<'static, 'static>;
| ^^^^^^^^^^^^^^^^^^^ expected 1 type argument | ^^^^^^^^^^^^^^^^^^^ expected 1 type argument
error[E0224]: at least one non-builtin trait is required for an object type error[E0224]: at least one non-builtin trait is required for an object type
--> $DIR/trait-object-vs-lifetime.rs:16:14 --> $DIR/trait-object-vs-lifetime.rs:14:14
| |
LL | let _: S<'static +, 'static>; LL | let _: S<'static +, 'static>;
| ^^^^^^^^^ | ^^^^^^^^^