diff options
| author | Mazdak Farrokhzad <twingoow@gmail.com> | 2019-07-24 10:26:32 +0200 |
|---|---|---|
| committer | Mazdak Farrokhzad <twingoow@gmail.com> | 2019-07-28 20:43:09 +0200 |
| commit | dfad725be540137e0bc3022fe5341378e4690b9b (patch) | |
| tree | f8b4f5d3561be6ae57395dabea7b244f1e5a8b78 /src/libsyntax/parse | |
| parent | 023525dbda35748a10713471b948974b68a1c2cc (diff) | |
| download | rust-dfad725be540137e0bc3022fe5341378e4690b9b.tar.gz rust-dfad725be540137e0bc3022fe5341378e4690b9b.zip | |
Recover 'for ( $pat in $expr ) $block'.
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/diagnostics.rs | 44 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 11 |
2 files changed, 55 insertions, 0 deletions
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index f4fc87506f3..e9dcfa81343 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -923,6 +923,50 @@ impl<'a> Parser<'a> { } } + /// Recover a situation like `for ( $pat in $expr )` + /// and suggest writing `for $pat in $expr` instead. + /// + /// This should be called before parsing the `$block`. + crate fn recover_parens_around_for_head( + &mut self, + pat: P<Pat>, + expr: &Expr, + begin_paren: Option<Span>, + ) -> P<Pat> { + match (&self.token.kind, begin_paren) { + (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { + self.bump(); + + let pat_str = self + .sess + .source_map() + // Remove the `(` from the span of the pattern: + .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) + .unwrap_or_else(|_| pprust::pat_to_string(&pat)); + + self.struct_span_err(self.prev_span, "unexpected closing `)`") + .span_label(begin_par_sp, "opening `(`") + .span_suggestion( + begin_par_sp.to(self.prev_span), + "remove parenthesis in `for` loop", + format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), + // 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. + Applicability::MachineApplicable, + ) + .emit(); + + // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. + pat.and_then(|pat| match pat.node { + PatKind::Paren(pat) => pat, + _ => P(pat), + }) + } + _ => pat, + } + } + crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { self.token.is_ident() && if let ast::ExprKind::Path(..) = node { true } else { false } && diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8f8ed411180..42030acf9df 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3275,6 +3275,14 @@ impl<'a> Parser<'a> { mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { // Parse: `for <src_pat> in <src_expr> <src_loop_block>` + // 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(token::Paren) => Some(self.token.span), + _ => None, + }; + let pat = self.parse_top_level_pat()?; if !self.eat_keyword(kw::In) { let in_span = self.prev_span.between(self.token.span); @@ -3290,6 +3298,9 @@ impl<'a> Parser<'a> { let in_span = self.prev_span; self.check_for_for_in_in_typo(in_span); let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + let pat = self.recover_parens_around_for_head(pat, &expr, begin_paren); + let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); |
