From 2a7c6a99ef5c43311a430a07bd4eeab96a7c5d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Fri, 2 Jun 2023 14:40:44 +0800 Subject: [PATCH] Fix suggestion for matching struct with `..` on both ends --- compiler/rustc_parse/src/parser/pat.rs | 57 ++++++++++++++++++++------ tests/ui/parser/issue-112188.fixed | 14 +++++++ tests/ui/parser/issue-112188.rs | 14 +++++++ tests/ui/parser/issue-112188.stderr | 37 +++++++++++++++++ tests/ui/parser/issue-49257.stderr | 2 +- 5 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 tests/ui/parser/issue-112188.fixed create mode 100644 tests/ui/parser/issue-112188.rs create mode 100644 tests/ui/parser/issue-112188.stderr diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c317d96368ee6..fdf365178474d 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> = 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 0000000000000..5e73d8e38de81 --- /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 0000000000000..27ca192e52263 --- /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 0000000000000..6d2d8e6a3b055 --- /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 846467f7f2268..97e16f88b8d32 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 `,`