diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 3f736c36111..4d0401097c4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5330,23 +5330,28 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, /// possibly including trailing comma. - fn parse_generic_args(&mut self) - -> PResult<'a, (Vec, Vec)> { + fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { let mut args = Vec::new(); let mut bindings = Vec::new(); let mut seen_type = false; let mut seen_binding = false; + let mut first_type_or_binding_span: Option = None; + let mut bad_lifetime_pos = vec![]; + let mut last_comma_span = None; + let mut suggestions = vec![]; loop { if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. args.push(GenericArg::Lifetime(self.expect_lifetime())); if seen_type || seen_binding { - self.struct_span_err( - self.prev_span, - "lifetime parameters must be declared prior to type parameters" - ) - .span_label(self.prev_span, "must be declared prior to type parameters") - .emit(); + let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span); + bad_lifetime_pos.push(self.prev_span); + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) { + suggestions.push((remove_sp, String::new())); + suggestions.push(( + first_type_or_binding_span.unwrap().shrink_to_lo(), + format!("{}, ", snippet))); + } } } else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) { // Parse associated type binding. @@ -5354,13 +5359,17 @@ impl<'a> Parser<'a> { let ident = self.parse_ident()?; self.bump(); let ty = self.parse_ty()?; + let span = lo.to(self.prev_span); bindings.push(TypeBinding { id: ast::DUMMY_NODE_ID, ident, ty, - span: lo.to(self.prev_span), + span, }); seen_binding = true; + if first_type_or_binding_span.is_none() { + first_type_or_binding_span = Some(span); + } } else if self.check_type() { // Parse type argument. let ty_param = self.parse_ty()?; @@ -5375,6 +5384,9 @@ impl<'a> Parser<'a> { ) .emit(); } + if first_type_or_binding_span.is_none() { + first_type_or_binding_span = Some(ty_param.span); + } args.push(GenericArg::Type(ty_param)); seen_type = true; } else { @@ -5383,8 +5395,30 @@ impl<'a> Parser<'a> { if !self.eat(&token::Comma) { 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)) } diff --git a/src/test/ui/parser/issue-14303-fncall.rs b/src/test/ui/parser/issue-14303-fncall.rs index f08b847ac32..17b9b766b21 100644 --- a/src/test/ui/parser/issue-14303-fncall.rs +++ b/src/test/ui/parser/issue-14303-fncall.rs @@ -1,8 +1,17 @@ -fn main() { - (0..4) - .map(|x| x * 2) - .collect::>() - //~^ ERROR lifetime parameters must be declared prior to type parameters - //~| ERROR use of undeclared lifetime name - //~| ERROR use of undeclared lifetime name +// can't run rustfix because it doesn't handle multipart suggestions correctly +// compile-flags: -Zborrowck=mir +// we need the above to avoid ast borrowck failure in recovered code + +struct S<'a, T> { + a: &'a T, + 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::>>(); + //~^ ERROR lifetime parameters must be declared prior to type parameters +} + +fn main() {} diff --git a/src/test/ui/parser/issue-14303-fncall.stderr b/src/test/ui/parser/issue-14303-fncall.stderr index fa2deafb673..2a736491594 100644 --- a/src/test/ui/parser/issue-14303-fncall.stderr +++ b/src/test/ui/parser/issue-14303-fncall.stderr @@ -1,21 +1,12 @@ 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::>() - | ^^ must be declared prior to type parameters - -error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/issue-14303-fncall.rs:4:20 +LL | .collect::>>(); + | ^^ must be declared prior to type parameters +help: move the lifetime parameter prior to the first type parameter | -LL | .collect::>() - | ^^ undeclared lifetime +LL | .collect::>>(); + | ^^^ -- -error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/issue-14303-fncall.rs:4:31 - | -LL | .collect::>() - | ^^ undeclared lifetime +error: aborting due to previous error -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/parser/issue-14303-path.rs b/src/test/ui/parser/issue-14303-path.rs index 8749d46818f..a08c89f3437 100644 --- a/src/test/ui/parser/issue-14303-path.rs +++ b/src/test/ui/parser/issue-14303-path.rs @@ -9,6 +9,5 @@ mod foo { 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 fn main() {} diff --git a/src/test/ui/parser/issue-14303-path.stderr b/src/test/ui/parser/issue-14303-path.stderr index 07e4441b0de..fb4fb32e11e 100644 --- a/src/test/ui/parser/issue-14303-path.stderr +++ b/src/test/ui/parser/issue-14303-path.stderr @@ -2,13 +2,13 @@ error: lifetime parameters must be declared prior to type parameters --> $DIR/issue-14303-path.rs:10:40 | LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {} - | ^^ must be declared prior to type parameters - -error: lifetime parameters must be declared prior to type parameters - --> $DIR/issue-14303-path.rs:10:44 + | ^^ ^^ must be declared prior to type parameters + | | + | must be declared prior to type parameters +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>) {} - | ^^ must be declared prior to type parameters +LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, 'b, 'c, T>) {} + | ^^^ ^^^ -- -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/traits/trait-object-vs-lifetime.rs b/src/test/ui/traits/trait-object-vs-lifetime.rs index 57d9b2df9f5..a12429c868e 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime.rs +++ b/src/test/ui/traits/trait-object-vs-lifetime.rs @@ -1,8 +1,6 @@ // 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. -// compile-flags: -Z continue-parse-after-error - struct S<'a, T>(&'a u8, T); fn main() { diff --git a/src/test/ui/traits/trait-object-vs-lifetime.stderr b/src/test/ui/traits/trait-object-vs-lifetime.stderr index 00f8164caa4..4cc96bae5cd 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime.stderr +++ b/src/test/ui/traits/trait-object-vs-lifetime.stderr @@ -1,29 +1,33 @@ 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>; | ^^^^^^^ 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 - --> $DIR/trait-object-vs-lifetime.rs:11:23 + --> $DIR/trait-object-vs-lifetime.rs:9:23 | LL | let _: S<'static, 'static +>; | ^^^^^^^^^ 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>; | ^^^^^^^ unexpected lifetime argument 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>; | ^^^^^^^^^^^^^^^^^^^ expected 1 type argument 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>; | ^^^^^^^^^