Skip to content

Commit 60204fd

Browse files
authored
Unrolled build for rust-lang#121153
Rollup merge of rust-lang#121153 - chenyukang:yukang-fix-105431-type-mismatch, r=estebank Suggest removing superfluous semicolon when statements used as expression Fixes rust-lang#105431 - it's not a pure recursive visitor, so I guess there may be some more complex scenarios not covered. - moved `consider_removing_semicolon` to `compiler/rustc_infer` for reusing this helper function.
2 parents e612d07 + e2ce5d7 commit 60204fd

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
@@ -1744,7 +1744,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17441744
Ty::new_unit(self.tcx),
17451745
);
17461746
}
1747-
if !self.consider_removing_semicolon(blk, expected_ty, err) {
1747+
if !self.err_ctxt().consider_removing_semicolon(
1748+
blk,
1749+
expected_ty,
1750+
err,
1751+
) {
17481752
self.err_ctxt().consider_returning_binding(
17491753
blk,
17501754
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;
@@ -1791,45 +1791,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17911791
}
17921792
}
17931793

1794-
/// A common error is to add an extra semicolon:
1795-
///
1796-
/// ```compile_fail,E0308
1797-
/// fn foo() -> usize {
1798-
/// 22;
1799-
/// }
1800-
/// ```
1801-
///
1802-
/// This routine checks if the final statement in a block is an
1803-
/// expression with an explicit semicolon whose type is compatible
1804-
/// with `expected_ty`. If so, it suggests removing the semicolon.
1805-
pub(crate) fn consider_removing_semicolon(
1806-
&self,
1807-
blk: &'tcx hir::Block<'tcx>,
1808-
expected_ty: Ty<'tcx>,
1809-
err: &mut Diag<'_>,
1810-
) -> bool {
1811-
if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
1812-
if let StatementAsExpression::NeedsBoxing = boxed {
1813-
err.span_suggestion_verbose(
1814-
span_semi,
1815-
"consider removing this semicolon and boxing the expression",
1816-
"",
1817-
Applicability::HasPlaceholders,
1818-
);
1819-
} else {
1820-
err.span_suggestion_short(
1821-
span_semi,
1822-
"remove this semicolon to return this value",
1823-
"",
1824-
Applicability::MachineApplicable,
1825-
);
1826-
}
1827-
true
1828-
} else {
1829-
false
1830-
}
1831-
}
1832-
18331794
pub(crate) fn is_field_suggestable(
18341795
&self,
18351796
field: &ty::FieldDef,

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

+1
Original file line numberDiff line numberDiff line change
@@ -1989,6 +1989,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
19891989
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
19901990
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
19911991
self.suggest_function_pointers(cause, span, &exp_found, diag);
1992+
self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
19921993
}
19931994
}
19941995

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, Diag};
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,
@@ -293,6 +298,97 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
293298
}
294299
}
295300

301+
pub(super) fn suggest_turning_stmt_into_expr(
302+
&self,
303+
cause: &ObligationCause<'tcx>,
304+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
305+
diag: &mut Diag<'_>,
306+
) {
307+
let ty::error::ExpectedFound { expected, found } = exp_found;
308+
if !found.peel_refs().is_unit() {
309+
return;
310+
}
311+
312+
let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
313+
else {
314+
return;
315+
};
316+
317+
let node = self.tcx.hir_node(*hir_id);
318+
let mut blocks = vec![];
319+
if let hir::Node::Block(block) = node
320+
&& let Some(expr) = block.expr
321+
&& let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
322+
&& let Res::Local(local) = res
323+
&& let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local)
324+
{
325+
fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
326+
match expr.kind {
327+
// `blk1` and `blk2` must be have the same types, it will be reported before reaching here
328+
hir::ExprKind::If(_, blk1, Some(blk2)) => {
329+
collect_blocks(blk1, blocks);
330+
collect_blocks(blk2, blocks);
331+
}
332+
hir::ExprKind::Match(_, arms, _) => {
333+
// all arms must have same types
334+
for arm in arms.iter() {
335+
collect_blocks(arm.body, blocks);
336+
}
337+
}
338+
hir::ExprKind::Block(blk, _) => {
339+
blocks.push(blk);
340+
}
341+
_ => {}
342+
}
343+
}
344+
collect_blocks(init, &mut blocks);
345+
}
346+
347+
let expected_inner: Ty<'_> = expected.peel_refs();
348+
for block in blocks.iter() {
349+
self.consider_removing_semicolon(block, expected_inner, diag);
350+
}
351+
}
352+
353+
/// A common error is to add an extra semicolon:
354+
///
355+
/// ```compile_fail,E0308
356+
/// fn foo() -> usize {
357+
/// 22;
358+
/// }
359+
/// ```
360+
///
361+
/// This routine checks if the final statement in a block is an
362+
/// expression with an explicit semicolon whose type is compatible
363+
/// with `expected_ty`. If so, it suggests removing the semicolon.
364+
pub fn consider_removing_semicolon(
365+
&self,
366+
blk: &'tcx hir::Block<'tcx>,
367+
expected_ty: Ty<'tcx>,
368+
diag: &mut Diag<'_>,
369+
) -> bool {
370+
if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
371+
if let StatementAsExpression::NeedsBoxing = boxed {
372+
diag.span_suggestion_verbose(
373+
span_semi,
374+
"consider removing this semicolon and boxing the expression",
375+
"",
376+
Applicability::HasPlaceholders,
377+
);
378+
} else {
379+
diag.span_suggestion_short(
380+
span_semi,
381+
"remove this semicolon to return this value",
382+
"",
383+
Applicability::MachineApplicable,
384+
);
385+
}
386+
true
387+
} else {
388+
false
389+
}
390+
}
391+
296392
pub(super) fn suggest_function_pointers(
297393
&self,
298394
cause: &ObligationCause<'tcx>,
+76
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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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/stmts-as-exp-105431.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)