about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-04-25 23:15:14 +0200
committerGitHub <noreply@github.com>2021-04-25 23:15:14 +0200
commitad3389a6dfa5c239df8db7e55e4a341bdd6d5f66 (patch)
tree1d01e0414bbe90f4abbfc244c30c434a040b58bb
parentae316d6603cfe333b56c49516a1197bee32c2758 (diff)
parent3b504610b6768d8cb700bc2a8fa2a6263b9d3a06 (diff)
downloadrust-ad3389a6dfa5c239df8db7e55e4a341bdd6d5f66.tar.gz
rust-ad3389a6dfa5c239df8db7e55e4a341bdd6d5f66.zip
Rollup merge of #84516 - torhovland:issue-84114, r=estebank
Add suggestion to "use break" when attempting to implicit-break a loop

Fixes #84114
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs4
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs44
-rw-r--r--src/test/ui/loops/loop-no-implicit-break.rs31
-rw-r--r--src/test/ui/loops/loop-no-implicit-break.stderr47
4 files changed, 121 insertions, 5 deletions
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index 427f967a9b6..236fec94bdb 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -1494,7 +1494,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         if let (Some((expr, _)), Some((fn_decl, _, _))) =
             (expression, fcx.get_node_fn_decl(parent_item))
         {
-            fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found, parent_id);
+            fcx.suggest_missing_break_or_return_expr(
+                &mut err, expr, fn_decl, expected, found, id, parent_id,
+            );
         }
 
         if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index b7583344845..d6b1e56316b 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -8,7 +8,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, ItemKind, Node};
+use rustc_hir::{Expr, ExprKind, ItemKind, Node, Stmt, StmtKind};
 use rustc_infer::infer;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Binder, Ty};
@@ -55,7 +55,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             pointing_at_return_type =
                 self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
             let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
-            self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found, fn_id);
+            self.suggest_missing_break_or_return_expr(
+                err, expr, &fn_decl, expected, found, blk_id, fn_id,
+            );
         }
         pointing_at_return_type
     }
@@ -472,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub(in super::super) fn suggest_missing_return_expr(
+    pub(in super::super) fn suggest_missing_break_or_return_expr(
         &self,
         err: &mut DiagnosticBuilder<'_>,
         expr: &'tcx hir::Expr<'tcx>,
@@ -480,14 +482,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         id: hir::HirId,
+        fn_id: hir::HirId,
     ) {
         if !expected.is_unit() {
             return;
         }
         let found = self.resolve_vars_with_obligations(found);
+
+        let in_loop = self.is_loop(id)
+            || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
+
+        let in_local_statement = self.is_local_statement(id)
+            || self
+                .tcx
+                .hir()
+                .parent_iter(id)
+                .any(|(parent_id, _)| self.is_local_statement(parent_id));
+
+        if in_loop && in_local_statement {
+            err.multipart_suggestion(
+                "you might have meant to break the loop with this value",
+                vec![
+                    (expr.span.shrink_to_lo(), "break ".to_string()),
+                    (expr.span.shrink_to_hi(), ";".to_string()),
+                ],
+                Applicability::MaybeIncorrect,
+            );
+            return;
+        }
+
         if let hir::FnRetTy::Return(ty) = fn_decl.output {
             let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
-            let bound_vars = self.tcx.late_bound_vars(id);
+            let bound_vars = self.tcx.late_bound_vars(fn_id);
             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
             let ty = self.normalize_associated_types_in(expr.span, ty);
             if self.can_coerce(found, ty) {
@@ -514,4 +540,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
         }
     }
+
+    fn is_loop(&self, id: hir::HirId) -> bool {
+        let node = self.tcx.hir().get(id);
+        matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
+    }
+
+    fn is_local_statement(&self, id: hir::HirId) -> bool {
+        let node = self.tcx.hir().get(id);
+        matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
+    }
 }
diff --git a/src/test/ui/loops/loop-no-implicit-break.rs b/src/test/ui/loops/loop-no-implicit-break.rs
new file mode 100644
index 00000000000..93078cb4b14
--- /dev/null
+++ b/src/test/ui/loops/loop-no-implicit-break.rs
@@ -0,0 +1,31 @@
+fn main() {
+    let a: i8 = loop {
+        1 //~ ERROR mismatched types
+    };
+
+    let b: i8 = loop {
+        break 1;
+    };
+}
+
+fn foo() -> i8 {
+    let a: i8 = loop {
+        1 //~ ERROR mismatched types
+    };
+
+    let b: i8 = loop {
+        break 1;
+    };
+
+    loop {
+        1 //~ ERROR mismatched types
+    }
+
+    loop {
+        return 1;
+    }
+
+    loop {
+        1 //~ ERROR mismatched types
+    }
+}
diff --git a/src/test/ui/loops/loop-no-implicit-break.stderr b/src/test/ui/loops/loop-no-implicit-break.stderr
new file mode 100644
index 00000000000..5087662e7bf
--- /dev/null
+++ b/src/test/ui/loops/loop-no-implicit-break.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:3:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to break the loop with this value
+   |
+LL |         break 1;
+   |         ^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:13:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to break the loop with this value
+   |
+LL |         break 1;
+   |         ^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:21:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to return this value
+   |
+LL |         return 1;
+   |         ^^^^^^  ^
+
+error[E0308]: mismatched types
+  --> $DIR/loop-no-implicit-break.rs:29:9
+   |
+LL |         1
+   |         ^ expected `()`, found integer
+   |
+help: you might have meant to return this value
+   |
+LL |         return 1;
+   |         ^^^^^^  ^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.