Skip to content

Commit 1983a62

Browse files
committed
Detect bindings assigned blocks without tail expressions
Address #44173 for type check errors.
1 parent 388538f commit 1983a62

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+45
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7575
self.note_need_for_fn_pointer(err, expected, expr_ty);
7676
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
7777
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
78+
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
7879
}
7980

8081
/// Requires that the two types unify, and prints an error message if
@@ -1670,4 +1671,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16701671
Applicability::MachineApplicable,
16711672
);
16721673
}
1674+
1675+
/// Identify when the type error is because `()` is found in a binding that was assigned a
1676+
/// block without a tail expression.
1677+
fn check_for_binding_assigned_block_without_tail_expression(
1678+
&self,
1679+
err: &mut Diagnostic,
1680+
expr: &hir::Expr<'_>,
1681+
checked_ty: Ty<'tcx>,
1682+
expected_ty: Ty<'tcx>,
1683+
) {
1684+
if !checked_ty.is_unit() {
1685+
return;
1686+
}
1687+
let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
1688+
let hir::def::Res::Local(hir_id) = path.res else { return; };
1689+
let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
1690+
return;
1691+
};
1692+
let Some(hir::Node::Local(hir::Local {
1693+
ty: None,
1694+
init: Some(init),
1695+
..
1696+
})) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
1697+
let hir::ExprKind::Block(block, None) = init.kind else { return; };
1698+
if block.expr.is_some() {
1699+
return;
1700+
}
1701+
let [.., stmt] = block.stmts else {
1702+
err.span_help(block.span, "this empty block is missing a tail expression");
1703+
return;
1704+
};
1705+
let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
1706+
let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; };
1707+
if self.can_eq(self.param_env, expected_ty, ty).is_ok() {
1708+
err.span_suggestion_verbose(
1709+
stmt.span.with_lo(tail_expr.span.hi()),
1710+
"remove this semicolon",
1711+
"",
1712+
Applicability::MachineApplicable,
1713+
);
1714+
} else {
1715+
err.span_help(block.span, "this block is missing a tail expression");
1716+
}
1717+
}
16731718
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn main() {
2+
let x = {
3+
println!("foo");
4+
42;
5+
};
6+
let y = {};
7+
let z = {
8+
"hi";
9+
};
10+
println!("{}", x); //~ ERROR E0277
11+
println!("{}", y); //~ ERROR E0277
12+
println!("{}", z); //~ ERROR E0277
13+
let _: i32 = x; //~ ERROR E0308
14+
let _: i32 = y; //~ ERROR E0308
15+
let _: i32 = z; //~ ERROR E0308
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
error[E0277]: `()` doesn't implement `std::fmt::Display`
2+
--> $DIR/binding-assigned-block-without-tail-expression.rs:10:20
3+
|
4+
LL | println!("{}", x);
5+
| ^ `()` cannot be formatted with the default formatter
6+
|
7+
= help: the trait `std::fmt::Display` is not implemented for `()`
8+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
9+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
10+
11+
error[E0277]: `()` doesn't implement `std::fmt::Display`
12+
--> $DIR/binding-assigned-block-without-tail-expression.rs:11:20
13+
|
14+
LL | println!("{}", y);
15+
| ^ `()` cannot be formatted with the default formatter
16+
|
17+
= help: the trait `std::fmt::Display` is not implemented for `()`
18+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
19+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
20+
21+
error[E0277]: `()` doesn't implement `std::fmt::Display`
22+
--> $DIR/binding-assigned-block-without-tail-expression.rs:12:20
23+
|
24+
LL | println!("{}", z);
25+
| ^ `()` cannot be formatted with the default formatter
26+
|
27+
= help: the trait `std::fmt::Display` is not implemented for `()`
28+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
29+
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
30+
31+
error[E0308]: mismatched types
32+
--> $DIR/binding-assigned-block-without-tail-expression.rs:13:18
33+
|
34+
LL | let _: i32 = x;
35+
| --- ^ expected `i32`, found `()`
36+
| |
37+
| expected due to this
38+
|
39+
help: remove this semicolon
40+
|
41+
LL - 42;
42+
LL + 42
43+
|
44+
45+
error[E0308]: mismatched types
46+
--> $DIR/binding-assigned-block-without-tail-expression.rs:14:18
47+
|
48+
LL | let _: i32 = y;
49+
| --- ^ expected `i32`, found `()`
50+
| |
51+
| expected due to this
52+
|
53+
help: this empty block is missing a tail expression
54+
--> $DIR/binding-assigned-block-without-tail-expression.rs:6:13
55+
|
56+
LL | let y = {};
57+
| ^^
58+
59+
error[E0308]: mismatched types
60+
--> $DIR/binding-assigned-block-without-tail-expression.rs:15:18
61+
|
62+
LL | let _: i32 = z;
63+
| --- ^ expected `i32`, found `()`
64+
| |
65+
| expected due to this
66+
|
67+
help: this block is missing a tail expression
68+
--> $DIR/binding-assigned-block-without-tail-expression.rs:7:13
69+
|
70+
LL | let z = {
71+
| _____________^
72+
LL | | "hi";
73+
LL | | };
74+
| |_____^
75+
76+
error: aborting due to 6 previous errors
77+
78+
Some errors have detailed explanations: E0277, E0308.
79+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)