@@ -8,7 +8,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
88use rustc_hir as hir;
99use rustc_hir:: def:: { CtorOf , DefKind } ;
1010use rustc_hir:: lang_items:: LangItem ;
11- use rustc_hir:: { ExprKind , ItemKind , Node } ;
11+ use rustc_hir:: { Expr , ExprKind , ItemKind , Node , Stmt , StmtKind } ;
1212use rustc_infer:: infer;
1313use rustc_middle:: lint:: in_external_macro;
1414use rustc_middle:: ty:: { self , Binder , Ty } ;
@@ -55,7 +55,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5555 pointing_at_return_type =
5656 self . suggest_missing_return_type ( err, & fn_decl, expected, found, can_suggest) ;
5757 let fn_id = self . tcx . hir ( ) . get_return_block ( blk_id) . unwrap ( ) ;
58- self . suggest_missing_return_expr ( err, expr, & fn_decl, expected, found, fn_id) ;
58+ self . suggest_missing_break_or_return_expr (
59+ err, expr, & fn_decl, expected, found, blk_id, fn_id,
60+ ) ;
5961 }
6062 pointing_at_return_type
6163 }
@@ -472,22 +474,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
472474 }
473475 }
474476
475- pub ( in super :: super ) fn suggest_missing_return_expr (
477+ pub ( in super :: super ) fn suggest_missing_break_or_return_expr (
476478 & self ,
477479 err : & mut DiagnosticBuilder < ' _ > ,
478480 expr : & ' tcx hir:: Expr < ' tcx > ,
479481 fn_decl : & hir:: FnDecl < ' _ > ,
480482 expected : Ty < ' tcx > ,
481483 found : Ty < ' tcx > ,
482484 id : hir:: HirId ,
485+ fn_id : hir:: HirId ,
483486 ) {
484487 if !expected. is_unit ( ) {
485488 return ;
486489 }
487490 let found = self . resolve_vars_with_obligations ( found) ;
491+
492+ let in_loop = self . is_loop ( id)
493+ || self . tcx . hir ( ) . parent_iter ( id) . any ( |( parent_id, _) | self . is_loop ( parent_id) ) ;
494+
495+ let in_local_statement = self . is_local_statement ( id)
496+ || self
497+ . tcx
498+ . hir ( )
499+ . parent_iter ( id)
500+ . any ( |( parent_id, _) | self . is_local_statement ( parent_id) ) ;
501+
502+ if in_loop && in_local_statement {
503+ err. multipart_suggestion (
504+ "you might have meant to break the loop with this value" ,
505+ vec ! [
506+ ( expr. span. shrink_to_lo( ) , "break " . to_string( ) ) ,
507+ ( expr. span. shrink_to_hi( ) , ";" . to_string( ) ) ,
508+ ] ,
509+ Applicability :: MaybeIncorrect ,
510+ ) ;
511+ return ;
512+ }
513+
488514 if let hir:: FnRetTy :: Return ( ty) = fn_decl. output {
489515 let ty = <dyn AstConv < ' _ > >:: ast_ty_to_ty ( self , ty) ;
490- let bound_vars = self . tcx . late_bound_vars ( id ) ;
516+ let bound_vars = self . tcx . late_bound_vars ( fn_id ) ;
491517 let ty = self . tcx . erase_late_bound_regions ( Binder :: bind_with_vars ( ty, bound_vars) ) ;
492518 let ty = self . normalize_associated_types_in ( expr. span , ty) ;
493519 if self . can_coerce ( found, ty) {
@@ -514,4 +540,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
514540 self . tcx . sess . parse_sess . expr_parentheses_needed ( err, * sp, None ) ;
515541 }
516542 }
543+
544+ fn is_loop ( & self , id : hir:: HirId ) -> bool {
545+ let node = self . tcx . hir ( ) . get ( id) ;
546+ matches ! ( node, Node :: Expr ( Expr { kind: ExprKind :: Loop ( ..) , .. } ) )
547+ }
548+
549+ fn is_local_statement ( & self , id : hir:: HirId ) -> bool {
550+ let node = self . tcx . hir ( ) . get ( id) ;
551+ matches ! ( node, Node :: Stmt ( Stmt { kind: StmtKind :: Local ( ..) , .. } ) )
552+ }
517553}
0 commit comments