Skip to content

Commit 0ef0cc4

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

10 files changed

+138
-81
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+6-37
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;
@@ -1895,37 +1895,6 @@ impl<'a> Parser<'a> {
18951895
}
18961896
}
18971897

1898-
/// Recovers a situation like `for ( $pat in $expr )`
1899-
/// and suggest writing `for $pat in $expr` instead.
1900-
///
1901-
/// This should be called before parsing the `$block`.
1902-
pub(super) fn recover_parens_around_for_head(
1903-
&mut self,
1904-
pat: P<Pat>,
1905-
begin_paren: Option<(Span, Span)>,
1906-
) -> P<Pat> {
1907-
match (&self.token.kind, begin_paren) {
1908-
(token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => {
1909-
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
1910-
self.bump();
1911-
self.sess.emit_err(ParenthesesInForHead {
1912-
span: vec![begin_par_sp, self.prev_token.span],
1913-
// With e.g. `for (x) in y)` this would replace `(x) in y)`
1914-
// with `x) in y)` which is syntactically invalid.
1915-
// However, this is prevented before we get here.
1916-
sugg: ParenthesesInForHeadSugg { left, right },
1917-
});
1918-
1919-
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
1920-
pat.and_then(|pat| match pat.kind {
1921-
PatKind::Paren(pat) => pat,
1922-
_ => P(pat),
1923-
})
1924-
}
1925-
_ => pat,
1926-
}
1927-
}
1928-
19291898
pub(super) fn recover_seq_parse_error(
19301899
&mut self,
19311900
delim: Delimiter,

compiler/rustc_parse/src/parser/expr.rs

+53-20
Original file line numberDiff line numberDiff line change
@@ -2590,33 +2590,66 @@ impl<'a> Parser<'a> {
25902590
}
25912591
}
25922592

2593-
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2594-
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
2595-
// Record whether we are about to parse `for (`.
2596-
// This is used below for recovery in case of `for ( $stuff ) $block`
2597-
// in which case we will suggest `for $stuff $block`.
2598-
let begin_paren = match self.token.kind {
2599-
token::OpenDelim(Delimiter::Parenthesis) => Some((
2600-
self.token.span,
2601-
self.prev_token.span.between(self.look_ahead(1, |t| t.span)),
2602-
)),
2603-
_ => None,
2593+
fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
2594+
let pat = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
2595+
// Record whether we are about to parse `for (`.
2596+
// This is used below for recovery in case of `for ( $stuff ) $block`
2597+
// in which case we will suggest `for $stuff $block`.
2598+
let start_span = self.token.span;
2599+
let left = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
2600+
match self.parse_pat_allow_top_alt(
2601+
None,
2602+
RecoverComma::Yes,
2603+
RecoverColon::Yes,
2604+
CommaRecoveryMode::LikelyTuple,
2605+
) {
2606+
Ok(pat) => pat,
2607+
Err(err) if self.eat_keyword(kw::In) => {
2608+
let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) {
2609+
Ok(expr) => expr,
2610+
Err(expr_err) => {
2611+
expr_err.cancel();
2612+
return Err(err);
2613+
}
2614+
};
2615+
return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) {
2616+
let span = vec![start_span, self.token.span];
2617+
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
2618+
self.bump(); // )
2619+
err.cancel();
2620+
self.sess.emit_err(errors::ParenthesesInForHead {
2621+
span,
2622+
// With e.g. `for (x) in y)` this would replace `(x) in y)`
2623+
// with `x) in y)` which is syntactically invalid.
2624+
// However, this is prevented before we get here.
2625+
sugg: errors::ParenthesesInForHeadSugg { left, right },
2626+
});
2627+
Ok((self.mk_pat(start_span.to(right), ast::PatKind::Wild), expr))
2628+
} else {
2629+
Err(err)
2630+
};
2631+
}
2632+
Err(err) => return Err(err),
2633+
}
2634+
} else {
2635+
self.parse_pat_allow_top_alt(
2636+
None,
2637+
RecoverComma::Yes,
2638+
RecoverColon::Yes,
2639+
CommaRecoveryMode::LikelyTuple,
2640+
)?
26042641
};
2605-
2606-
let pat = self.parse_pat_allow_top_alt(
2607-
None,
2608-
RecoverComma::Yes,
2609-
RecoverColon::Yes,
2610-
CommaRecoveryMode::LikelyTuple,
2611-
)?;
26122642
if !self.eat_keyword(kw::In) {
26132643
self.error_missing_in_for_loop();
26142644
}
26152645
self.check_for_for_in_in_typo(self.prev_token.span);
26162646
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
2647+
Ok((pat, expr))
2648+
}
26172649

2618-
let pat = self.recover_parens_around_for_head(pat, begin_paren);
2619-
2650+
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
2651+
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
2652+
let (pat, expr) = self.parse_for_head()?;
26202653
// Recover from missing expression in `for` loop
26212654
if matches!(expr.kind, ExprKind::Block(..))
26222655
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))

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

+2-3
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

+3-4
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

+12-6
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

+2-1
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

+18-1
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`.
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+
}
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
}
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)