@@ -661,9 +661,25 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
661
661
use ObligationCauseCode :: * ;
662
662
663
663
if is_if_fallback {
664
+ let then_expr = & arms[ 0 ] . body ;
665
+ // If this `if` expr is the parent's function return expr,
666
+ // the cause of the type coercion is the return type, point at it. (#25228)
667
+ let ret_reason = self . maybe_get_coercion_reason ( then_expr. hir_id , expr. span ) ;
668
+
664
669
let cause = self . cause ( expr. span , IfExpressionWithNoElse ) ;
665
670
assert ! ( arm_ty. is_unit( ) ) ;
666
- coercion. coerce_forced_unit ( self , & cause, & mut |_| ( ) , true ) ;
671
+
672
+ coercion. coerce_forced_unit ( self , & cause, & mut |err| {
673
+ if let Some ( ( span, msg) ) = & ret_reason {
674
+ err. span_label ( * span, msg. as_str ( ) ) ;
675
+ } else if let ExprKind :: Block ( block, _) = & then_expr. node {
676
+ if let Some ( expr) = & block. expr {
677
+ err. span_label ( expr. span , "found here" . to_string ( ) ) ;
678
+ }
679
+ }
680
+ err. note ( "`if` expressions without `else` evaluate to `()`" ) ;
681
+ err. help ( "consider adding an `else` block that evaluates to the expected type" ) ;
682
+ } , ret_reason. is_none ( ) ) ;
667
683
} else {
668
684
let cause = if source_if {
669
685
match i {
@@ -700,6 +716,39 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
700
716
coercion. complete ( self )
701
717
}
702
718
719
+ fn maybe_get_coercion_reason ( & self , hir_id : hir:: HirId , span : Span ) -> Option < ( Span , String ) > {
720
+ use hir:: Node :: { Block , Item } ;
721
+
722
+ let node = self . tcx . hir ( ) . get_by_hir_id ( self . tcx . hir ( ) . get_parent_node_by_hir_id (
723
+ self . tcx . hir ( ) . get_parent_node_by_hir_id ( hir_id) ,
724
+ ) ) ;
725
+ if let Block ( block) = node {
726
+ // check that the body's parent is an fn
727
+ let parent = self . tcx . hir ( ) . get_by_hir_id (
728
+ self . tcx . hir ( ) . get_parent_node_by_hir_id (
729
+ self . tcx . hir ( ) . get_parent_node_by_hir_id ( block. hir_id ) ,
730
+ ) ,
731
+ ) ;
732
+ if let ( Some ( expr) , Item ( hir:: Item {
733
+ node : hir:: ItemKind :: Fn ( ..) , ..
734
+ } ) ) = ( & block. expr , parent) {
735
+ // check that the `if` expr without `else` is the fn body's expr
736
+ if expr. span == span {
737
+ return self . get_fn_decl ( hir_id) . map ( |( fn_decl, _) | (
738
+ fn_decl. output . span ( ) ,
739
+ format ! ( "expected `{}` because of this return type" , fn_decl. output) ,
740
+ ) ) ;
741
+ }
742
+ }
743
+ }
744
+ if let hir:: Node :: Local ( hir:: Local {
745
+ ty : Some ( _) , pat, ..
746
+ } ) = node {
747
+ return Some ( ( pat. span , "expected because of this assignment" . to_string ( ) ) ) ;
748
+ }
749
+ None
750
+ }
751
+
703
752
fn if_cause (
704
753
& self ,
705
754
expr : & ' gcx hir:: Expr ,
@@ -708,6 +757,19 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
708
757
else_ty : Ty < ' tcx > ,
709
758
) -> ObligationCause < ' tcx > {
710
759
let mut outer_sp = if self . tcx . sess . source_map ( ) . is_multiline ( expr. span ) {
760
+ // The `if`/`else` isn't in one line in the output, include some context to make it
761
+ // clear it is an if/else expression:
762
+ // ```
763
+ // LL | let x = if true {
764
+ // | _____________-
765
+ // LL || 10i32
766
+ // || ----- expected because of this
767
+ // LL || } else {
768
+ // LL || 10u32
769
+ // || ^^^^^ expected i32, found u32
770
+ // LL || };
771
+ // ||_____- if and else have incompatible types
772
+ // ```
711
773
Some ( expr. span )
712
774
} else {
713
775
// The entire expression is in one line, only point at the arms
@@ -770,7 +832,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
770
832
} ;
771
833
772
834
// Compute `Span` of `then` part of `if`-expression:
773
- let then_sp: Span = if let ExprKind :: Block ( block, _) = & then_expr. node {
835
+ let then_sp = if let ExprKind :: Block ( block, _) = & then_expr. node {
774
836
if let Some ( expr) = & block. expr {
775
837
expr. span
776
838
} else if let Some ( stmt) = block. stmts . last ( ) {
0 commit comments