Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0d68e41

Browse files
Rollup merge of rust-lang#116400 - estebank:issue-78585, r=WaffleLapkin
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 rust-lang#78585.
2 parents 6d1c3a4 + 8fd345d commit 0d68e41

28 files changed

+225
-41
lines changed

compiler/rustc_parse/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ parse_expected_semi_found_str = expected `;`, found `{$token}`
225225
226226
parse_expected_statement_after_outer_attr = expected statement after outer attribute
227227
228+
parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$token}`
229+
.label = expected one of `,`, `:`, or `{"}"}`
230+
.ident_label = while parsing this struct field
231+
228232
parse_expected_trait_in_trait_impl_found_type = expected a trait, found type
229233
230234
parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements

compiler/rustc_parse/src/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,17 @@ pub(crate) struct ExpectedElseBlock {
430430
pub condition_start: Span,
431431
}
432432

433+
#[derive(Diagnostic)]
434+
#[diag(parse_expected_struct_field)]
435+
pub(crate) struct ExpectedStructField {
436+
#[primary_span]
437+
#[label]
438+
pub span: Span,
439+
pub token: Token,
440+
#[label(parse_ident_label)]
441+
pub ident_span: Span,
442+
}
443+
433444
#[derive(Diagnostic)]
434445
#[diag(parse_outer_attribute_not_allowed_on_if_else)]
435446
pub(crate) struct OuterAttributeNotAllowedOnIfElse {

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,7 +2834,7 @@ impl<'a> Parser<'a> {
28342834
)?;
28352835
let guard = if this.eat_keyword(kw::If) {
28362836
let if_span = this.prev_token.span;
2837-
let mut cond = this.parse_expr_res(Restrictions::ALLOW_LET, None)?;
2837+
let mut cond = this.parse_match_guard_condition()?;
28382838

28392839
CondChecker { parser: this, forbid_let_reason: None }.visit_expr(&mut cond);
28402840

@@ -2860,9 +2860,9 @@ impl<'a> Parser<'a> {
28602860
{
28612861
err.span_suggestion(
28622862
this.token.span,
2863-
"try using a fat arrow here",
2863+
"use a fat arrow to start a match arm",
28642864
"=>",
2865-
Applicability::MaybeIncorrect,
2865+
Applicability::MachineApplicable,
28662866
);
28672867
err.emit();
28682868
this.bump();
@@ -2979,6 +2979,33 @@ impl<'a> Parser<'a> {
29792979
})
29802980
}
29812981

2982+
fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
2983+
self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, None).map_err(
2984+
|mut err| {
2985+
if self.prev_token == token::OpenDelim(Delimiter::Brace) {
2986+
let sugg_sp = self.prev_token.span.shrink_to_lo();
2987+
// Consume everything within the braces, let's avoid further parse
2988+
// errors.
2989+
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
2990+
let msg = "you might have meant to start a match arm after the match guard";
2991+
if self.eat(&token::CloseDelim(Delimiter::Brace)) {
2992+
let applicability = if self.token.kind != token::FatArrow {
2993+
// We have high confidence that we indeed didn't have a struct
2994+
// literal in the match guard, but rather we had some operation
2995+
// that ended in a path, immediately followed by a block that was
2996+
// meant to be the match arm.
2997+
Applicability::MachineApplicable
2998+
} else {
2999+
Applicability::MaybeIncorrect
3000+
};
3001+
err.span_suggestion_verbose(sugg_sp, msg, "=> ".to_string(), applicability);
3002+
}
3003+
}
3004+
err
3005+
},
3006+
)
3007+
}
3008+
29823009
pub(crate) fn is_builtin(&self) -> bool {
29833010
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
29843011
}
@@ -3049,9 +3076,10 @@ impl<'a> Parser<'a> {
30493076
|| self.look_ahead(2, |t| t == &token::Colon)
30503077
&& (
30513078
// `{ ident: token, ` cannot start a block.
3052-
self.look_ahead(4, |t| t == &token::Comma) ||
3053-
// `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`.
3054-
self.look_ahead(3, |t| !t.can_begin_type())
3079+
self.look_ahead(4, |t| t == &token::Comma)
3080+
// `{ ident: ` cannot start a block unless it's a type ascription
3081+
// `ident: Type`.
3082+
|| self.look_ahead(3, |t| !t.can_begin_type())
30553083
)
30563084
)
30573085
}
@@ -3091,6 +3119,7 @@ impl<'a> Parser<'a> {
30913119
let mut fields = ThinVec::new();
30923120
let mut base = ast::StructRest::None;
30933121
let mut recover_async = false;
3122+
let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
30943123

30953124
let mut async_block_err = |e: &mut Diagnostic, span: Span| {
30963125
recover_async = true;
@@ -3128,6 +3157,26 @@ impl<'a> Parser<'a> {
31283157
e.span_label(pth.span, "while parsing this struct");
31293158
}
31303159

3160+
if let Some((ident, _)) = self.token.ident()
3161+
&& !self.token.is_reserved_ident()
3162+
&& self.look_ahead(1, |t| {
3163+
AssocOp::from_token(&t).is_some()
3164+
|| matches!(t.kind, token::OpenDelim(_))
3165+
|| t.kind == token::Dot
3166+
})
3167+
{
3168+
// Looks like they tried to write a shorthand, complex expression.
3169+
e.span_suggestion_verbose(
3170+
self.token.span.shrink_to_lo(),
3171+
"try naming a field",
3172+
&format!("{ident}: ", ),
3173+
Applicability::MaybeIncorrect,
3174+
);
3175+
}
3176+
if in_if_guard && close_delim == Delimiter::Brace {
3177+
return Err(e);
3178+
}
3179+
31313180
if !recover {
31323181
return Err(e);
31333182
}
@@ -3173,19 +3222,6 @@ impl<'a> Parser<'a> {
31733222
",",
31743223
Applicability::MachineApplicable,
31753224
);
3176-
} else if is_shorthand
3177-
&& (AssocOp::from_token(&self.token).is_some()
3178-
|| matches!(&self.token.kind, token::OpenDelim(_))
3179-
|| self.token.kind == token::Dot)
3180-
{
3181-
// Looks like they tried to write a shorthand, complex expression.
3182-
let ident = parsed_field.expect("is_shorthand implies Some").ident;
3183-
e.span_suggestion(
3184-
ident.span.shrink_to_lo(),
3185-
"try naming a field",
3186-
&format!("{ident}: "),
3187-
Applicability::HasPlaceholders,
3188-
);
31893225
}
31903226
}
31913227
if !recover {
@@ -3288,6 +3324,24 @@ impl<'a> Parser<'a> {
32883324

32893325
// Check if a colon exists one ahead. This means we're parsing a fieldname.
32903326
let is_shorthand = !this.look_ahead(1, |t| t == &token::Colon || t == &token::Eq);
3327+
// Proactively check whether parsing the field will be incorrect.
3328+
let is_wrong = this.token.is_ident()
3329+
&& !this.token.is_reserved_ident()
3330+
&& !this.look_ahead(1, |t| {
3331+
t == &token::Colon
3332+
|| t == &token::Eq
3333+
|| t == &token::Comma
3334+
|| t == &token::CloseDelim(Delimiter::Brace)
3335+
|| t == &token::CloseDelim(Delimiter::Parenthesis)
3336+
});
3337+
if is_wrong {
3338+
return Err(errors::ExpectedStructField {
3339+
span: this.look_ahead(1, |t| t.span),
3340+
ident_span: this.token.span,
3341+
token: this.look_ahead(1, |t| t.clone()),
3342+
}
3343+
.into_diagnostic(&self.sess.span_diagnostic));
3344+
}
32913345
let (ident, expr) = if is_shorthand {
32923346
// Mimic `x: x` for the `x` field shorthand.
32933347
let ident = this.parse_ident_common(false)?;

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ bitflags::bitflags! {
5252
const NO_STRUCT_LITERAL = 1 << 1;
5353
const CONST_EXPR = 1 << 2;
5454
const ALLOW_LET = 1 << 3;
55+
const IN_IF_GUARD = 1 << 4;
5556
}
5657
}
5758

0 commit comments

Comments
 (0)