Skip to content

Commit 0d1b0f8

Browse files
committed
Suggest removing superfluous semicolos when statements used as expressions
1 parent 340bb19 commit 0d1b0f8

File tree

6 files changed

+308
-41
lines changed

6 files changed

+308
-41
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1632,7 +1632,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16321632
Ty::new_unit(self.tcx),
16331633
);
16341634
}
1635-
if !self.consider_removing_semicolon(blk, expected_ty, err) {
1635+
if !self.err_ctxt().consider_removing_semicolon(
1636+
blk,
1637+
expected_ty,
1638+
err,
1639+
) {
16361640
self.err_ctxt().consider_returning_binding(
16371641
blk,
16381642
expected_ty,

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+1-40
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_hir::{
2222
Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
2323
};
2424
use rustc_hir_analysis::astconv::AstConv;
25-
use rustc_infer::traits::{self, StatementAsExpression};
25+
use rustc_infer::traits::{self};
2626
use rustc_middle::lint::in_external_macro;
2727
use rustc_middle::middle::stability::EvalResult;
2828
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -1778,45 +1778,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17781778
}
17791779
}
17801780

1781-
/// A common error is to add an extra semicolon:
1782-
///
1783-
/// ```compile_fail,E0308
1784-
/// fn foo() -> usize {
1785-
/// 22;
1786-
/// }
1787-
/// ```
1788-
///
1789-
/// This routine checks if the final statement in a block is an
1790-
/// expression with an explicit semicolon whose type is compatible
1791-
/// with `expected_ty`. If so, it suggests removing the semicolon.
1792-
pub(crate) fn consider_removing_semicolon(
1793-
&self,
1794-
blk: &'tcx hir::Block<'tcx>,
1795-
expected_ty: Ty<'tcx>,
1796-
err: &mut Diagnostic,
1797-
) -> bool {
1798-
if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
1799-
if let StatementAsExpression::NeedsBoxing = boxed {
1800-
err.span_suggestion_verbose(
1801-
span_semi,
1802-
"consider removing this semicolon and boxing the expression",
1803-
"",
1804-
Applicability::HasPlaceholders,
1805-
);
1806-
} else {
1807-
err.span_suggestion_short(
1808-
span_semi,
1809-
"remove this semicolon to return this value",
1810-
"",
1811-
Applicability::MachineApplicable,
1812-
);
1813-
}
1814-
true
1815-
} else {
1816-
false
1817-
}
1818-
}
1819-
18201781
pub(crate) fn is_field_suggestable(
18211782
&self,
18221783
field: &ty::FieldDef,

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
19911991
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
19921992
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
19931993
self.suggest_function_pointers(cause, span, &exp_found, diag);
1994+
self.suggest_for_statments_as_exp(cause, &exp_found, diag);
19941995
}
19951996
}
19961997

compiler/rustc_infer/src/infer/error_reporting/suggest.rs

+96
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
use crate::infer::error_reporting::hir::Path;
12
use hir::def::CtorKind;
23
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
4+
use hir::{Local, QPath};
35
use rustc_data_structures::fx::FxIndexSet;
46
use rustc_errors::{Applicability, Diagnostic};
57
use rustc_hir as hir;
8+
use rustc_hir::def::Res;
9+
use rustc_hir::MatchSource;
10+
use rustc_hir::Node;
611
use rustc_middle::traits::{
712
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
813
StatementAsExpression,
@@ -289,6 +294,97 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
289294
}
290295
}
291296

297+
pub(super) fn suggest_for_statments_as_exp(
298+
&self,
299+
cause: &ObligationCause<'tcx>,
300+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
301+
diag: &mut Diagnostic,
302+
) {
303+
let ty::error::ExpectedFound { expected, found } = exp_found;
304+
if !found.peel_refs().is_unit() {
305+
return;
306+
}
307+
308+
let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
309+
else {
310+
return;
311+
};
312+
313+
let node = self.tcx.hir_node(*hir_id);
314+
let mut blocks = vec![];
315+
if let hir::Node::Block(block) = node
316+
&& let Some(expr) = block.expr
317+
&& let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
318+
&& let Res::Local(local) = res
319+
&& let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local)
320+
{
321+
fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
322+
match expr.kind {
323+
// `blk1` and `blk2` must be have the same types, it will be reported before reaching here
324+
hir::ExprKind::If(_, blk1, Some(blk2)) => {
325+
collect_blocks(blk1, blocks);
326+
collect_blocks(blk2, blocks);
327+
}
328+
hir::ExprKind::Match(_, arms, _) => {
329+
// all arms must have same types
330+
for arm in arms.iter() {
331+
collect_blocks(arm.body, blocks);
332+
}
333+
}
334+
hir::ExprKind::Block(blk, _) => {
335+
blocks.push(blk);
336+
}
337+
_ => {}
338+
}
339+
}
340+
collect_blocks(init, &mut blocks);
341+
}
342+
343+
let expected_inner: Ty<'_> = expected.peel_refs();
344+
for block in blocks.iter() {
345+
self.consider_removing_semicolon(block, expected_inner, diag);
346+
}
347+
}
348+
349+
/// A common error is to add an extra semicolon:
350+
///
351+
/// ```compile_fail,E0308
352+
/// fn foo() -> usize {
353+
/// 22;
354+
/// }
355+
/// ```
356+
///
357+
/// This routine checks if the final statement in a block is an
358+
/// expression with an explicit semicolon whose type is compatible
359+
/// with `expected_ty`. If so, it suggests removing the semicolon.
360+
pub fn consider_removing_semicolon(
361+
&self,
362+
blk: &'tcx hir::Block<'tcx>,
363+
expected_ty: Ty<'tcx>,
364+
err: &mut Diagnostic,
365+
) -> bool {
366+
if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
367+
if let StatementAsExpression::NeedsBoxing = boxed {
368+
err.span_suggestion_verbose(
369+
span_semi,
370+
"consider removing this semicolon and boxing the expression",
371+
"",
372+
Applicability::HasPlaceholders,
373+
);
374+
} else {
375+
err.span_suggestion_short(
376+
span_semi,
377+
"remove this semicolon to return this value",
378+
"",
379+
Applicability::MachineApplicable,
380+
);
381+
}
382+
true
383+
} else {
384+
false
385+
}
386+
}
387+
292388
pub(super) fn suggest_function_pointers(
293389
&self,
294390
cause: &ObligationCause<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#![allow(unused)]
2+
3+
fn test_if() -> i32 {
4+
let x = if true {
5+
eprintln!("hello");
6+
3;
7+
}
8+
else {
9+
4;
10+
};
11+
x //~ ERROR mismatched types
12+
}
13+
14+
fn test_if_without_binding() -> i32 {
15+
if true { //~ ERROR mismatched types
16+
eprintln!("hello");
17+
3;
18+
}
19+
else { //~ ERROR mismatched types
20+
4;
21+
}
22+
}
23+
24+
fn test_match() -> i32 {
25+
let v = 1;
26+
let res = match v {
27+
1 => { 1; }
28+
_ => { 2; }
29+
};
30+
res //~ ERROR mismatched types
31+
}
32+
33+
fn test_match_match_without_binding() -> i32 {
34+
let v = 1;
35+
match v {
36+
1 => { 1; } //~ ERROR mismatched types
37+
_ => { 2; } //~ ERROR mismatched types
38+
}
39+
}
40+
41+
fn test_match_arm_different_types() -> i32 {
42+
let v = 1;
43+
let res = match v {
44+
1 => { if 1 < 2 { 1 } else { 2 } }
45+
_ => { 2; } //~ ERROR `match` arms have incompatible types
46+
};
47+
res
48+
}
49+
50+
fn test_if_match_mixed() -> i32 {
51+
let x = if true {
52+
3;
53+
} else {
54+
match 1 {
55+
1 => { 1 }
56+
_ => { 2 }
57+
};
58+
};
59+
x //~ ERROR mismatched types
60+
}
61+
62+
fn test_if_match_mixed_failed() -> i32 {
63+
let x = if true {
64+
3;
65+
} else {
66+
// because this is a tailed expr, so we won't check deeper
67+
match 1 {
68+
1 => { 33; }
69+
_ => { 44; }
70+
}
71+
};
72+
x //~ ERROR mismatched types
73+
}
74+
75+
76+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-105431-stmts-as-exp.rs:11:5
3+
|
4+
LL | fn test_if() -> i32 {
5+
| --- expected `i32` because of return type
6+
...
7+
LL | x
8+
| ^ expected `i32`, found `()`
9+
|
10+
help: remove this semicolon to return this value
11+
|
12+
LL - 3;
13+
LL + 3
14+
|
15+
help: remove this semicolon to return this value
16+
|
17+
LL - 4;
18+
LL + 4
19+
|
20+
21+
error[E0308]: mismatched types
22+
--> $DIR/issue-105431-stmts-as-exp.rs:15:13
23+
|
24+
LL | if true {
25+
| _____________^
26+
LL | | eprintln!("hello");
27+
LL | | 3;
28+
| | - help: remove this semicolon to return this value
29+
LL | | }
30+
| |_____^ expected `i32`, found `()`
31+
32+
error[E0308]: mismatched types
33+
--> $DIR/issue-105431-stmts-as-exp.rs:19:10
34+
|
35+
LL | else {
36+
| __________^
37+
LL | | 4;
38+
| | - help: remove this semicolon to return this value
39+
LL | | }
40+
| |_____^ expected `i32`, found `()`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/issue-105431-stmts-as-exp.rs:30:5
44+
|
45+
LL | fn test_match() -> i32 {
46+
| --- expected `i32` because of return type
47+
...
48+
LL | res
49+
| ^^^ expected `i32`, found `()`
50+
|
51+
help: remove this semicolon to return this value
52+
|
53+
LL - 1 => { 1; }
54+
LL + 1 => { 1 }
55+
|
56+
help: remove this semicolon to return this value
57+
|
58+
LL - _ => { 2; }
59+
LL + _ => { 2 }
60+
|
61+
62+
error[E0308]: mismatched types
63+
--> $DIR/issue-105431-stmts-as-exp.rs:36:14
64+
|
65+
LL | 1 => { 1; }
66+
| ^^^-^^
67+
| | |
68+
| | help: remove this semicolon to return this value
69+
| expected `i32`, found `()`
70+
71+
error[E0308]: mismatched types
72+
--> $DIR/issue-105431-stmts-as-exp.rs:37:14
73+
|
74+
LL | _ => { 2; }
75+
| ^^^-^^
76+
| | |
77+
| | help: remove this semicolon to return this value
78+
| expected `i32`, found `()`
79+
80+
error[E0308]: `match` arms have incompatible types
81+
--> $DIR/issue-105431-stmts-as-exp.rs:45:16
82+
|
83+
LL | let res = match v {
84+
| _______________-
85+
LL | | 1 => { if 1 < 2 { 1 } else { 2 } }
86+
| | ------------------------- this is found to be of type `{integer}`
87+
LL | | _ => { 2; }
88+
| | ^-
89+
| | ||
90+
| | |help: consider removing this semicolon
91+
| | expected integer, found `()`
92+
LL | | };
93+
| |_____- `match` arms have incompatible types
94+
95+
error[E0308]: mismatched types
96+
--> $DIR/issue-105431-stmts-as-exp.rs:59:5
97+
|
98+
LL | fn test_if_match_mixed() -> i32 {
99+
| --- expected `i32` because of return type
100+
...
101+
LL | x
102+
| ^ expected `i32`, found `()`
103+
|
104+
help: remove this semicolon to return this value
105+
|
106+
LL - 3;
107+
LL + 3
108+
|
109+
help: remove this semicolon to return this value
110+
|
111+
LL - };
112+
LL + }
113+
|
114+
115+
error[E0308]: mismatched types
116+
--> $DIR/issue-105431-stmts-as-exp.rs:72:5
117+
|
118+
LL | fn test_if_match_mixed_failed() -> i32 {
119+
| --- expected `i32` because of return type
120+
LL | let x = if true {
121+
LL | 3;
122+
| - help: remove this semicolon to return this value
123+
...
124+
LL | x
125+
| ^ expected `i32`, found `()`
126+
127+
error: aborting due to 9 previous errors
128+
129+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)