@@ -4843,11 +4843,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
48434843 // #41425 -- label the implicit `()` as being the
48444844 // "found type" here, rather than the "expected type".
48454845 if !self . diverges . get ( ) . always ( ) {
4846- coerce. coerce_forced_unit ( self , & self . misc ( blk. span ) , & mut |err| {
4846+ // #50009 -- Do not point at the entire fn block span, point at the return type
4847+ // span, as it is the cause of the requirement, and
4848+ // `consider_hint_about_removing_semicolon` will point at the last expression
4849+ // if it were a relevant part of the error. This improves usability in editors
4850+ // that highlight errors inline.
4851+ let mut sp = blk. span ;
4852+ let mut fn_span = None ;
4853+ if let Some ( ( decl, ident) ) = self . get_parent_fn_decl ( blk. id ) {
4854+ let ret_sp = decl. output . span ( ) ;
4855+ if let Some ( block_sp) = self . parent_item_span ( blk. id ) {
4856+ // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
4857+ // output would otherwise be incorrect and even misleading. Make sure
4858+ // the span we're aiming at correspond to a `fn` body.
4859+ if block_sp == blk. span {
4860+ sp = ret_sp;
4861+ fn_span = Some ( ident. span ) ;
4862+ }
4863+ }
4864+ }
4865+ coerce. coerce_forced_unit ( self , & self . misc ( sp) , & mut |err| {
48474866 if let Some ( expected_ty) = expected. only_has_type ( self ) {
4848- self . consider_hint_about_removing_semicolon ( blk,
4849- expected_ty,
4850- err) ;
4867+ self . consider_hint_about_removing_semicolon ( blk, expected_ty, err) ;
4868+ }
4869+ if let Some ( fn_span) = fn_span {
4870+ err. span_label ( fn_span, "this function's body doesn't return" ) ;
48514871 }
48524872 } , false ) ;
48534873 }
@@ -4872,59 +4892,81 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
48724892 ty
48734893 }
48744894
4875- /// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether a
4876- /// suggestion can be made, `None` otherwise.
4877- pub fn get_fn_decl ( & self , blk_id : ast:: NodeId ) -> Option < ( hir:: FnDecl , bool ) > {
4878- // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
4879- // `while` before reaching it, as block tail returns are not available in them.
4880- if let Some ( fn_id) = self . tcx . hir ( ) . get_return_block ( blk_id) {
4881- let parent = self . tcx . hir ( ) . get ( fn_id) ;
4895+ fn parent_item_span ( & self , id : ast:: NodeId ) -> Option < Span > {
4896+ let node = self . tcx . hir ( ) . get ( self . tcx . hir ( ) . get_parent ( id) ) ;
4897+ match node {
4898+ Node :: Item ( & hir:: Item {
4899+ node : hir:: ItemKind :: Fn ( _, _, _, body_id) , ..
4900+ } ) |
4901+ Node :: ImplItem ( & hir:: ImplItem {
4902+ node : hir:: ImplItemKind :: Method ( _, body_id) , ..
4903+ } ) => {
4904+ let body = self . tcx . hir ( ) . body ( body_id) ;
4905+ if let ExprKind :: Block ( block, _) = & body. value . node {
4906+ return Some ( block. span ) ;
4907+ }
4908+ }
4909+ _ => { }
4910+ }
4911+ None
4912+ }
48824913
4883- if let Node :: Item ( & hir:: Item {
4914+ /// Given a function block's `NodeId`, return its `FnDecl` if it exists, or `None` otherwise.
4915+ fn get_parent_fn_decl ( & self , blk_id : ast:: NodeId ) -> Option < ( hir:: FnDecl , ast:: Ident ) > {
4916+ let parent = self . tcx . hir ( ) . get ( self . tcx . hir ( ) . get_parent ( blk_id) ) ;
4917+ self . get_node_fn_decl ( parent) . map ( |( fn_decl, ident, _) | ( fn_decl, ident) )
4918+ }
4919+
4920+ /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise.
4921+ fn get_node_fn_decl ( & self , node : Node ) -> Option < ( hir:: FnDecl , ast:: Ident , bool ) > {
4922+ match node {
4923+ Node :: Item ( & hir:: Item {
48844924 ident, node : hir:: ItemKind :: Fn ( ref decl, ..) , ..
4885- } ) = parent {
4886- decl. clone ( ) . and_then ( |decl| {
4887- // This is less than ideal, it will not suggest a return type span on any
4888- // method called `main`, regardless of whether it is actually the entry point,
4889- // but it will still present it as the reason for the expected type.
4890- Some ( ( decl, ident. name != Symbol :: intern ( "main" ) ) )
4891- } )
4892- } else if let Node :: TraitItem ( & hir:: TraitItem {
4893- node : hir:: TraitItemKind :: Method ( hir:: MethodSig {
4925+ } ) => decl. clone ( ) . and_then ( |decl| {
4926+ // This is less than ideal, it will not suggest a return type span on any
4927+ // method called `main`, regardless of whether it is actually the entry point,
4928+ // but it will still present it as the reason for the expected type.
4929+ Some ( ( decl, ident, ident. name != Symbol :: intern ( "main" ) ) )
4930+ } ) ,
4931+ Node :: TraitItem ( & hir:: TraitItem {
4932+ ident, node : hir:: TraitItemKind :: Method ( hir:: MethodSig {
48944933 ref decl, ..
48954934 } , ..) , ..
4896- } ) = parent {
4897- decl. clone ( ) . and_then ( |decl| {
4898- Some ( ( decl, true ) )
4899- } )
4900- } else if let Node :: ImplItem ( & hir:: ImplItem {
4901- node : hir:: ImplItemKind :: Method ( hir:: MethodSig {
4935+ } ) => decl. clone ( ) . and_then ( |decl| Some ( ( decl, ident, true ) ) ) ,
4936+ Node :: ImplItem ( & hir:: ImplItem {
4937+ ident, node : hir:: ImplItemKind :: Method ( hir:: MethodSig {
49024938 ref decl, ..
49034939 } , ..) , ..
4904- } ) = parent {
4905- decl. clone ( ) . and_then ( |decl| {
4906- Some ( ( decl, false ) )
4907- } )
4908- } else {
4909- None
4910- }
4911- } else {
4912- None
4940+ } ) => decl. clone ( ) . and_then ( |decl| Some ( ( decl, ident, false ) ) ) ,
4941+ _ => None ,
49134942 }
49144943 }
49154944
4945+ /// Given a `NodeId`, return the `FnDecl` of the method it is enclosed by and whether a
4946+ /// suggestion can be made, `None` otherwise.
4947+ pub fn get_fn_decl ( & self , blk_id : ast:: NodeId ) -> Option < ( hir:: FnDecl , bool ) > {
4948+ // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or
4949+ // `while` before reaching it, as block tail returns are not available in them.
4950+ self . tcx . hir ( ) . get_return_block ( blk_id) . and_then ( |blk_id| {
4951+ let parent = self . tcx . hir ( ) . get ( blk_id) ;
4952+ self . get_node_fn_decl ( parent) . map ( |( fn_decl, _, is_main) | ( fn_decl, is_main) )
4953+ } )
4954+ }
4955+
49164956 /// On implicit return expressions with mismatched types, provide the following suggestions:
49174957 ///
49184958 /// - Point out the method's return type as the reason for the expected type
49194959 /// - Possible missing semicolon
49204960 /// - Possible missing return type if the return type is the default, and not `fn main()`
4921- pub fn suggest_mismatched_types_on_tail ( & self ,
4922- err : & mut DiagnosticBuilder < ' tcx > ,
4923- expression : & ' gcx hir:: Expr ,
4924- expected : Ty < ' tcx > ,
4925- found : Ty < ' tcx > ,
4926- cause_span : Span ,
4927- blk_id : ast:: NodeId ) {
4961+ pub fn suggest_mismatched_types_on_tail (
4962+ & self ,
4963+ err : & mut DiagnosticBuilder < ' tcx > ,
4964+ expression : & ' gcx hir:: Expr ,
4965+ expected : Ty < ' tcx > ,
4966+ found : Ty < ' tcx > ,
4967+ cause_span : Span ,
4968+ blk_id : ast:: NodeId ,
4969+ ) {
49284970 self . suggest_missing_semicolon ( err, expression, expected, cause_span) ;
49294971 if let Some ( ( fn_decl, can_suggest) ) = self . get_fn_decl ( blk_id) {
49304972 self . suggest_missing_return_type ( err, & fn_decl, expected, found, can_suggest) ;
0 commit comments