about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs57
-rw-r--r--tests/ui/parser/issue-112188.fixed14
-rw-r--r--tests/ui/parser/issue-112188.rs14
-rw-r--r--tests/ui/parser/issue-112188.stderr37
-rw-r--r--tests/ui/parser/issue-49257.stderr2
5 files changed, 111 insertions, 13 deletions
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index c317d96368e..fdf36517847 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -938,7 +938,8 @@ impl<'a> Parser<'a> {
         let mut etc = false;
         let mut ate_comma = true;
         let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
-        let mut etc_span = None;
+        let mut first_etc_and_maybe_comma_span = None;
+        let mut last_non_comma_dotdot_span = None;
 
         while self.token != token::CloseDelim(Delimiter::Brace) {
             let attrs = match self.parse_outer_attributes() {
@@ -969,12 +970,27 @@ impl<'a> Parser<'a> {
             {
                 etc = true;
                 let mut etc_sp = self.token.span;
+                if first_etc_and_maybe_comma_span.is_none() {
+                    if let Some(comma_tok) = self
+                        .look_ahead(1, |t| if *t == token::Comma { Some(t.clone()) } else { None })
+                    {
+                        let nw_span = self
+                            .sess
+                            .source_map()
+                            .span_extend_to_line(comma_tok.span)
+                            .trim_start(comma_tok.span.shrink_to_lo())
+                            .map(|s| self.sess.source_map().span_until_non_whitespace(s));
+                        first_etc_and_maybe_comma_span = nw_span.map(|s| etc_sp.to(s));
+                    } else {
+                        first_etc_and_maybe_comma_span =
+                            Some(self.sess.source_map().span_until_non_whitespace(etc_sp));
+                    }
+                }
 
                 self.recover_bad_dot_dot();
                 self.bump(); // `..` || `...` || `_`
 
                 if self.token == token::CloseDelim(Delimiter::Brace) {
-                    etc_span = Some(etc_sp);
                     break;
                 }
                 let token_str = super::token_descr(&self.token);
@@ -996,7 +1012,6 @@ impl<'a> Parser<'a> {
                     ate_comma = true;
                 }
 
-                etc_span = Some(etc_sp.until(self.token.span));
                 if self.token == token::CloseDelim(Delimiter::Brace) {
                     // If the struct looks otherwise well formed, recover and continue.
                     if let Some(sp) = comma_sp {
@@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> {
                         }
                     }?;
                     ate_comma = this.eat(&token::Comma);
+
+                    last_non_comma_dotdot_span = Some(this.prev_token.span);
+
                     // We just ate a comma, so there's no need to use
                     // `TrailingToken::Comma`
                     Ok((field, TrailingToken::None))
@@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> {
         }
 
         if let Some(mut err) = delayed_err {
-            if let Some(etc_span) = etc_span {
-                err.multipart_suggestion(
-                    "move the `..` to the end of the field list",
-                    vec![
-                        (etc_span, String::new()),
-                        (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })),
-                    ],
-                    Applicability::MachineApplicable,
-                );
+            if let Some(first_etc_span) = first_etc_and_maybe_comma_span {
+                if self.prev_token == token::DotDot {
+                    // We have `.., x, ..`.
+                    err.multipart_suggestion(
+                        "remove the starting `..`",
+                        vec![(first_etc_span, String::new())],
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    if let Some(last_non_comma_dotdot_span) = last_non_comma_dotdot_span {
+                        // We have `.., x`.
+                        err.multipart_suggestion(
+                            "move the `..` to the end of the field list",
+                            vec![
+                                (first_etc_span, String::new()),
+                                (
+                                    self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()),
+                                    format!("{} .. }}", if ate_comma { "" } else { "," }),
+                                ),
+                            ],
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
             }
             err.emit();
         }
diff --git a/tests/ui/parser/issue-112188.fixed b/tests/ui/parser/issue-112188.fixed
new file mode 100644
index 00000000000..5e73d8e38de
--- /dev/null
+++ b/tests/ui/parser/issue-112188.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo { x: i32 }
+
+fn main() {
+    let f = Foo { x: 0 };
+    let Foo { .. } = f;
+    let Foo { .. } = f; //~ ERROR expected `}`, found `,`
+    let Foo { x, .. } = f;
+    let Foo { x, .. } = f; //~ ERROR expected `}`, found `,`
+    let Foo { x, .. } = f; //~ ERROR expected `}`, found `,`
+}
diff --git a/tests/ui/parser/issue-112188.rs b/tests/ui/parser/issue-112188.rs
new file mode 100644
index 00000000000..27ca192e522
--- /dev/null
+++ b/tests/ui/parser/issue-112188.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo { x: i32 }
+
+fn main() {
+    let f = Foo { x: 0 };
+    let Foo { .. } = f;
+    let Foo { .., } = f; //~ ERROR expected `}`, found `,`
+    let Foo { x, .. } = f;
+    let Foo { .., x } = f; //~ ERROR expected `}`, found `,`
+    let Foo { .., x, .. } = f; //~ ERROR expected `}`, found `,`
+}
diff --git a/tests/ui/parser/issue-112188.stderr b/tests/ui/parser/issue-112188.stderr
new file mode 100644
index 00000000000..6d2d8e6a3b0
--- /dev/null
+++ b/tests/ui/parser/issue-112188.stderr
@@ -0,0 +1,37 @@
+error: expected `}`, found `,`
+  --> $DIR/issue-112188.rs:10:17
+   |
+LL |     let Foo { .., } = f;
+   |               --^
+   |               | |
+   |               | expected `}`
+   |               | help: remove this comma
+   |               `..` must be at the end and cannot have a trailing comma
+
+error: expected `}`, found `,`
+  --> $DIR/issue-112188.rs:12:17
+   |
+LL |     let Foo { .., x } = f;
+   |               --^
+   |               | |
+   |               | expected `}`
+   |               `..` must be at the end and cannot have a trailing comma
+   |
+help: move the `..` to the end of the field list
+   |
+LL -     let Foo { .., x } = f;
+LL +     let Foo { x, .. } = f;
+   |
+
+error: expected `}`, found `,`
+  --> $DIR/issue-112188.rs:13:17
+   |
+LL |     let Foo { .., x, .. } = f;
+   |               --^-
+   |               | |
+   |               | expected `}`
+   |               `..` must be at the end and cannot have a trailing comma
+   |               help: remove the starting `..`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/parser/issue-49257.stderr b/tests/ui/parser/issue-49257.stderr
index 846467f7f22..97e16f88b8d 100644
--- a/tests/ui/parser/issue-49257.stderr
+++ b/tests/ui/parser/issue-49257.stderr
@@ -25,7 +25,7 @@ LL |     let Point { .., y } = p;
 help: move the `..` to the end of the field list
    |
 LL -     let Point { .., y } = p;
-LL +     let Point { y , .. } = p;
+LL +     let Point { y, .. } = p;
    |
 
 error: expected `}`, found `,`