diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 0d42035e74b..8921c1c6a03 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8a4b7b5b99e..88c0c9703db 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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)) diff --git a/tests/ui/lint/issue-103435-extra-parentheses.fixed b/tests/ui/lint/issue-103435-extra-parentheses.fixed index 3c23ec3b8b9..74b5aa06e35 100644 --- a/tests/ui/lint/issue-103435-extra-parentheses.fixed +++ b/tests/ui/lint/issue-103435-extra-parentheses.fixed @@ -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 } diff --git a/tests/ui/lint/issue-103435-extra-parentheses.rs b/tests/ui/lint/issue-103435-extra-parentheses.rs index 8261610cf56..cc81a64f217 100644 --- a/tests/ui/lint/issue-103435-extra-parentheses.rs +++ b/tests/ui/lint/issue-103435-extra-parentheses.rs @@ -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 } diff --git a/tests/ui/lint/issue-103435-extra-parentheses.stderr b/tests/ui/lint/issue-103435-extra-parentheses.stderr index 15b906f256e..5a166eb5008 100644 --- a/tests/ui/lint/issue-103435-extra-parentheses.stderr +++ b/tests/ui/lint/issue-103435-extra-parentheses.stderr @@ -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 {} | diff --git a/tests/ui/parser/recover/recover-enum2.rs b/tests/ui/parser/recover/recover-enum2.rs index 0c942088955..56b57f625ba 100644 --- a/tests/ui/parser/recover/recover-enum2.rs +++ b/tests/ui/parser/recover/recover-enum2.rs @@ -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 } } diff --git a/tests/ui/parser/recover/recover-enum2.stderr b/tests/ui/parser/recover/recover-enum2.stderr index 71c2804c3a7..eb921450124 100644 --- a/tests/ui/parser/recover/recover-enum2.stderr +++ b/tests/ui/parser/recover/recover-enum2.stderr @@ -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`. diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.fixed b/tests/ui/parser/recover/recover-for-loop-parens-around-head.fixed new file mode 100644 index 00000000000..6afc2d99355 --- /dev/null +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.fixed @@ -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 + } +} diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs b/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs index 053b428bd12..b1716900c49 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.rs @@ -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 } } diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index b1540e19f44..beaa346e76c 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr @@ -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`.