about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2019-01-24 23:24:58 +0100
committerDavid Wood <david@davidtw.co>2019-01-25 11:15:16 +0100
commit463e623ca967c2bd301cc0291fae219130b53daf (patch)
treee2cbc05116853eed511d4484752ab6e89b4d9ce7 /src
parent095b44c83b540bb4dbf74be1cae604f4bae87989 (diff)
downloadrust-463e623ca967c2bd301cc0291fae219130b53daf.tar.gz
rust-463e623ca967c2bd301cc0291fae219130b53daf.zip
Suggestion moving types before associated types.
This commit extends existing suggestions to move lifetimes before types
in generic arguments to also suggest moving types behind associated type
bindings.
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/parse/parser.rs67
-rw-r--r--src/test/ui/parser/issue-32214.stderr4
-rw-r--r--src/test/ui/suggestions/suggest-move-types.rs42
-rw-r--r--src/test/ui/suggestions/suggest-move-types.stderr49
4 files changed, 147 insertions, 15 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index c7e33a16564..232b8bb5966 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -5530,22 +5530,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)));
                     }
@@ -5563,24 +5572,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);
                 }
@@ -5596,6 +5610,7 @@ impl<'a> Parser<'a> {
                 last_comma_span = Some(self.prev_span);
             }
         }
+
         if !bad_lifetime_pos.is_empty() {
             let mut err = self.struct_span_err(
                 bad_lifetime_pos.clone(),
@@ -5604,18 +5619,40 @@ impl<'a> Parser<'a> {
             for sp in &bad_lifetime_pos {
                 err.span_label(*sp, "must be declared prior to type parameters");
             }
-            if !suggestions.is_empty() {
+            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 { "" },
                     ),
-                    suggestions,
+                    lifetime_suggestions,
                     Applicability::MachineApplicable,
                 );
             }
             err.emit();
         }
+
+        if !bad_type_pos.is_empty() {
+            let mut err = self.struct_span_err(
+                bad_type_pos.clone(),
+                "type parameters must be declared prior to associated type bindings"
+            );
+            for sp in &bad_type_pos {
+                err.span_label(*sp, "must be declared prior to associated type bindings");
+            }
+            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();
+        }
+
         Ok((args, bindings))
     }
 
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..8f35e4ecbca
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.rs
@@ -0,0 +1,42 @@
+#![allow(warnings)]
+
+// This test verifies that the suggestion to move types before associated type bindings
+// is correct.
+
+trait One<T> {
+  type A;
+}
+
+trait Three<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 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 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 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,
+}
+
+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..c74f79a00c7
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-move-types.stderr
@@ -0,0 +1,49 @@
+error: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:16: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: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:21: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: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:28: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: type parameters must be declared prior to associated type bindings
+  --> $DIR/suggest-move-types.rs:35: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: aborting due to 4 previous errors
+