about summary refs log tree commit diff
path: root/compiler/rustc_parse/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src')
-rw-r--r--compiler/rustc_parse/src/errors.rs3
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs11
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs109
4 files changed, 115 insertions, 10 deletions
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 7ec4ad6dc35..37eb463cba6 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -650,8 +650,9 @@ pub(crate) struct LeftArrowOperator {
 #[diag(parse_remove_let)]
 pub(crate) struct RemoveLet {
     #[primary_span]
-    #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")]
     pub span: Span,
+    #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")]
+    pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 0ac6133e828..0012db471ef 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2683,6 +2683,13 @@ impl<'a> Parser<'a> {
                 //            ^^
                 //     }
                 //
+                // We account for macro calls that were meant as conditions as well.
+                //
+                //     if ... {
+                //     } else if macro! { foo bar } {
+                //            ^^
+                //     }
+                //
                 // If $cond is "statement-like" such as ExprKind::While then we
                 // want to suggest wrapping in braces.
                 //
@@ -2693,7 +2700,9 @@ impl<'a> Parser<'a> {
                 //     }
                 //     ^
                     if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
-                        && classify::expr_requires_semi_to_be_stmt(&cond) =>
+                        && (classify::expr_requires_semi_to_be_stmt(&cond)
+                            || matches!(cond.kind, ExprKind::MacCall(..)))
+                    =>
                 {
                     self.dcx().emit_err(errors::ExpectedElseBlock {
                         first_tok_span,
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 3546e5b0f04..c4326427f67 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -685,7 +685,7 @@ impl<'a> Parser<'a> {
             self.bump();
             // Trim extra space after the `let`
             let span = lo.with_hi(self.token.span.lo());
-            self.dcx().emit_err(RemoveLet { span });
+            self.dcx().emit_err(RemoveLet { span: lo, suggestion: span });
             lo = self.token.span;
         }
 
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index b7cdae3e3e1..190cd9ed061 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -475,6 +475,7 @@ impl<'a> Parser<'a> {
     }
 
     fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> {
+        let prev = self.prev_token.span;
         let sp = self.token.span;
         let mut e = self.dcx().struct_span_err(sp, msg);
         let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
@@ -514,8 +515,97 @@ impl<'a> Parser<'a> {
                 } else {
                     stmt.span
                 };
+                self.suggest_fixes_misparsed_for_loop_head(
+                    &mut e,
+                    prev.between(sp),
+                    stmt_span,
+                    &stmt.kind,
+                );
+            }
+            Err(e) => {
+                self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
+                e.cancel();
+            }
+            _ => {}
+        }
+        e.span_label(sp, "expected `{`");
+        e
+    }
+
+    fn suggest_fixes_misparsed_for_loop_head(
+        &self,
+        e: &mut Diag<'_>,
+        between: Span,
+        stmt_span: Span,
+        stmt_kind: &StmtKind,
+    ) {
+        match (&self.token.kind, &stmt_kind) {
+            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
+                if let ExprKind::Call(..) = expr.kind =>
+            {
+                // for _ in x y() {}
+                e.span_suggestion_verbose(
+                    between,
+                    "you might have meant to write a method call",
+                    ".".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
+                if let ExprKind::Field(..) = expr.kind =>
+            {
+                // for _ in x y.z {}
+                e.span_suggestion_verbose(
+                    between,
+                    "you might have meant to write a field access",
+                    ".".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
+                if let ExprKind::Struct(expr) = &expr.kind
+                    && let None = expr.qself
+                    && expr.path.segments.len() == 1 =>
+            {
+                // This is specific to "mistyped `if` condition followed by empty body"
+                //
+                // for _ in x y {}
+                e.span_suggestion_verbose(
+                    between,
+                    "you might have meant to write a field access",
+                    ".".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
+                if let ExprKind::Lit(lit) = expr.kind
+                    && let None = lit.suffix
+                    && let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
+            {
+                // for _ in x 0 {}
+                // for _ in x 0.0 {}
+                e.span_suggestion_verbose(
+                    between,
+                    format!("you might have meant to write a field access"),
+                    ".".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
+                if let ExprKind::Loop(..)
+                | ExprKind::If(..)
+                | ExprKind::While(..)
+                | ExprKind::Match(..)
+                | ExprKind::ForLoop { .. }
+                | ExprKind::TryBlock(..)
+                | ExprKind::Ret(..)
+                | ExprKind::Closure(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Try(..) = expr.kind =>
+            {
+                // These are more likely to have been meant as a block body.
                 e.multipart_suggestion(
-                    "try placing this code inside a block",
+                    "you might have meant to write this as part of a block",
                     vec![
                         (stmt_span.shrink_to_lo(), "{ ".to_string()),
                         (stmt_span.shrink_to_hi(), " }".to_string()),
@@ -524,14 +614,19 @@ impl<'a> Parser<'a> {
                     Applicability::MaybeIncorrect,
                 );
             }
-            Err(e) => {
-                self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
-                e.cancel();
+            (token::OpenDelim(Delimiter::Brace), _) => {}
+            (_, _) => {
+                e.multipart_suggestion(
+                    "you might have meant to write this as part of a block",
+                    vec![
+                        (stmt_span.shrink_to_lo(), "{ ".to_string()),
+                        (stmt_span.shrink_to_hi(), " }".to_string()),
+                    ],
+                    // Speculative; has been misleading in the past (#46836).
+                    Applicability::MaybeIncorrect,
+                );
             }
-            _ => {}
         }
-        e.span_label(sp, "expected `{`");
-        e
     }
 
     fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {