Skip to content

Commit

Permalink
Unrolled build for rust-lang#116400
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rust-timer committed Oct 7, 2023
2 parents 8fdb0a9 + 8fd345d commit 6b94110
Show file tree
Hide file tree
Showing 28 changed files with 225 additions and 41 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ parse_expected_semi_found_str = expected `;`, found `{$token}`
parse_expected_statement_after_outer_attr = expected statement after outer attribute
parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$token}`
.label = expected one of `,`, `:`, or `{"}"}`
.ident_label = while parsing this struct field
parse_expected_trait_in_trait_impl_found_type = expected a trait, found type
parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
92 changes: 73 additions & 19 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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();
Expand Down Expand Up @@ -2979,6 +2979,33 @@ 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
};
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)
}
Expand Down Expand Up @@ -3049,9 +3076,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())
)
)
}
Expand Down Expand Up @@ -3091,6 +3119,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;
Expand Down Expand Up @@ -3128,6 +3157,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::MaybeIncorrect,
);
}
if in_if_guard && close_delim == Delimiter::Brace {
return Err(e);
}

if !recover {
return Err(e);
}
Expand Down Expand Up @@ -3173,19 +3222,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 {
Expand Down Expand Up @@ -3288,6 +3324,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 incorrect.
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)?;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 0 additions & 3 deletions tests/ui/parser/issues/issue-15980.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
13 changes: 4 additions & 9 deletions tests/ui/parser/issues/issue-15980.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

9 changes: 7 additions & 2 deletions tests/ui/parser/issues/issue-52496.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/parser/issues/issue-89396.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
4 changes: 2 additions & 2 deletions tests/ui/parser/issues/issue-89396.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}
4 changes: 2 additions & 2 deletions tests/ui/parser/issues/issue-89396.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

38 changes: 38 additions & 0 deletions tests/ui/parser/missing-fat-arrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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`
},
_ => {}
}
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 changes: 78 additions & 0 deletions tests/ui/parser/missing-fat-arrow.stderr
Original file line number Diff line number Diff line change
@@ -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:22: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:32: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:12:18
|
LL | let _: i32 = ();
| --- ^^ expected `i32`, found `()`
| |
| expected due to this

error[E0308]: mismatched types
--> $DIR/missing-fat-arrow.rs:26:18
|
LL | let _: i32 = ();
| --- ^^ expected `i32`, found `()`
| |
| expected due to this

error[E0308]: mismatched types
--> $DIR/missing-fat-arrow.rs:37: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`.
Loading

0 comments on commit 6b94110

Please sign in to comment.