Skip to content

Commit 0ff331b

Browse files
committed
Change how for (x in foo) {} is handled
Use the same approach used for match arm patterns.
1 parent c473189 commit 0ff331b

10 files changed

+138
-81
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ use crate::errors::{
1111
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
1212
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
1313
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
14-
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
15-
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
16-
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
17-
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
18-
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
19-
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
14+
IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg,
15+
SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg,
16+
StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt,
17+
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam,
18+
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
19+
UseEqInstead, WrapType,
2020
};
2121

2222
use crate::fluent_generated as fluent;
@@ -1994,37 +1994,6 @@ impl<'a> Parser<'a> {
19941994
}
19951995
}
19961996

1997-
/// Recovers a situation like `for ( $pat in $expr )`
1998-
/// and suggest writing `for $pat in $expr` instead.
1999-
///
2000-
/// This should be called before parsing the `$block`.
2001-
pub(super) fn recover_parens_around_for_head(
2002-
&mut self,
2003-
pat: P<Pat>,
2004-
begin_paren: Option<(Span, Span)>,
2005-
) -> P<Pat> {
2006-
match (&self.token.kind, begin_paren) {
2007-
(token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => {
2008-
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
2009-
self.bump();
2010-
self.sess.emit_err(ParenthesesInForHead {
2011-
span: vec![begin_par_sp, self.prev_token.span],
2012-
// With e.g. `for (x) in y)` this would replace `(x) in y)`
2013-
// with `x) in y)` which is syntactically invalid.
2014-
// However, this is prevented before we get here.
2015-
sugg: ParenthesesInForHeadSugg { left, right },
2016-
});
2017-
2018-
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
2019-
pat.and_then(|pat| match pat.kind {
2020-
PatKind::Paren(pat) => pat,
2021-
_ => P(pat),
2022-
})
2023-
}
2024-
_ => pat,
2025-
}
2026-
}
2027-
20281997
pub(super) fn recover_seq_parse_error(
20291998
&mut self,
20301999
delim: Delimiter,

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,33 +2609,66 @@ impl<'a> Parser<'a> {
26092609
}
26102610
}
26112611

2612-
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2613-
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
2614-
// Record whether we are about to parse `for (`.
2615-
// This is used below for recovery in case of `for ( $stuff ) $block`
2616-
// in which case we will suggest `for $stuff $block`.
2617-
let begin_paren = match self.token.kind {
2618-
token::OpenDelim(Delimiter::Parenthesis) => Some((
2619-
self.token.span,
2620-
self.prev_token.span.between(self.look_ahead(1, |t| t.span)),
2621-
)),
2622-
_ => None,
2612+
fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
2613+
let pat = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
2614+
// Record whether we are about to parse `for (`.
2615+
// This is used below for recovery in case of `for ( $stuff ) $block`
2616+
// in which case we will suggest `for $stuff $block`.
2617+
let start_span = self.token.span;
2618+
let left = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
2619+
match self.parse_pat_allow_top_alt(
2620+
None,
2621+
RecoverComma::Yes,
2622+
RecoverColon::Yes,
2623+
CommaRecoveryMode::LikelyTuple,
2624+
) {
2625+
Ok(pat) => pat,
2626+
Err(err) if self.eat_keyword(kw::In) => {
2627+
let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) {
2628+
Ok(expr) => expr,
2629+
Err(expr_err) => {
2630+
expr_err.cancel();
2631+
return Err(err);
2632+
}
2633+
};
2634+
return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) {
2635+
let span = vec![start_span, self.token.span];
2636+
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
2637+
self.bump(); // )
2638+
err.cancel();
2639+
self.sess.emit_err(errors::ParenthesesInForHead {
2640+
span,
2641+
// With e.g. `for (x) in y)` this would replace `(x) in y)`
2642+
// with `x) in y)` which is syntactically invalid.
2643+
// However, this is prevented before we get here.
2644+
sugg: errors::ParenthesesInForHeadSugg { left, right },
2645+
});
2646+
Ok((self.mk_pat(start_span.to(right), ast::PatKind::Wild), expr))
2647+
} else {
2648+
Err(err)
2649+
};
2650+
}
2651+
Err(err) => return Err(err),
2652+
}
2653+
} else {
2654+
self.parse_pat_allow_top_alt(
2655+
None,
2656+
RecoverComma::Yes,
2657+
RecoverColon::Yes,
2658+
CommaRecoveryMode::LikelyTuple,
2659+
)?
26232660
};
2624-
2625-
let pat = self.parse_pat_allow_top_alt(
2626-
None,
2627-
RecoverComma::Yes,
2628-
RecoverColon::Yes,
2629-
CommaRecoveryMode::LikelyTuple,
2630-
)?;
26312661
if !self.eat_keyword(kw::In) {
26322662
self.error_missing_in_for_loop();
26332663
}
26342664
self.check_for_for_in_in_typo(self.prev_token.span);
26352665
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
2666+
Ok((pat, expr))
2667+
}
26362668

2637-
let pat = self.recover_parens_around_for_head(pat, begin_paren);
2638-
2669+
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2670+
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
2671+
let (pat, expr) = self.parse_for_head()?;
26392672
// Recover from missing expression in `for` loop
26402673
if matches!(expr.kind, ExprKind::Block(..))
26412674
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))

tests/ui/lint/issue-103435-extra-parentheses.fixed

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ fn main() {
1212
//~^ ERROR unnecessary parentheses around `if` condition
1313

1414
// reported by parser
15-
for(_x in 1..10){}
16-
//~^ ERROR expected one of
17-
//~| ERROR unexpected parentheses surrounding
15+
for _x in 1..10 {}
16+
//~^ ERROR unexpected parentheses surrounding
1817
}

tests/ui/lint/issue-103435-extra-parentheses.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ fn main() {
88
for(_x)in 1..10 {}
99
//~^ ERROR unnecessary parentheses around pattern
1010

11-
if(2 == 1){}
11+
if(2 == 1) {}
1212
//~^ ERROR unnecessary parentheses around `if` condition
1313

1414
// reported by parser
15-
for(_x in 1..10){}
16-
//~^ ERROR expected one of
17-
//~| ERROR unexpected parentheses surrounding
15+
for(_x in 1..10) {}
16+
//~^ ERROR unexpected parentheses surrounding
1817
}

tests/ui/lint/issue-103435-extra-parentheses.stderr

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
2-
--> $DIR/issue-103435-extra-parentheses.rs:15:12
1+
error: unexpected parentheses surrounding `for` loop head
2+
--> $DIR/issue-103435-extra-parentheses.rs:15:8
3+
|
4+
LL | for(_x in 1..10) {}
5+
| ^ ^
6+
|
7+
help: remove parentheses in `for` loop
8+
|
9+
LL - for(_x in 1..10) {}
10+
LL + for _x in 1..10 {}
311
|
4-
LL | for(_x in 1..10){}
5-
| ^^ expected one of `)`, `,`, `@`, or `|`
612

713
error: unnecessary parentheses around pattern
814
--> $DIR/issue-103435-extra-parentheses.rs:5:11
@@ -36,12 +42,12 @@ LL + for _x in 1..10 {}
3642
error: unnecessary parentheses around `if` condition
3743
--> $DIR/issue-103435-extra-parentheses.rs:11:7
3844
|
39-
LL | if(2 == 1){}
45+
LL | if(2 == 1) {}
4046
| ^ ^
4147
|
4248
help: remove these parentheses
4349
|
44-
LL - if(2 == 1){}
50+
LL - if(2 == 1) {}
4551
LL + if 2 == 1 {}
4652
|
4753

tests/ui/parser/recover/recover-enum2.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fn main() {
88
}
99

1010
// recover...
11-
let a = 1;
11+
let () = 1; //~ ERROR mismatched types
1212
enum Test2 {
1313
Fine,
1414
}
@@ -24,5 +24,6 @@ fn main() {
2424
enum Test4 {
2525
Nope(i32 {}) //~ ERROR: found `{`
2626
}
27+
let () = 1; //~ ERROR mismatched types
2728
}
2829
}

tests/ui/parser/recover/recover-enum2.stderr

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,22 @@ LL | enum Test4 {
1414
LL | Nope(i32 {})
1515
| ^ expected one of 7 possible tokens
1616

17-
error: aborting due to 2 previous errors
17+
error[E0308]: mismatched types
18+
--> $DIR/recover-enum2.rs:11:9
19+
|
20+
LL | let () = 1;
21+
| ^^ - this expression has type `{integer}`
22+
| |
23+
| expected integer, found `()`
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/recover-enum2.rs:27:13
27+
|
28+
LL | let () = 1;
29+
| ^^ - this expression has type `{integer}`
30+
| |
31+
| expected integer, found `()`
32+
33+
error: aborting due to 4 previous errors
1834

35+
For more information about this error, try `rustc --explain E0308`.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-rustfix
2+
// Here we test that the parser is able to recover in a situation like
3+
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
4+
// Instead we suggest that the user writes `for $pat in $expr`.
5+
6+
#![deny(unused)] // Make sure we don't trigger `unused_parens`.
7+
8+
fn main() {
9+
let vec = vec![1, 2, 3];
10+
11+
for _elem in vec {
12+
//~^ ERROR unexpected parentheses surrounding `for` loop head
13+
const _RECOVERY_WITNESS: u32 = 0u32; //~ ERROR mismatched types
14+
}
15+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// run-rustfix
12
// Here we test that the parser is able to recover in a situation like
23
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
34
// Instead we suggest that the user writes `for $pat in $expr`.
@@ -7,9 +8,8 @@
78
fn main() {
89
let vec = vec![1, 2, 3];
910

10-
for ( elem in vec ) {
11-
//~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in`
12-
//~| ERROR unexpected parentheses surrounding `for` loop head
13-
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
11+
for ( _elem in vec ) {
12+
//~^ ERROR unexpected parentheses surrounding `for` loop head
13+
const _RECOVERY_WITNESS: u32 = 0u8; //~ ERROR mismatched types
1414
}
1515
}
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1-
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
2-
--> $DIR/recover-for-loop-parens-around-head.rs:10:16
1+
error: unexpected parentheses surrounding `for` loop head
2+
--> $DIR/recover-for-loop-parens-around-head.rs:11:9
33
|
4-
LL | for ( elem in vec ) {
5-
| ^^ expected one of `)`, `,`, `@`, or `|`
4+
LL | for ( _elem in vec ) {
5+
| ^ ^
6+
|
7+
help: remove parentheses in `for` loop
8+
|
9+
LL - for ( _elem in vec ) {
10+
LL + for _elem in vec {
11+
|
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/recover-for-loop-parens-around-head.rs:13:40
15+
|
16+
LL | const _RECOVERY_WITNESS: u32 = 0u8;
17+
| ^^^ expected `u32`, found `u8`
18+
|
19+
help: change the type of the numeric literal from `u8` to `u32`
20+
|
21+
LL | const _RECOVERY_WITNESS: u32 = 0u32;
22+
| ~~~
623

7-
error: aborting due to previous error
24+
error: aborting due to 2 previous errors
825

26+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)