Recover from Foo(a: 1, b: 2)
Detect likely `struct` literal using parentheses as delimiters and emit targeted suggestion instead of type ascription parse error. Fix #61326.
This commit is contained in:
parent
385f8e2078
commit
b82ec362ca
@ -882,6 +882,12 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn look_ahead_type_ascription_as_field(&mut self) -> bool {
|
||||||
|
self.look_ahead(1, |t| t.is_ident())
|
||||||
|
&& self.look_ahead(2, |t| t == &token::Colon)
|
||||||
|
&& self.look_ahead(3, |t| t.can_begin_expr())
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
|
fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
|
||||||
match self.token.uninterpolate().kind {
|
match self.token.uninterpolate().kind {
|
||||||
token::Ident(..) => self.parse_dot_suffix(base, lo),
|
token::Ident(..) => self.parse_dot_suffix(base, lo),
|
||||||
@ -1031,9 +1037,56 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
/// Parse a function call expression, `expr(...)`.
|
/// Parse a function call expression, `expr(...)`.
|
||||||
fn parse_fn_call_expr(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
|
fn parse_fn_call_expr(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
|
||||||
let seq = self.parse_paren_expr_seq().map(|args| {
|
let snapshot = if self.token.kind == token::OpenDelim(token::Paren)
|
||||||
|
&& self.look_ahead_type_ascription_as_field()
|
||||||
|
{
|
||||||
|
Some((self.clone(), fun.kind.clone()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let open_paren = self.token.span;
|
||||||
|
|
||||||
|
let mut seq = self.parse_paren_expr_seq().map(|args| {
|
||||||
self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new())
|
self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new())
|
||||||
});
|
});
|
||||||
|
match (seq.as_mut(), snapshot) {
|
||||||
|
(Err(ref mut err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
|
||||||
|
let name = pprust::path_to_string(&path);
|
||||||
|
snapshot.bump(); // `(`
|
||||||
|
match snapshot.parse_struct_fields(path.clone(), false, token::Paren) {
|
||||||
|
Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(token::Paren)) => {
|
||||||
|
// We have are certain we have `Enum::Foo(a: 3, b: 4)`, suggest
|
||||||
|
// `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.
|
||||||
|
*self = snapshot;
|
||||||
|
let close_paren = self.prev_token.span;
|
||||||
|
let span = lo.to(self.prev_token.span);
|
||||||
|
err.cancel();
|
||||||
|
self.struct_span_err(
|
||||||
|
span,
|
||||||
|
"invalid `struct` delimiters or `fn` call arguments",
|
||||||
|
)
|
||||||
|
.multipart_suggestion(
|
||||||
|
&format!("if `{}` is a struct, use braces as delimiters", name),
|
||||||
|
vec![(open_paren, " { ".to_string()), (close_paren, " }".to_string())],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
)
|
||||||
|
.multipart_suggestion(
|
||||||
|
&format!("if `{}` is a function, use the arguments directly", name),
|
||||||
|
fields
|
||||||
|
.into_iter()
|
||||||
|
.map(|field| (field.span.until(field.expr.span), String::new()))
|
||||||
|
.collect(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
return self.mk_expr_err(span);
|
||||||
|
}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mut err) => err.emit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
self.recover_seq_parse_error(token::Paren, lo, seq)
|
self.recover_seq_parse_error(token::Paren, lo, seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2332,14 +2385,12 @@ impl<'a> Parser<'a> {
|
|||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Precondition: already parsed the '{'.
|
pub(super) fn parse_struct_fields(
|
||||||
pub(super) fn parse_struct_expr(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
qself: Option<ast::QSelf>,
|
|
||||||
pth: ast::Path,
|
pth: ast::Path,
|
||||||
attrs: AttrVec,
|
|
||||||
recover: bool,
|
recover: bool,
|
||||||
) -> PResult<'a, P<Expr>> {
|
close_delim: token::DelimToken,
|
||||||
|
) -> PResult<'a, (Vec<ExprField>, ast::StructRest, bool)> {
|
||||||
let mut fields = Vec::new();
|
let mut fields = Vec::new();
|
||||||
let mut base = ast::StructRest::None;
|
let mut base = ast::StructRest::None;
|
||||||
let mut recover_async = false;
|
let mut recover_async = false;
|
||||||
@ -2351,11 +2402,11 @@ impl<'a> Parser<'a> {
|
|||||||
e.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
|
e.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
|
||||||
};
|
};
|
||||||
|
|
||||||
while self.token != token::CloseDelim(token::Brace) {
|
while self.token != token::CloseDelim(close_delim) {
|
||||||
if self.eat(&token::DotDot) {
|
if self.eat(&token::DotDot) {
|
||||||
let exp_span = self.prev_token.span;
|
let exp_span = self.prev_token.span;
|
||||||
// We permit `.. }` on the left-hand side of a destructuring assignment.
|
// We permit `.. }` on the left-hand side of a destructuring assignment.
|
||||||
if self.check(&token::CloseDelim(token::Brace)) {
|
if self.check(&token::CloseDelim(close_delim)) {
|
||||||
self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
|
self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
|
||||||
base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi());
|
base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi());
|
||||||
break;
|
break;
|
||||||
@ -2396,7 +2447,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) {
|
match self.expect_one_of(&[token::Comma], &[token::CloseDelim(close_delim)]) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if let Some(f) = parsed_field.or(recovery_field) {
|
if let Some(f) = parsed_field.or(recovery_field) {
|
||||||
// Only include the field if there's no parse error for the field name.
|
// Only include the field if there's no parse error for the field name.
|
||||||
@ -2427,8 +2478,21 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok((fields, base, recover_async))
|
||||||
|
}
|
||||||
|
|
||||||
let span = pth.span.to(self.token.span);
|
/// Precondition: already parsed the '{'.
|
||||||
|
pub(super) fn parse_struct_expr(
|
||||||
|
&mut self,
|
||||||
|
qself: Option<ast::QSelf>,
|
||||||
|
pth: ast::Path,
|
||||||
|
attrs: AttrVec,
|
||||||
|
recover: bool,
|
||||||
|
) -> PResult<'a, P<Expr>> {
|
||||||
|
let lo = pth.span;
|
||||||
|
let (fields, base, recover_async) =
|
||||||
|
self.parse_struct_fields(pth.clone(), recover, token::Brace)?;
|
||||||
|
let span = lo.to(self.token.span);
|
||||||
self.expect(&token::CloseDelim(token::Brace))?;
|
self.expect(&token::CloseDelim(token::Brace))?;
|
||||||
let expr = if recover_async {
|
let expr = if recover_async {
|
||||||
ExprKind::Err
|
ExprKind::Err
|
||||||
|
@ -6,5 +6,5 @@ enum Test {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Test::Drill(field: 42);
|
Test::Drill(field: 42);
|
||||||
//~^ ERROR expected type, found
|
//~^ ERROR invalid `struct` delimiters or `fn` call arguments
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
error: expected type, found `42`
|
error: invalid `struct` delimiters or `fn` call arguments
|
||||||
--> $DIR/issue-34255-1.rs:8:24
|
--> $DIR/issue-34255-1.rs:8:5
|
||||||
|
|
|
|
||||||
LL | Test::Drill(field: 42);
|
LL | Test::Drill(field: 42);
|
||||||
| - ^^ expected type
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
|
||||||
| tried to parse a type due to this type ascription
|
|
||||||
|
|
|
|
||||||
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
|
help: if `Test::Drill` is a struct, use braces as delimiters
|
||||||
= note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
|
|
|
||||||
|
LL | Test::Drill { field: 42 };
|
||||||
|
| ~ ~
|
||||||
|
help: if `Test::Drill` is a function, use the arguments directly
|
||||||
|
|
|
||||||
|
LL - Test::Drill(field: 42);
|
||||||
|
LL + Test::Drill(42);
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
($rest: tt) => {
|
($rest: tt) => {
|
||||||
bar(baz: $rest)
|
bar(baz: $rest) //~ ERROR invalid `struct` delimiters or `fn` call arguments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
foo!(true); //~ ERROR expected type, found keyword
|
foo!(true);
|
||||||
//~^ ERROR expected identifier, found keyword
|
//~^ ERROR expected identifier, found keyword
|
||||||
}
|
}
|
||||||
|
@ -9,17 +9,25 @@ help: you can escape reserved keywords to use them as identifiers
|
|||||||
LL | foo!(r#true);
|
LL | foo!(r#true);
|
||||||
| ~~~~~~
|
| ~~~~~~
|
||||||
|
|
||||||
error: expected type, found keyword `true`
|
error: invalid `struct` delimiters or `fn` call arguments
|
||||||
--> $DIR/issue-44406.rs:8:10
|
--> $DIR/issue-44406.rs:3:9
|
||||||
|
|
|
|
||||||
LL | bar(baz: $rest)
|
LL | bar(baz: $rest)
|
||||||
| - help: try using a semicolon: `;`
|
| ^^^^^^^^^^^^^^^
|
||||||
...
|
...
|
||||||
LL | foo!(true);
|
LL | foo!(true);
|
||||||
| ^^^^ expected type
|
| ----------- in this macro invocation
|
||||||
|
|
|
|
||||||
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
|
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
= note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
|
help: if `bar` is a struct, use braces as delimiters
|
||||||
|
|
|
||||||
|
LL | bar { }
|
||||||
|
| ~
|
||||||
|
help: if `bar` is a function, use the arguments directly
|
||||||
|
|
|
||||||
|
LL - bar(baz: $rest)
|
||||||
|
LL + bar(true);
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ enum Enum {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = Enum::Foo(a: 3, b: 4);
|
let x = Enum::Foo(a: 3, b: 4);
|
||||||
//~^ ERROR expected type, found `3`
|
//~^ ERROR invalid `struct` delimiters or `fn` call arguments
|
||||||
match x {
|
match x {
|
||||||
Enum::Foo(a, b) => {}
|
Enum::Foo(a, b) => {}
|
||||||
//~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo`
|
//~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo`
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
error: expected type, found `3`
|
error: invalid `struct` delimiters or `fn` call arguments
|
||||||
--> $DIR/recover-from-bad-variant.rs:7:26
|
--> $DIR/recover-from-bad-variant.rs:7:13
|
||||||
|
|
|
|
||||||
LL | let x = Enum::Foo(a: 3, b: 4);
|
LL | let x = Enum::Foo(a: 3, b: 4);
|
||||||
| - ^ expected type
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
| |
|
|
||||||
| tried to parse a type due to this type ascription
|
|
||||||
|
|
|
|
||||||
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
|
help: if `Enum::Foo` is a struct, use braces as delimiters
|
||||||
= note: see issue #23416 <https://github.com/rust-lang/rust/issues/23416> for more information
|
|
|
||||||
|
LL | let x = Enum::Foo { a: 3, b: 4 };
|
||||||
|
| ~ ~
|
||||||
|
help: if `Enum::Foo` is a function, use the arguments directly
|
||||||
|
|
|
||||||
|
LL - let x = Enum::Foo(a: 3, b: 4);
|
||||||
|
LL + let x = Enum::Foo(3, 4);
|
||||||
|
|
|
||||||
|
|
||||||
error[E0532]: expected tuple struct or tuple variant, found struct variant `Enum::Foo`
|
error[E0532]: expected tuple struct or tuple variant, found struct variant `Enum::Foo`
|
||||||
--> $DIR/recover-from-bad-variant.rs:10:9
|
--> $DIR/recover-from-bad-variant.rs:10:9
|
||||||
|
Loading…
x
Reference in New Issue
Block a user