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
@ -11,12 +11,12 @@ use crate::errors::{
|
||||
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
|
||||
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
|
||||
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
|
||||
IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg,
|
||||
PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst,
|
||||
StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens,
|
||||
StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
|
||||
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
|
||||
IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg,
|
||||
SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg,
|
||||
StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt,
|
||||
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam,
|
||||
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
|
||||
UseEqInstead, WrapType,
|
||||
};
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
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_expr_for(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
|
||||
// Record whether we are about to parse `for (`.
|
||||
// This is used below for recovery in case of `for ( $stuff ) $block`
|
||||
// in which case we will suggest `for $stuff $block`.
|
||||
let begin_paren = match self.token.kind {
|
||||
token::OpenDelim(Delimiter::Parenthesis) => Some((
|
||||
self.token.span,
|
||||
self.prev_token.span.between(self.look_ahead(1, |t| t.span)),
|
||||
)),
|
||||
_ => None,
|
||||
fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
|
||||
let pat = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
|
||||
// Record whether we are about to parse `for (`.
|
||||
// This is used below for recovery in case of `for ( $stuff ) $block`
|
||||
// in which case we will suggest `for $stuff $block`.
|
||||
let start_span = self.token.span;
|
||||
let left = self.prev_token.span.between(self.look_ahead(1, |t| t.span));
|
||||
match self.parse_pat_allow_top_alt(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
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,
|
||||
)?
|
||||
};
|
||||
|
||||
let pat = self.parse_pat_allow_top_alt(
|
||||
None,
|
||||
RecoverComma::Yes,
|
||||
RecoverColon::Yes,
|
||||
CommaRecoveryMode::LikelyTuple,
|
||||
)?;
|
||||
if !self.eat_keyword(kw::In) {
|
||||
self.error_missing_in_for_loop();
|
||||
}
|
||||
self.check_for_for_in_in_typo(self.prev_token.span);
|
||||
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
|
||||
if matches!(expr.kind, ExprKind::Block(..))
|
||||
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
|
||||
|
@ -12,7 +12,6 @@ fn main() {
|
||||
//~^ ERROR unnecessary parentheses around `if` condition
|
||||
|
||||
// reported by parser
|
||||
for(_x in 1..10){}
|
||||
//~^ ERROR expected one of
|
||||
//~| ERROR unexpected parentheses surrounding
|
||||
for _x in 1..10 {}
|
||||
//~^ ERROR unexpected parentheses surrounding
|
||||
}
|
||||
|
@ -8,11 +8,10 @@ fn main() {
|
||||
for(_x)in 1..10 {}
|
||||
//~^ ERROR unnecessary parentheses around pattern
|
||||
|
||||
if(2 == 1){}
|
||||
if(2 == 1) {}
|
||||
//~^ ERROR unnecessary parentheses around `if` condition
|
||||
|
||||
// reported by parser
|
||||
for(_x in 1..10){}
|
||||
//~^ ERROR expected one of
|
||||
//~| ERROR unexpected parentheses surrounding
|
||||
for(_x in 1..10) {}
|
||||
//~^ ERROR unexpected parentheses surrounding
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
||||
--> $DIR/issue-103435-extra-parentheses.rs:15:12
|
||||
error: unexpected parentheses surrounding `for` loop head
|
||||
--> $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
|
||||
--> $DIR/issue-103435-extra-parentheses.rs:5:11
|
||||
@ -36,12 +42,12 @@ LL + for _x in 1..10 {}
|
||||
error: unnecessary parentheses around `if` condition
|
||||
--> $DIR/issue-103435-extra-parentheses.rs:11:7
|
||||
|
|
||||
LL | if(2 == 1){}
|
||||
LL | if(2 == 1) {}
|
||||
| ^ ^
|
||||
|
|
||||
help: remove these parentheses
|
||||
|
|
||||
LL - if(2 == 1){}
|
||||
LL - if(2 == 1) {}
|
||||
LL + if 2 == 1 {}
|
||||
|
|
||||
|
||||
|
@ -8,7 +8,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// recover...
|
||||
let a = 1;
|
||||
let () = 1; //~ ERROR mismatched types
|
||||
enum Test2 {
|
||||
Fine,
|
||||
}
|
||||
@ -24,5 +24,6 @@ fn main() {
|
||||
enum Test4 {
|
||||
Nope(i32 {}) //~ ERROR: found `{`
|
||||
}
|
||||
let () = 1; //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
@ -14,5 +14,22 @@ LL | enum Test4 {
|
||||
LL | Nope(i32 {})
|
||||
| ^ 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
|
||||
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
|
||||
// Instead we suggest that the user writes `for $pat in $expr`.
|
||||
@ -7,9 +8,8 @@
|
||||
fn main() {
|
||||
let vec = vec![1, 2, 3];
|
||||
|
||||
for ( elem in vec ) {
|
||||
//~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
||||
//~| ERROR unexpected parentheses surrounding `for` loop head
|
||||
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
|
||||
for ( _elem in vec ) {
|
||||
//~^ ERROR unexpected parentheses surrounding `for` loop head
|
||||
const _RECOVERY_WITNESS: u32 = 0u8; //~ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,26 @@
|
||||
error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
|
||||
--> $DIR/recover-for-loop-parens-around-head.rs:10:16
|
||||
error: unexpected parentheses surrounding `for` loop head
|
||||
--> $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…
x
Reference in New Issue
Block a user