diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 427f967a9b6bd..236fec94bdba7 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 b758334484535..d6b1e56316b37 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 = >::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 0000000000000..93078cb4b144b --- /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 0000000000000..5087662e7bfa4 --- /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`.