about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-11 03:30:55 +0000
committerbors <bors@rust-lang.org>2021-09-11 03:30:55 +0000
commit22719efcc570b043f2e519d6025e5f36eab38fe2 (patch)
treef7fb4178f9e9964f7a1965b9c13e9c1654cb1cd4 /compiler/rustc_parse/src/parser
parentb69fe57261086e70aea9d5b58819a1794bf7c121 (diff)
parentf77311bc2b01a2708e5676a9a3bcb3d07d5040e2 (diff)
downloadrust-22719efcc570b043f2e519d6025e5f36eab38fe2.tar.gz
rust-22719efcc570b043f2e519d6025e5f36eab38fe2.zip
Auto merge of #88824 - Manishearth:rollup-7bzk9h6, r=Manishearth
Rollup of 15 pull requests

Successful merges:

 - #85200 (Ignore derived Clone and Debug implementations during dead code analysis)
 - #86165 (Add proc_macro::Span::{before, after}.)
 - #87088 (Fix stray notes when the source code is not available)
 - #87441 (Emit suggestion when passing byte literal to format macro)
 - #88546 (Emit proper errors when on missing closure braces)
 - #88578 (fix(rustc): suggest `items` be borrowed in `for i in items[x..]`)
 - #88632 (Fix issues with Markdown summary options)
 - #88639 (rustdoc: Fix ICE with `doc(hidden)` on tuple variant fields)
 - #88667 (Tweak `write_fmt` doc.)
 - #88720 (Rustdoc coverage fields count)
 - #88732 (RustWrapper: avoid deleted unclear attribute methods)
 - #88742 (Fix table in docblocks)
 - #88776 (Workaround blink/chromium grid layout limitation of 1000 rows)
 - #88807 (Fix typo in docs for iterators)
 - #88812 (Fix typo `option` -> `options`.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_parse/src/parser')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs30
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs103
2 files changed, 124 insertions, 9 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 05156745105..dc80dab8c6c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1,9 +1,12 @@
 use super::pat::{RecoverColon, RecoverComma, PARAM_EXPECTED};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
-use super::{AttrWrapper, BlockMode, ForceCollect, Parser, PathStyle, Restrictions, TokenType};
+use super::{
+    AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, TokenType,
+};
 use super::{SemiColonMode, SeqSep, TokenExpectType, TrailingToken};
 use crate::maybe_recover_from_interpolated_ty_qpath;
 
+use ast::token::DelimToken;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Token, TokenKind};
 use rustc_ast::tokenstream::Spacing;
@@ -91,6 +94,8 @@ impl<'a> Parser<'a> {
     /// Parses an expression.
     #[inline]
     pub fn parse_expr(&mut self) -> PResult<'a, P<Expr>> {
+        self.current_closure.take();
+
         self.parse_expr_res(Restrictions::empty(), None)
     }
 
@@ -1736,7 +1741,7 @@ impl<'a> Parser<'a> {
         let capture_clause = self.parse_capture_clause()?;
         let decl = self.parse_fn_block_decl()?;
         let decl_hi = self.prev_token.span;
-        let body = match decl.output {
+        let mut body = match decl.output {
             FnRetTy::Default(_) => {
                 let restrictions = self.restrictions - Restrictions::STMT_EXPR;
                 self.parse_expr_res(restrictions, None)?
@@ -1753,11 +1758,28 @@ impl<'a> Parser<'a> {
             self.sess.gated_spans.gate(sym::async_closure, span);
         }
 
-        Ok(self.mk_expr(
+        if self.token.kind == TokenKind::Semi && self.token_cursor.frame.delim == DelimToken::Paren
+        {
+            // It is likely that the closure body is a block but where the
+            // braces have been removed. We will recover and eat the next
+            // statements later in the parsing process.
+            body = self.mk_expr_err(body.span);
+        }
+
+        let body_span = body.span;
+
+        let closure = self.mk_expr(
             lo.to(body.span),
             ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)),
             attrs,
-        ))
+        );
+
+        // Disable recovery for closure body
+        let spans =
+            ClosureSpans { whole_closure: closure.span, closing_pipe: decl_hi, body: body_span };
+        self.current_closure = Some(spans);
+
+        Ok(closure)
     }
 
     /// Parses an optional `move` prefix to a closure-like construct.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index c4419e995ed..5c701fefd17 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -142,6 +142,17 @@ pub struct Parser<'a> {
     /// If present, this `Parser` is not parsing Rust code but rather a macro call.
     subparser_name: Option<&'static str>,
     capture_state: CaptureState,
+    /// This allows us to recover when the user forget to add braces around
+    /// multiple statements in the closure body.
+    pub current_closure: Option<ClosureSpans>,
+}
+
+/// Stores span informations about a closure.
+#[derive(Clone)]
+pub struct ClosureSpans {
+    pub whole_closure: Span,
+    pub closing_pipe: Span,
+    pub body: Span,
 }
 
 /// Indicates a range of tokens that should be replaced by
@@ -440,6 +451,7 @@ impl<'a> Parser<'a> {
                 replace_ranges: Vec::new(),
                 inner_attr_ranges: Default::default(),
             },
+            current_closure: None,
         };
 
         // Make parser point to the first token.
@@ -761,8 +773,11 @@ impl<'a> Parser<'a> {
                     first = false;
                 } else {
                     match self.expect(t) {
-                        Ok(false) => {}
+                        Ok(false) => {
+                            self.current_closure.take();
+                        }
                         Ok(true) => {
+                            self.current_closure.take();
                             recovered = true;
                             break;
                         }
@@ -770,10 +785,29 @@ impl<'a> Parser<'a> {
                             let sp = self.prev_token.span.shrink_to_hi();
                             let token_str = pprust::token_kind_to_string(t);
 
-                            // Attempt to keep parsing if it was a similar separator.
-                            if let Some(ref tokens) = t.similar_tokens() {
-                                if tokens.contains(&self.token.kind) && !unclosed_delims {
-                                    self.bump();
+                            match self.current_closure.take() {
+                                Some(closure_spans) if self.token.kind == TokenKind::Semi => {
+                                    // Finding a semicolon instead of a comma
+                                    // after a closure body indicates that the
+                                    // closure body may be a block but the user
+                                    // forgot to put braces around its
+                                    // statements.
+
+                                    self.recover_missing_braces_around_closure_body(
+                                        closure_spans,
+                                        expect_err,
+                                    )?;
+
+                                    continue;
+                                }
+
+                                _ => {
+                                    // Attempt to keep parsing if it was a similar separator.
+                                    if let Some(ref tokens) = t.similar_tokens() {
+                                        if tokens.contains(&self.token.kind) && !unclosed_delims {
+                                            self.bump();
+                                        }
+                                    }
                                 }
                             }
 
@@ -839,6 +873,65 @@ impl<'a> Parser<'a> {
         Ok((v, trailing, recovered))
     }
 
+    fn recover_missing_braces_around_closure_body(
+        &mut self,
+        closure_spans: ClosureSpans,
+        mut expect_err: DiagnosticBuilder<'_>,
+    ) -> PResult<'a, ()> {
+        let initial_semicolon = self.token.span;
+
+        while self.eat(&TokenKind::Semi) {
+            let _ = self.parse_stmt(ForceCollect::Yes)?;
+        }
+
+        expect_err.set_primary_message(
+            "closure bodies that contain statements must be surrounded by braces",
+        );
+
+        let preceding_pipe_span = closure_spans.closing_pipe;
+        let following_token_span = self.token.span;
+
+        let mut first_note = MultiSpan::from(vec![initial_semicolon]);
+        first_note.push_span_label(
+            initial_semicolon,
+            "this `;` turns the preceding closure into a statement".to_string(),
+        );
+        first_note.push_span_label(
+            closure_spans.body,
+            "this expression is a statement because of the trailing semicolon".to_string(),
+        );
+        expect_err.span_note(first_note, "statement found outside of a block");
+
+        let mut second_note = MultiSpan::from(vec![closure_spans.whole_closure]);
+        second_note.push_span_label(
+            closure_spans.whole_closure,
+            "this is the parsed closure...".to_string(),
+        );
+        second_note.push_span_label(
+            following_token_span,
+            "...but likely you meant the closure to end here".to_string(),
+        );
+        expect_err.span_note(second_note, "the closure body may be incorrectly delimited");
+
+        expect_err.set_span(vec![preceding_pipe_span, following_token_span]);
+
+        let opening_suggestion_str = " {".to_string();
+        let closing_suggestion_str = "}".to_string();
+
+        expect_err.multipart_suggestion(
+            "try adding braces",
+            vec![
+                (preceding_pipe_span.shrink_to_hi(), opening_suggestion_str),
+                (following_token_span.shrink_to_lo(), closing_suggestion_str),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+
+        expect_err.emit();
+
+        Ok(())
+    }
+
     /// Parses a sequence, not including the closing delimiter. The function
     /// `f` must consume tokens until reaching the next separator or
     /// closing bracket.