Fix suggestion for matching struct with ..
on both ends
This commit is contained in:
parent
f85ab544df
commit
2a7c6a99ef
5 changed files with 111 additions and 13 deletions
|
@ -938,7 +938,8 @@ impl<'a> Parser<'a> {
|
||||||
let mut etc = false;
|
let mut etc = false;
|
||||||
let mut ate_comma = true;
|
let mut ate_comma = true;
|
||||||
let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
|
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) {
|
while self.token != token::CloseDelim(Delimiter::Brace) {
|
||||||
let attrs = match self.parse_outer_attributes() {
|
let attrs = match self.parse_outer_attributes() {
|
||||||
|
@ -969,12 +970,27 @@ impl<'a> Parser<'a> {
|
||||||
{
|
{
|
||||||
etc = true;
|
etc = true;
|
||||||
let mut etc_sp = self.token.span;
|
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.recover_bad_dot_dot();
|
||||||
self.bump(); // `..` || `...` || `_`
|
self.bump(); // `..` || `...` || `_`
|
||||||
|
|
||||||
if self.token == token::CloseDelim(Delimiter::Brace) {
|
if self.token == token::CloseDelim(Delimiter::Brace) {
|
||||||
etc_span = Some(etc_sp);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let token_str = super::token_descr(&self.token);
|
let token_str = super::token_descr(&self.token);
|
||||||
|
@ -996,7 +1012,6 @@ impl<'a> Parser<'a> {
|
||||||
ate_comma = true;
|
ate_comma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
etc_span = Some(etc_sp.until(self.token.span));
|
|
||||||
if self.token == token::CloseDelim(Delimiter::Brace) {
|
if self.token == token::CloseDelim(Delimiter::Brace) {
|
||||||
// If the struct looks otherwise well formed, recover and continue.
|
// If the struct looks otherwise well formed, recover and continue.
|
||||||
if let Some(sp) = comma_sp {
|
if let Some(sp) = comma_sp {
|
||||||
|
@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
ate_comma = this.eat(&token::Comma);
|
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
|
// We just ate a comma, so there's no need to use
|
||||||
// `TrailingToken::Comma`
|
// `TrailingToken::Comma`
|
||||||
Ok((field, TrailingToken::None))
|
Ok((field, TrailingToken::None))
|
||||||
|
@ -1049,16 +1067,31 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut err) = delayed_err {
|
if let Some(mut err) = delayed_err {
|
||||||
if let Some(etc_span) = etc_span {
|
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(
|
err.multipart_suggestion(
|
||||||
"move the `..` to the end of the field list",
|
"move the `..` to the end of the field list",
|
||||||
vec![
|
vec![
|
||||||
(etc_span, String::new()),
|
(first_etc_span, String::new()),
|
||||||
(self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })),
|
(
|
||||||
|
self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()),
|
||||||
|
format!("{} .. }}", if ate_comma { "" } else { "," }),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
Ok((fields, etc))
|
Ok((fields, etc))
|
||||||
|
|
14
tests/ui/parser/issue-112188.fixed
Normal file
14
tests/ui/parser/issue-112188.fixed
Normal file
|
@ -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 `,`
|
||||||
|
}
|
14
tests/ui/parser/issue-112188.rs
Normal file
14
tests/ui/parser/issue-112188.rs
Normal file
|
@ -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 `,`
|
||||||
|
}
|
37
tests/ui/parser/issue-112188.stderr
Normal file
37
tests/ui/parser/issue-112188.stderr
Normal file
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue