about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-01-25 16:59:29 +0100
committerGitHub <noreply@github.com>2019-01-25 16:59:29 +0100
commit7768358e72bfbd5d01cfc687899ee18513b5d753 (patch)
treede7bc1f897e8d1b1a75feca59991471b5a8e2cea
parent141fa859b87534846b7793d026342ffaf5ad4639 (diff)
parent7a0abbff8be746e46841ac7eef5a17364d6b8b51 (diff)
downloadrust-7768358e72bfbd5d01cfc687899ee18513b5d753.tar.gz
rust-7768358e72bfbd5d01cfc687899ee18513b5d753.zip
Rollup merge of #57886 - davidtwco:issue-57385, r=estebank
Add suggestion for moving type declaration before associated type bindings in generic arguments.

Fixes #57385.

r? @estebank
-rw-r--r--src/libsyntax/parse/parser.rs134
-rw-r--r--src/test/ui/parser/issue-32214.stderr4
-rw-r--r--src/test/ui/suggestions/suggest-move-types.rs85
-rw-r--r--src/test/ui/suggestions/suggest-move-types.stderr107
4 files changed, 303 insertions, 27 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index fb832afb748..57dcc42d9e6 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -5543,22 +5543,31 @@ impl<'a> Parser<'a> {
     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 last_comma_span = None;
         let mut first_type_or_binding_span: Option<Span> = None;
+        let mut first_binding_span: Option<Span> = None;
+
         let mut bad_lifetime_pos = vec![];
-        let mut last_comma_span = None;
-        let mut suggestions = vec![];
+        let mut bad_type_pos = vec![];
+
+        let mut lifetime_suggestions = vec![];
+        let mut type_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 {
                     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((
+                        lifetime_suggestions.push((remove_sp, String::new()));
+                        lifetime_suggestions.push((
                             first_type_or_binding_span.unwrap().shrink_to_lo(),
                             format!("{}, ", snippet)));
                     }
@@ -5576,24 +5585,29 @@ impl<'a> Parser<'a> {
                     ty,
                     span,
                 });
+
                 seen_binding = true;
                 if first_type_or_binding_span.is_none() {
                     first_type_or_binding_span = Some(span);
                 }
+                if first_binding_span.is_none() {
+                    first_binding_span = Some(span);
+                }
             } else if self.check_type() {
                 // Parse type argument.
                 let ty_param = self.parse_ty()?;
                 if seen_binding {
-                    self.struct_span_err(
-                        ty_param.span,
-                        "type parameters must be declared prior to associated type bindings"
-                    )
-                        .span_label(
-                            ty_param.span,
-                            "must be declared prior to associated type bindings",
-                        )
-                        .emit();
+                    let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
+                    bad_type_pos.push(self.prev_span);
+
+                    if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
+                        type_suggestions.push((remove_sp, String::new()));
+                        type_suggestions.push((
+                            first_binding_span.unwrap().shrink_to_lo(),
+                            format!("{}, ", snippet)));
+                    }
                 }
+
                 if first_type_or_binding_span.is_none() {
                     first_type_or_binding_span = Some(ty_param.span);
                 }
@@ -5609,27 +5623,93 @@ impl<'a> Parser<'a> {
                 last_comma_span = Some(self.prev_span);
             }
         }
-        if !bad_lifetime_pos.is_empty() {
-            let mut err = self.struct_span_err(
+
+        self.maybe_report_incorrect_generic_argument_order(
+            bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
+        );
+
+        Ok((args, bindings))
+    }
+
+    /// Maybe report an error about incorrect generic argument order - "lifetime parameters
+    /// must be declared before type parameters", "type parameters must be declared before
+    /// associated type bindings" or both.
+    fn maybe_report_incorrect_generic_argument_order(
+        &self,
+        bad_lifetime_pos: Vec<Span>,
+        bad_type_pos: Vec<Span>,
+        lifetime_suggestions: Vec<(Span, String)>,
+        type_suggestions: Vec<(Span, String)>,
+    ) {
+        let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
+            let mut positions = bad_lifetime_pos.clone();
+            positions.extend_from_slice(&bad_type_pos);
+
+            self.struct_span_err(
+                positions,
+                "generic arguments must declare lifetimes, types and associated type bindings in \
+                 that order",
+            )
+        } else if !bad_lifetime_pos.is_empty() {
+            self.struct_span_err(
                 bad_lifetime_pos.clone(),
                 "lifetime parameters must be declared prior to type parameters"
-            );
+            )
+        } else if !bad_type_pos.is_empty() {
+            self.struct_span_err(
+                bad_type_pos.clone(),
+                "type parameters must be declared prior to associated type bindings"
+            )
+        } else {
+            return;
+        };
+
+        if !bad_lifetime_pos.is_empty() {
             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,
-                );
+        }
+
+        if !bad_type_pos.is_empty() {
+            for sp in &bad_type_pos {
+                err.span_label(*sp, "must be declared prior to associated type bindings");
             }
-            err.emit();
         }
-        Ok((args, bindings))
+
+        if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
+            let mut suggestions = lifetime_suggestions;
+            suggestions.extend_from_slice(&type_suggestions);
+
+            let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
+            err.multipart_suggestion_with_applicability(
+                &format!(
+                    "move the parameter{}",
+                    if plural { "s" } else { "" },
+                ),
+                suggestions,
+                Applicability::MachineApplicable,
+            );
+        } else if !lifetime_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 { "" },
+                ),
+                lifetime_suggestions,
+                Applicability::MachineApplicable,
+            );
+        } else if !type_suggestions.is_empty() {
+            err.multipart_suggestion_with_applicability(
+                &format!(
+                    "move the type parameter{} prior to the first associated type binding",
+                    if bad_type_pos.len() > 1 { "s" } else { "" },
+                ),
+                type_suggestions,
+                Applicability::MachineApplicable,
+            );
+        }
+
+        err.emit();
     }
 
     /// Parses an optional `where` clause and places it in `generics`.
diff --git a/src/test/ui/parser/issue-32214.stderr b/src/test/ui/parser/issue-32214.stderr
index a889513eaee..660e517c85a 100644
--- a/src/test/ui/parser/issue-32214.stderr
+++ b/src/test/ui/parser/issue-32214.stderr
@@ -3,6 +3,10 @@ error: type parameters must be declared prior to associated type bindings
    |
 LL | pub fn test<W, I: Trait<Item=(), W> >() {}
    |                                  ^ must be declared prior to associated type bindings
+help: move the type parameter prior to the first associated type binding
+   |
+LL | pub fn test<W, I: Trait<W, Item=()> >() {}
+   |                         ^^       --
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/suggest-move-types.rs b/src/test/ui/suggestions/suggest-move-types.rs
new file mode 100644
index 00000000000..fd10ba4350c
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.rs
@@ -0,0 +1,85 @@
+// ignore-tidy-linelength
+
+#![allow(warnings)]
+
+// This test verifies that the suggestion to move types before associated type bindings
+// is correct.
+
+trait One<T> {
+  type A;
+}
+
+trait OneWithLifetime<'a, T> {
+  type A;
+}
+
+trait Three<T, U, V> {
+  type A;
+  type B;
+  type C;
+}
+
+trait ThreeWithLifetime<'a, 'b, 'c, T, U, V> {
+  type A;
+  type B;
+  type C;
+}
+
+struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+}
+
+
+struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+}
+
+struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
+    m: M,
+    t: T,
+    u: U,
+    v: V,
+}
+
+struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
+//~^ ERROR generic arguments must declare lifetimes, types and associated type bindings in that order
+    m: M,
+    t: &'a T,
+    u: &'b U,
+    v: &'c V,
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/suggest-move-types.stderr b/src/test/ui/suggestions/suggest-move-types.stderr
new file mode 100644
index 00000000000..3643d9a9124
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.stderr
@@ -0,0 +1,107 @@
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:28:26
+   |
+LL | struct A<T, M: One<A=(), T>> { //~ ERROR type parameters must be declared
+   |                          ^ must be declared prior to associated type bindings
+help: move the type parameter prior to the first associated type binding
+   |
+LL | struct A<T, M: One<T, A=()>> { //~ ERROR type parameters must be declared
+   |                    ^^    --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:34:46
+   |
+LL | struct Al<'a, T, M: OneWithLifetime<A=(), T, 'a>> {
+   |                                           ^  ^^ must be declared prior to type parameters
+   |                                           |
+   |                                           must be declared prior to associated type bindings
+help: move the parameters
+   |
+LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A=()>> {
+   |                                     ^^^ ^^    --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:40:46
+   |
+LL | struct B<T, U, V, M: Three<A=(), B=(), C=(), T, U, V>> { //~ ERROR type parameters must be declared
+   |                                              ^  ^  ^ must be declared prior to associated type bindings
+   |                                              |  |
+   |                                              |  must be declared prior to associated type bindings
+   |                                              must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct B<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                            ^^ ^^ ^^                --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:47:80
+   |
+LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<A=(), B=(), C=(), T, U, V, 'a, 'b, 'c>> {
+   |                                                                       ^  ^  ^  ^^  ^^  ^^ must be declared prior to type parameters
+   |                                                                       |  |  |  |   |
+   |                                                                       |  |  |  |   must be declared prior to type parameters
+   |                                                                       |  |  |  must be declared prior to type parameters
+   |                                                                       |  |  must be declared prior to associated type bindings
+   |                                                                       |  must be declared prior to associated type bindings
+   |                                                                       must be declared prior to associated type bindings
+help: move the parameters
+   |
+LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ ^^ ^^ ^^                --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:55:49
+   |
+LL | struct C<T, U, V, M: Three<T, A=(), B=(), C=(), U, V>> { //~ ERROR type parameters must be declared
+   |                                                 ^  ^ must be declared prior to associated type bindings
+   |                                                 |
+   |                                                 must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct C<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                               ^^ ^^                --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:62:56
+   |
+LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), C=(), U, 'b, V, 'c>> {
+   |                                                        ^^                    ^  ^^  ^  ^^ must be declared prior to type parameters
+   |                                                        |                     |  |   |
+   |                                                        |                     |  |   must be declared prior to associated type bindings
+   |                                                        |                     |  must be declared prior to type parameters
+   |                                                        |                     must be declared prior to associated type bindings
+   |                                                        must be declared prior to type parameters
+help: move the parameters
+   |
+LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ -- ^^ ^^                --
+
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:70:43
+   |
+LL | struct D<T, U, V, M: Three<T, A=(), B=(), U, C=(), V>> { //~ ERROR type parameters must be declared
+   |                                           ^        ^ must be declared prior to associated type bindings
+   |                                           |
+   |                                           must be declared prior to associated type bindings
+help: move the type parameters prior to the first associated type binding
+   |
+LL | struct D<T, U, V, M: Three<T, U, V, A=(), B=(), C=()>> { //~ ERROR type parameters must be declared
+   |                               ^^ ^^          --    --
+
+error: generic arguments must declare lifetimes, types and associated type bindings in that order
+  --> $DIR/suggest-move-types.rs:77:56
+   |
+LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<T, 'a, A=(), B=(), U, 'b, C=(), V, 'c>> {
+   |                                                        ^^              ^  ^^        ^  ^^ must be declared prior to type parameters
+   |                                                        |               |  |         |
+   |                                                        |               |  |         must be declared prior to associated type bindings
+   |                                                        |               |  must be declared prior to type parameters
+   |                                                        |               must be declared prior to associated type bindings
+   |                                                        must be declared prior to type parameters
+help: move the parameters
+   |
+LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A=(), B=(), C=()>> {
+   |                                                     ^^^ ^^^ ^^^ -- ^^ ^^          --    --
+
+error: aborting due to 8 previous errors
+