@@ -4843,11 +4843,31 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
4843
4843
// #41425 -- label the implicit `()` as being the
4844
4844
// "found type" here, rather than the "expected type".
4845
4845
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| {
4847
4866
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" ) ;
4851
4871
}
4852
4872
} , false ) ;
4853
4873
}
@@ -4872,59 +4892,81 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
4872
4892
ty
4873
4893
}
4874
4894
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
+ }
4882
4913
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 {
4884
4924
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 {
4894
4933
ref decl, ..
4895
4934
} , ..) , ..
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 {
4902
4938
ref decl, ..
4903
4939
} , ..) , ..
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 ,
4913
4942
}
4914
4943
}
4915
4944
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
+
4916
4956
/// On implicit return expressions with mismatched types, provide the following suggestions:
4917
4957
///
4918
4958
/// - Point out the method's return type as the reason for the expected type
4919
4959
/// - Possible missing semicolon
4920
4960
/// - 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
+ ) {
4928
4970
self . suggest_missing_semicolon ( err, expression, expected, cause_span) ;
4929
4971
if let Some ( ( fn_decl, can_suggest) ) = self . get_fn_decl ( blk_id) {
4930
4972
self . suggest_missing_return_type ( err, & fn_decl, expected, found, can_suggest) ;
0 commit comments