Detect missing =>
after match guard during parsing
``` error: expected one of `,`, `:`, or `}`, found `.` --> $DIR/missing-fat-arrow.rs:25:14 | LL | Some(a) if a.value == b { | - while parsing this struct LL | a.value = 1; | -^ expected one of `,`, `:`, or `}` | | | while parsing this struct field | help: try naming a field | LL | a: a.value = 1; | ++ help: you might have meant to start a match arm after the match guard | LL | Some(a) if a.value == b => { | ++ ``` Fix #78585.
This commit is contained in:
parent
a6dfd89fa7
commit
745c1ea438
@ -195,6 +195,10 @@ parse_expected_else_block = expected `{"{"}`, found {$first_tok}
|
||||
.label = expected an `if` or a block after this `else`
|
||||
.suggestion = add an `if` if this is the condition of a chained `else if` statement
|
||||
|
||||
parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$token}`
|
||||
.label = expected one of `,`, `:`, or `{"}"}`
|
||||
.ident_label = while parsing this struct field
|
||||
|
||||
parse_expected_expression_found_let = expected expression, found `let` statement
|
||||
.note = only supported directly in conditions of `if` and `while` expressions
|
||||
.not_supported_or = `||` operators are not supported in let chain expressions
|
||||
|
@ -430,6 +430,17 @@ pub(crate) struct ExpectedElseBlock {
|
||||
pub condition_start: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_expected_struct_field)]
|
||||
pub(crate) struct ExpectedStructField {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
pub token: Token,
|
||||
#[label(parse_ident_label)]
|
||||
pub ident_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_outer_attribute_not_allowed_on_if_else)]
|
||||
pub(crate) struct OuterAttributeNotAllowedOnIfElse {
|
||||
|
@ -2834,7 +2834,7 @@ impl<'a> Parser<'a> {
|
||||
)?;
|
||||
let guard = if this.eat_keyword(kw::If) {
|
||||
let if_span = this.prev_token.span;
|
||||
let mut cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
|
||||
let mut cond = this.parse_match_guard_condition()?;
|
||||
|
||||
CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
|
||||
|
||||
@ -2860,9 +2860,9 @@ impl<'a> Parser<'a> {
|
||||
{
|
||||
err.span_suggestion(
|
||||
this.token.span,
|
||||
"try using a fat arrow here",
|
||||
"use a fat arrow to start a match arm",
|
||||
"=>",
|
||||
Applicability::MaybeIncorrect,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err.emit();
|
||||
this.bump();
|
||||
@ -2979,6 +2979,34 @@ impl<'a> Parser<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
|
||||
self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err(
|
||||
|mut err| {
|
||||
if self.prev_token == token::OpenDelim(Delimiter::Brace) {
|
||||
let sugg_sp = self.prev_token.span.shrink_to_lo();
|
||||
// Consume everything within the braces, let's avoid further parse
|
||||
// errors.
|
||||
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
||||
let msg = "you might have meant to start a match arm after the match guard";
|
||||
if self.eat(&token::CloseDelim(Delimiter::Brace)) {
|
||||
let applicability = if self.token.kind != token::FatArrow {
|
||||
// We have high confidence that we indeed didn't have a struct
|
||||
// literal in the match guard, but rather we had some operation
|
||||
// that ended in a path, immediately followed by a block that was
|
||||
// meant to be the match arm.
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
// self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
|
||||
err.span_suggestion_verbose(sugg_sp, msg, "=> ".to_string(), applicability);
|
||||
}
|
||||
}
|
||||
err
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn is_builtin(&self) -> bool {
|
||||
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
|
||||
}
|
||||
@ -3049,9 +3077,10 @@ impl<'a> Parser<'a> {
|
||||
|| self.look_ahead(2, |t| t == &token::Colon)
|
||||
&& (
|
||||
// `{ ident: token, ` cannot start a block.
|
||||
self.look_ahead(4, |t| t == &token::Comma) ||
|
||||
// `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
|
||||
self.look_ahead(3, |t| !t.can_begin_type())
|
||||
self.look_ahead(4, |t| t == &token::Comma)
|
||||
// `{ ident: ` cannot start a block unless it's a type ascription
|
||||
// `ident: Type`.
|
||||
|| self.look_ahead(3, |t| !t.can_begin_type())
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -3091,6 +3120,7 @@ impl<'a> Parser<'a> {
|
||||
let mut fields = ThinVec::new();
|
||||
let mut base = ast::StructRest::None;
|
||||
let mut recover_async = false;
|
||||
let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
|
||||
|
||||
let mut async_block_err = |e: &mut Diagnostic, span: Span| {
|
||||
recover_async = true;
|
||||
@ -3128,6 +3158,26 @@ impl<'a> Parser<'a> {
|
||||
e.span_label(pth.span, "while parsing this struct");
|
||||
}
|
||||
|
||||
if let Some((ident, _)) = self.token.ident()
|
||||
&& !self.token.is_reserved_ident()
|
||||
&& self.look_ahead(1, |t| {
|
||||
AssocOp::from_token(&t).is_some()
|
||||
|| matches!(t.kind, token::OpenDelim(_))
|
||||
|| t.kind == token::Dot
|
||||
})
|
||||
{
|
||||
// Looks like they tried to write a shorthand, complex expression.
|
||||
e.span_suggestion_verbose(
|
||||
self.token.span.shrink_to_lo(),
|
||||
"try naming a field",
|
||||
&format!("{ident}: ", ),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
if in_if_guard && close_delim == Delimiter::Brace {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
if !recover {
|
||||
return Err(e);
|
||||
}
|
||||
@ -3173,19 +3223,6 @@ impl<'a> Parser<'a> {
|
||||
",",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_shorthand
|
||||
&& (AssocOp::from_token(&self.token).is_some()
|
||||
|| matches!(&self.token.kind, token::OpenDelim(_))
|
||||
|| self.token.kind == token::Dot)
|
||||
{
|
||||
// Looks like they tried to write a shorthand, complex expression.
|
||||
let ident = parsed_field.expect("is_shorthand implies Some").ident;
|
||||
e.span_suggestion(
|
||||
ident.span.shrink_to_lo(),
|
||||
"try naming a field",
|
||||
&format!("{ident}: "),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
if !recover {
|
||||
@ -3288,6 +3325,24 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Check if a colon exists one ahead. This means we're parsing a fieldname.
|
||||
let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
|
||||
// Proactively check whether parsing the field will be correct.
|
||||
let is_wrong = this.token.is_ident()
|
||||
&& !this.token.is_reserved_ident()
|
||||
&& !this.look_ahead(1, |t| {
|
||||
t == &token::Colon
|
||||
|| t == &token::Eq
|
||||
|| t == &token::Comma
|
||||
|| t == &token::CloseDelim(Delimiter::Brace)
|
||||
|| t == &token::CloseDelim(Delimiter::Parenthesis)
|
||||
});
|
||||
if is_wrong {
|
||||
return Err(errors::ExpectedStructField {
|
||||
span: this.look_ahead(1, |t| t.span),
|
||||
ident_span: this.token.span,
|
||||
token: this.look_ahead(1, |t| t.clone()),
|
||||
}
|
||||
.into_diagnostic(&self.sess.span_diagnostic));
|
||||
}
|
||||
let (ident, expr) = if is_shorthand {
|
||||
// Mimic `x: x` for the `x` field shorthand.
|
||||
let ident = this.parse_ident_common(false)?;
|
||||
|
@ -52,6 +52,7 @@ bitflags::bitflags! {
|
||||
const NO_STRUCT_LITERAL = 1 << 1;
|
||||
const CONST_EXPR = 1 << 2;
|
||||
const ALLOW_LET = 1 << 3;
|
||||
const IN_IF_GUARD = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,6 @@ fn main(){
|
||||
//~^ ERROR expected identifier, found keyword `return`
|
||||
//~| NOTE expected identifier, found keyword
|
||||
}
|
||||
//~^ NOTE expected one of `.`, `=>`, `?`, or an operator
|
||||
_ => {}
|
||||
//~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
|
||||
//~| NOTE unexpected token
|
||||
}
|
||||
}
|
||||
|
@ -11,15 +11,10 @@ help: escape `return` to use it as an identifier
|
||||
|
|
||||
LL | r#return
|
||||
| ++
|
||||
|
||||
error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
|
||||
--> $DIR/issue-15980.rs:13:9
|
||||
help: you might have meant to start a match arm after the match guard
|
||||
|
|
||||
LL | }
|
||||
| - expected one of `.`, `=>`, `?`, or an operator
|
||||
LL |
|
||||
LL | _ => {}
|
||||
| ^ unexpected token
|
||||
LL | Err(ref e) if e.kind == io::EndOfFile => {
|
||||
| ++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -8,10 +8,15 @@ error: expected one of `,`, `:`, or `}`, found `.`
|
||||
--> $DIR/issue-52496.rs:8:22
|
||||
|
|
||||
LL | let _ = Foo { bar.into(), bat: -1, . };
|
||||
| --- - ^ expected one of `,`, `:`, or `}`
|
||||
| --- ---^ expected one of `,`, `:`, or `}`
|
||||
| | |
|
||||
| | help: try naming a field: `bar:`
|
||||
| | while parsing this struct field
|
||||
| while parsing this struct
|
||||
|
|
||||
help: try naming a field
|
||||
|
|
||||
LL | let _ = Foo { bar: bar.into(), bat: -1, . };
|
||||
| ++++
|
||||
|
||||
error: expected identifier, found `.`
|
||||
--> $DIR/issue-52496.rs:8:40
|
||||
|
@ -8,9 +8,9 @@ fn main() {
|
||||
let _ = match opt {
|
||||
Some(_) => true,
|
||||
//~^ ERROR: expected one of
|
||||
//~| HELP: try using a fat arrow here
|
||||
//~| HELP: use a fat arrow to start a match arm
|
||||
None => false,
|
||||
//~^ ERROR: expected one of
|
||||
//~| HELP: try using a fat arrow here
|
||||
//~| HELP: use a fat arrow to start a match arm
|
||||
};
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ fn main() {
|
||||
let _ = match opt {
|
||||
Some(_) = true,
|
||||
//~^ ERROR: expected one of
|
||||
//~| HELP: try using a fat arrow here
|
||||
//~| HELP: use a fat arrow to start a match arm
|
||||
None -> false,
|
||||
//~^ ERROR: expected one of
|
||||
//~| HELP: try using a fat arrow here
|
||||
//~| HELP: use a fat arrow to start a match arm
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ LL | Some(_) = true,
|
||||
| ^
|
||||
| |
|
||||
| expected one of `=>`, `if`, or `|`
|
||||
| help: try using a fat arrow here: `=>`
|
||||
| help: use a fat arrow to start a match arm: `=>`
|
||||
|
||||
error: expected one of `=>`, `@`, `if`, or `|`, found `->`
|
||||
--> $DIR/issue-89396.rs:12:14
|
||||
@ -14,7 +14,7 @@ LL | None -> false,
|
||||
| ^^
|
||||
| |
|
||||
| expected one of `=>`, `@`, `if`, or `|`
|
||||
| help: try using a fat arrow here: `=>`
|
||||
| help: use a fat arrow to start a match arm: `=>`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
41
tests/ui/parser/missing-fat-arrow.rs
Normal file
41
tests/ui/parser/missing-fat-arrow.rs
Normal file
@ -0,0 +1,41 @@
|
||||
fn main() {
|
||||
let x = 1;
|
||||
let y = 2;
|
||||
let value = 3;
|
||||
|
||||
match value {
|
||||
Some(x) if x == y {
|
||||
self.next_token()?; //~ ERROR expected identifier, found keyword `self`
|
||||
Ok(true)
|
||||
},
|
||||
_ => {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
let _: i32 = (); //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
value: usize
|
||||
}
|
||||
|
||||
fn foo(a: Option<&mut Foo>, b: usize) {
|
||||
match a {
|
||||
Some(a) if a.value == b {
|
||||
a.value = 1; //~ ERROR expected one of `,`, `:`, or `}`, found `.`
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
let _: i32 = (); //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn bar(a: Option<&mut Foo>, b: usize) {
|
||||
match a {
|
||||
Some(a) if a.value == b {
|
||||
a.value, //~ ERROR expected one of `,`, `:`, or `}`, found `.`
|
||||
} => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let _: i32 = (); //~ ERROR mismatched types
|
||||
}
|
78
tests/ui/parser/missing-fat-arrow.stderr
Normal file
78
tests/ui/parser/missing-fat-arrow.stderr
Normal file
@ -0,0 +1,78 @@
|
||||
error: expected identifier, found keyword `self`
|
||||
--> $DIR/missing-fat-arrow.rs:8:13
|
||||
|
|
||||
LL | Some(x) if x == y {
|
||||
| - while parsing this struct
|
||||
LL | self.next_token()?;
|
||||
| ^^^^ expected identifier, found keyword
|
||||
|
|
||||
help: you might have meant to start a match arm after the match guard
|
||||
|
|
||||
LL | Some(x) if x == y => {
|
||||
| ++
|
||||
|
||||
error: expected one of `,`, `:`, or `}`, found `.`
|
||||
--> $DIR/missing-fat-arrow.rs:25:14
|
||||
|
|
||||
LL | Some(a) if a.value == b {
|
||||
| - while parsing this struct
|
||||
LL | a.value = 1;
|
||||
| -^ expected one of `,`, `:`, or `}`
|
||||
| |
|
||||
| while parsing this struct field
|
||||
|
|
||||
help: try naming a field
|
||||
|
|
||||
LL | a: a.value = 1;
|
||||
| ++
|
||||
help: you might have meant to start a match arm after the match guard
|
||||
|
|
||||
LL | Some(a) if a.value == b => {
|
||||
| ++
|
||||
|
||||
error: expected one of `,`, `:`, or `}`, found `.`
|
||||
--> $DIR/missing-fat-arrow.rs:35:14
|
||||
|
|
||||
LL | Some(a) if a.value == b {
|
||||
| - while parsing this struct
|
||||
LL | a.value,
|
||||
| -^ expected one of `,`, `:`, or `}`
|
||||
| |
|
||||
| while parsing this struct field
|
||||
|
|
||||
help: try naming a field
|
||||
|
|
||||
LL | a: a.value,
|
||||
| ++
|
||||
help: you might have meant to start a match arm after the match guard
|
||||
|
|
||||
LL | Some(a) if a.value == b => {
|
||||
| ++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-fat-arrow.rs:15:18
|
||||
|
|
||||
LL | let _: i32 = ();
|
||||
| --- ^^ expected `i32`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-fat-arrow.rs:29:18
|
||||
|
|
||||
LL | let _: i32 = ();
|
||||
| --- ^^ expected `i32`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing-fat-arrow.rs:40:18
|
||||
|
|
||||
LL | let _: i32 = ();
|
||||
| --- ^^ expected `i32`, found `()`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -2,8 +2,9 @@ error: expected one of `,`, `:`, or `}`, found `a`
|
||||
--> $DIR/removed-syntax-with-2.rs:8:31
|
||||
|
|
||||
LL | let b = S { foo: (), with a };
|
||||
| - ^ expected one of `,`, `:`, or `}`
|
||||
| |
|
||||
| - ---- ^ expected one of `,`, `:`, or `}`
|
||||
| | |
|
||||
| | while parsing this struct field
|
||||
| while parsing this struct
|
||||
|
||||
error[E0063]: missing field `bar` in initializer of `S`
|
||||
|
Loading…
x
Reference in New Issue
Block a user