Skip to content

Commit fb584d7

Browse files
committed
reintegrate #58981 into if/match logic and extend to if-let.
1 parent 38814db commit fb584d7

File tree

4 files changed

+137
-3
lines changed

4 files changed

+137
-3
lines changed

Diff for: src/librustc_typeck/check/_match.rs

+64-2
Original file line numberDiff line numberDiff line change
@@ -661,9 +661,25 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
661661
use ObligationCauseCode::*;
662662

663663
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+
664669
let cause = self.cause(expr.span, IfExpressionWithNoElse);
665670
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());
667683
} else {
668684
let cause = if source_if {
669685
match i {
@@ -700,6 +716,39 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
700716
coercion.complete(self)
701717
}
702718

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+
703752
fn if_cause(
704753
&self,
705754
expr: &'gcx hir::Expr,
@@ -708,6 +757,19 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
708757
else_ty: Ty<'tcx>,
709758
) -> ObligationCause<'tcx> {
710759
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+
// ```
711773
Some(expr.span)
712774
} else {
713775
// 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");
770832
};
771833

772834
// 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 {
774836
if let Some(expr) = &block.expr {
775837
expr.span
776838
} else if let Some(stmt) = block.stmts.last() {

Diff for: src/test/ui/if/if-without-else-as-fn-expr.rs

+23
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,29 @@ fn foo3(bar: usize) -> usize {
2020
//~^^^ ERROR if may be missing an else clause
2121
}
2222

23+
fn foo_let(bar: usize) -> usize {
24+
if let 0 = 1 {
25+
return 3;
26+
}
27+
//~^^^ ERROR if may be missing an else clause
28+
}
29+
30+
fn foo2_let(bar: usize) -> usize {
31+
let x: usize = if let 0 = 1 {
32+
return 3;
33+
};
34+
//~^^^ ERROR if may be missing an else clause
35+
x
36+
}
37+
38+
fn foo3_let(bar: usize) -> usize {
39+
if let 0 = 1 {
40+
3
41+
}
42+
//~^^^ ERROR if may be missing an else clause
43+
}
44+
2345
fn main() {
2446
let _ = foo(1);
2547
}
48+

Diff for: src/test/ui/if/if-without-else-as-fn-expr.stderr

+47-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,52 @@ LL | | }
4444
= note: `if` expressions without `else` evaluate to `()`
4545
= help: consider adding an `else` block that evaluates to the expected type
4646

47-
error: aborting due to 3 previous errors
47+
error[E0317]: if may be missing an else clause
48+
--> $DIR/if-without-else-as-fn-expr.rs:24:5
49+
|
50+
LL | fn foo_let(bar: usize) -> usize {
51+
| ----- expected `usize` because of this return type
52+
LL | / if let 0 = 1 {
53+
LL | | return 3;
54+
LL | | }
55+
| |_____^ expected usize, found ()
56+
|
57+
= note: expected type `usize`
58+
found type `()`
59+
= note: `if` expressions without `else` evaluate to `()`
60+
= help: consider adding an `else` block that evaluates to the expected type
61+
62+
error[E0317]: if may be missing an else clause
63+
--> $DIR/if-without-else-as-fn-expr.rs:31:20
64+
|
65+
LL | let x: usize = if let 0 = 1 {
66+
| _________-__________^
67+
| | |
68+
| | expected because of this assignment
69+
LL | | return 3;
70+
LL | | };
71+
| |_____^ expected usize, found ()
72+
|
73+
= note: expected type `usize`
74+
found type `()`
75+
= note: `if` expressions without `else` evaluate to `()`
76+
= help: consider adding an `else` block that evaluates to the expected type
77+
78+
error[E0317]: if may be missing an else clause
79+
--> $DIR/if-without-else-as-fn-expr.rs:39:5
80+
|
81+
LL | fn foo3_let(bar: usize) -> usize {
82+
| ----- expected `usize` because of this return type
83+
LL | / if let 0 = 1 {
84+
LL | | 3
85+
LL | | }
86+
| |_____^ expected usize, found ()
87+
|
88+
= note: expected type `usize`
89+
found type `()`
90+
= note: `if` expressions without `else` evaluate to `()`
91+
= help: consider adding an `else` block that evaluates to the expected type
92+
93+
error: aborting due to 6 previous errors
4894

4995
For more information about this error, try `rustc --explain E0317`.

Diff for: src/test/ui/issues/issue-19991.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ LL | |
66
LL | |
77
LL | |
88
LL | | 765
9+
| | --- found here
910
LL | | };
1011
| |_____^ expected (), found integer
1112
|
1213
= note: expected type `()`
1314
found type `{integer}`
15+
= note: `if` expressions without `else` evaluate to `()`
16+
= help: consider adding an `else` block that evaluates to the expected type
1417

1518
error: aborting due to previous error
1619

0 commit comments

Comments
 (0)