Change how for (x in foo) {}
is handled
Use the same approach used for match arm patterns.
This commit is contained in:
parent
c47318983b
commit
0ff331bc78
10 changed files with 138 additions and 81 deletions
|
@ -11,12 +11,12 @@ use crate::errors::{
|
||||||
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
|
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
|
||||||
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
|
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
|
||||||
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
|
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
|
||||||
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
|
IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg,
|
||||||
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
|
SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg,
|
||||||
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
|
StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt,
|
||||||
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
|
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam,
|
||||||
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
|
||||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
|
UseEqInstead, WrapType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::fluent_generated as fluent;
|
use crate::fluent_generated as fluent;
|
||||||
|
@ -1994,37 +1994,6 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recovers a situation like `for ( $pat in $expr )`
|
|
||||||
/// and suggest writing `for $pat in $expr` instead.
|
|
||||||
///
|
|
||||||
/// This should be called before parsing the `$block`.
|
|
||||||
pub(super) fn recover_parens_around_for_head(
|
|
||||||
&mut self,
|
|
||||||
pat: P<Pat>,
|
|
||||||
begin_paren: Option<(Span, Span)>,
|
|
||||||
) -> P<Pat> {
|
|
||||||
match (&self.token.kind, begin_paren) {
|
|
||||||
(token::CloseDelim(Delimiter::Parenthesis), Some((begin_par_sp, left))) => {
|
|
||||||
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
|
|
||||||
self.bump();
|
|
||||||
self.sess.emit_err(ParenthesesInForHead {
|
|
||||||
span: vec![begin_par_sp, self.prev_token.span],
|
|
||||||
// With e.g. `for (x) in y)` this would replace `(x) in y)`
|
|
||||||
// with `x) in y)` which is syntactically invalid.
|
|
||||||
// However, this is prevented before we get here.
|
|
||||||
sugg: ParenthesesInForHeadSugg { left, right },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
|
|
||||||
pat.and_then(|pat| match pat.kind {
|
|
||||||
PatKind::Paren(pat) => pat,
|
|
||||||
_ => P(pat),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => pat,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn recover_seq_parse_error(
|
pub(super) fn recover_seq_parse_error(
|
||||||
&mut self,
|
&mut self,
|
||||||
delim: Delimiter,
|
delim: Delimiter,
|
||||||
|
|
|
@ -2609,33 +2609,66 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
|
fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
|
||||||
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
|
let pat = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
|
||||||
// Record whether we are about to parse `for (`.
|
// Record whether we are about to parse `for (`.
|
||||||
// This is used below for recovery in case of `for ( $stuff ) $block`
|
// This is used below for recovery in case of `for ( $stuff ) $block`
|
||||||
// in which case we will suggest `for $stuff $block`.
|
// in which case we will suggest `for $stuff $block`.
|
||||||
let begin_paren = match self.token.kind {
|
let start_span = self.token.span;
|
||||||
token::OpenDelim(Delimiter::Parenthesis) => Some((
|
let left = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
|
||||||
self.token.span,
|
match self.parse_pat_allow_top_alt(
|
||||||
self.prev_token.span.between(self.look_ahead(1, |t| t.span)),
|
|
||||||
)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let pat = self.parse_pat_allow_top_alt(
|
|
||||||
None,
|
None,
|
||||||
RecoverComma::Yes,
|
RecoverComma::Yes,
|
||||||
RecoverColon::Yes,
|
RecoverColon::Yes,
|
||||||
CommaRecoveryMode::LikelyTuple,
|
CommaRecoveryMode::LikelyTuple,
|
||||||
)?;
|
) {
|
||||||
|
Ok(pat) => pat,
|
||||||
|
Err(err) if self.eat_keyword(kw::In) => {
|
||||||
|
let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) {
|
||||||
|
Ok(expr) => expr,
|
||||||
|
Err(expr_err) => {
|
||||||
|
expr_err.cancel();
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) {
|
||||||
|
let span = vec![start_span, self.token.span];
|
||||||
|
let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
|
||||||
|
self.bump(); // )
|
||||||
|
err.cancel();
|
||||||
|
self.sess.emit_err(errors::ParenthesesInForHead {
|
||||||
|
span,
|
||||||
|
// With e.g. `for (x) in y)` this would replace `(x) in y)`
|
||||||
|
// with `x) in y)` which is syntactically invalid.
|
||||||
|
// However, this is prevented before we get here.
|
||||||
|
sugg: errors::ParenthesesInForHeadSugg { left, right },
|
||||||
|
});
|
||||||
|
Ok((self.mk_pat(start_span.to(right), ast::PatKind::Wild), expr))
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.parse_pat_allow_top_alt(
|
||||||
|
None,
|
||||||
|
RecoverComma::Yes,
|
||||||
|
RecoverColon::Yes,
|
||||||
|
CommaRecoveryMode::LikelyTuple,
|
||||||
|
)?
|
||||||
|
};
|
||||||
if !self.eat_keyword(kw::In) {
|
if !self.eat_keyword(kw::In) {
|
||||||
self.error_missing_in_for_loop();
|
self.error_missing_in_for_loop();
|
||||||
}
|
}
|
||||||
self.check_for_for_in_in_typo(self.prev_token.span);
|
self.check_for_for_in_in_typo(self.prev_token.span);
|
||||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
|
Ok((pat, expr))
|
||||||
|
}
|
||||||
|
|
||||||
let pat = self.recover_parens_around_for_head(pat, begin_paren);
|
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
|
||||||
|
fn parse_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
|
||||||
|
let (pat, expr) = self.parse_for_head()?;
|
||||||
// Recover from missing expression in `for` loop
|
// Recover from missing expression in `for` loop
|
||||||
if matches!(expr.kind, ExprKind::Block(..))
|
if matches!(expr.kind, ExprKind::Block(..))
|
||||||
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
|
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
|
||||||
|
|
|
@ -12,7 +12,6 @@ fn main() {
|
||||||
//~^ ERROR unnecessary parentheses around `if` condition
|
//~^ ERROR unnecessary parentheses around `if` condition
|
||||||
|
|
||||||
// reported by parser
|
// reported by parser
|
||||||
for(_x in 1..10){}
|
for _x in 1..10 {}
|
||||||
//~^ ERROR expected one of
|
//~^ ERROR unexpected parentheses surrounding
|
||||||
//~| ERROR unexpected parentheses surrounding
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,10 @@ fn main() {
|
||||||
for(_x)in 1..10 {}
|
for(_x)in 1..10 {}
|
||||||
//~^ ERROR unnecessary parentheses around pattern
|
//~^ ERROR unnecessary parentheses around pattern
|
||||||
|
|
||||||
if(2 == 1){}
|
if(2 == 1) {}
|
||||||
//~^ ERROR unnecessary parentheses around `if` condition
|
//~^ ERROR unnecessary parentheses around `if` condition
|
||||||
|
|
||||||
// reported by parser
|
// reported by parser
|
||||||
for(_x in 1..10){}
|
for(_x in 1..10) {}
|
||||||
//~^ ERROR expected one of
|
//~^ ERROR unexpected parentheses surrounding
|
||||||
//~| ERROR unexpected parentheses surrounding
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
error: unexpected parentheses surrounding `for` loop head
|
||||||
--> $DIR/issue-103435-extra-parentheses.rs:15:12
|
--> $DIR/issue-103435-extra-parentheses.rs:15:8
|
||||||
|
|
|
||||||
|
LL | for(_x in 1..10) {}
|
||||||
|
| ^ ^
|
||||||
|
|
|
||||||
|
help: remove parentheses in `for` loop
|
||||||
|
|
|
||||||
|
LL - for(_x in 1..10) {}
|
||||||
|
LL + for _x in 1..10 {}
|
||||||
|
|
|
|
||||||
LL | for(_x in 1..10){}
|
|
||||||
| ^^ expected one of `)`, `,`, `@`, or `|`
|
|
||||||
|
|
||||||
error: unnecessary parentheses around pattern
|
error: unnecessary parentheses around pattern
|
||||||
--> $DIR/issue-103435-extra-parentheses.rs:5:11
|
--> $DIR/issue-103435-extra-parentheses.rs:5:11
|
||||||
|
@ -36,12 +42,12 @@ LL + for _x in 1..10 {}
|
||||||
error: unnecessary parentheses around `if` condition
|
error: unnecessary parentheses around `if` condition
|
||||||
--> $DIR/issue-103435-extra-parentheses.rs:11:7
|
--> $DIR/issue-103435-extra-parentheses.rs:11:7
|
||||||
|
|
|
|
||||||
LL | if(2 == 1){}
|
LL | if(2 == 1) {}
|
||||||
| ^ ^
|
| ^ ^
|
||||||
|
|
|
|
||||||
help: remove these parentheses
|
help: remove these parentheses
|
||||||
|
|
|
|
||||||
LL - if(2 == 1){}
|
LL - if(2 == 1) {}
|
||||||
LL + if 2 == 1 {}
|
LL + if 2 == 1 {}
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover...
|
// recover...
|
||||||
let a = 1;
|
let () = 1; //~ ERROR mismatched types
|
||||||
enum Test2 {
|
enum Test2 {
|
||||||
Fine,
|
Fine,
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,6 @@ fn main() {
|
||||||
enum Test4 {
|
enum Test4 {
|
||||||
Nope(i32 {}) //~ ERROR: found `{`
|
Nope(i32 {}) //~ ERROR: found `{`
|
||||||
}
|
}
|
||||||
|
let () = 1; //~ ERROR mismatched types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,5 +14,22 @@ LL | enum Test4 {
|
||||||
LL | Nope(i32 {})
|
LL | Nope(i32 {})
|
||||||
| ^ expected one of 7 possible tokens
|
| ^ expected one of 7 possible tokens
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/recover-enum2.rs:11:9
|
||||||
|
|
|
||||||
|
LL | let () = 1;
|
||||||
|
| ^^ - this expression has type `{integer}`
|
||||||
|
| |
|
||||||
|
| expected integer, found `()`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/recover-enum2.rs:27:13
|
||||||
|
|
|
||||||
|
LL | let () = 1;
|
||||||
|
| ^^ - this expression has type `{integer}`
|
||||||
|
| |
|
||||||
|
| expected integer, found `()`
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// run-rustfix
|
||||||
|
// Here we test that the parser is able to recover in a situation like
|
||||||
|
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
|
||||||
|
// Instead we suggest that the user writes `for $pat in $expr`.
|
||||||
|
|
||||||
|
#![deny(unused)] // Make sure we don't trigger `unused_parens`.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let vec = vec![1, 2, 3];
|
||||||
|
|
||||||
|
for _elem in vec {
|
||||||
|
//~^ ERROR unexpected parentheses surrounding `for` loop head
|
||||||
|
const _RECOVERY_WITNESS: u32 = 0u32; //~ ERROR mismatched types
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// run-rustfix
|
||||||
// Here we test that the parser is able to recover in a situation like
|
// Here we test that the parser is able to recover in a situation like
|
||||||
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
|
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
|
||||||
// Instead we suggest that the user writes `for $pat in $expr`.
|
// Instead we suggest that the user writes `for $pat in $expr`.
|
||||||
|
@ -7,9 +8,8 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec = vec![1, 2, 3];
|
let vec = vec![1, 2, 3];
|
||||||
|
|
||||||
for ( elem in vec ) {
|
for ( _elem in vec ) {
|
||||||
//~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
//~^ ERROR unexpected parentheses surrounding `for` loop head
|
||||||
//~| ERROR unexpected parentheses surrounding `for` loop head
|
const _RECOVERY_WITNESS: u32 = 0u8; //~ ERROR mismatched types
|
||||||
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
error: unexpected parentheses surrounding `for` loop head
|
||||||
--> $DIR/recover-for-loop-parens-around-head.rs:10:16
|
--> $DIR/recover-for-loop-parens-around-head.rs:11:9
|
||||||
|
|
|
||||||
|
LL | for ( _elem in vec ) {
|
||||||
|
| ^ ^
|
||||||
|
|
|
||||||
|
help: remove parentheses in `for` loop
|
||||||
|
|
|
||||||
|
LL - for ( _elem in vec ) {
|
||||||
|
LL + for _elem in vec {
|
||||||
|
|
|
|
||||||
LL | for ( elem in vec ) {
|
|
||||||
| ^^ expected one of `)`, `,`, `@`, or `|`
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/recover-for-loop-parens-around-head.rs:13:40
|
||||||
|
|
|
||||||
|
LL | const _RECOVERY_WITNESS: u32 = 0u8;
|
||||||
|
| ^^^ expected `u32`, found `u8`
|
||||||
|
|
|
||||||
|
help: change the type of the numeric literal from `u8` to `u32`
|
||||||
|
|
|
||||||
|
LL | const _RECOVERY_WITNESS: u32 = 0u32;
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue