Skip to content

Commit

Permalink
Rollup merge of rust-lang#84516 - torhovland:issue-84114, r=estebank
Browse files Browse the repository at this point in the history
Add suggestion to "use break" when attempting to implicit-break a loop

Fixes rust-lang#84114
  • Loading branch information
Dylan-DPC authored Apr 25, 2021
2 parents d1e2564 + 3b50461 commit a48c332
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 5 deletions.
4 changes: 3 additions & 1 deletion compiler/rustc_typeck/src/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
44 changes: 40 additions & 4 deletions compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -472,22 +474,46 @@ 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>,
fn_decl: &hir::FnDecl<'_>,
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) {
Expand All @@ -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(..), .. }))
}
}
31 changes: 31 additions & 0 deletions src/test/ui/loops/loop-no-implicit-break.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
47 changes: 47 additions & 0 deletions src/test/ui/loops/loop-no-implicit-break.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit a48c332

Please sign in to comment.