about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libsyntax/parse/parser.rs52
-rw-r--r--src/test/ui/parser/issue-14303-fncall.rs23
-rw-r--r--src/test/ui/parser/issue-14303-fncall.stderr23
-rw-r--r--src/test/ui/parser/issue-14303-path.rs1
-rw-r--r--src/test/ui/parser/issue-14303-path.stderr14
-rw-r--r--src/test/ui/traits/trait-object-vs-lifetime.rs2
-rw-r--r--src/test/ui/traits/trait-object-vs-lifetime.stderr14
7 files changed, 82 insertions, 47 deletions
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<GenericArg>, Vec<TypeBinding>)> {
+    fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
         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<Span> = 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::<Vec<'a, usize, 'b>>()
-    //~^ 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::<Vec<S<_, 'a>>>();
+        //~^ 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::<Vec<'a, usize, 'b>>()
-   |                               ^^ must be declared prior to type parameters
-
-error[E0261]: use of undeclared lifetime name `'a`
-  --> $DIR/issue-14303-fncall.rs:4:20
-   |
-LL |     .collect::<Vec<'a, usize, 'b>>()
-   |                    ^^ undeclared lifetime
-
-error[E0261]: use of undeclared lifetime name `'b`
-  --> $DIR/issue-14303-fncall.rs:4:31
+LL |         .collect::<Vec<S<_, 'a>>>();
+   |                             ^^ must be declared prior to type parameters
+help: move the lifetime parameter prior to the first type parameter
    |
-LL |     .collect::<Vec<'a, usize, 'b>>()
-   |                               ^^ undeclared lifetime
+LL |         .collect::<Vec<S<'a, _>>>();
+   |                          ^^^ --
 
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-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>;
    |              ^^^^^^^^^