Skip to content

Commit 8a167ed

Browse files
committed
Point at enclosing match when expecting () in arm
When encountering code like the following: ```rust fn main() { match 3 { 4 => 1, 3 => { println!("Yep it maches."); 2 } _ => 2 } println!("Bye!") } ``` point at the enclosing `match` expression and suggest ignoring the returned value: ``` error[E0308]: mismatched types --> $DIR/match-needing-semi.rs:8:13 | LL | / match 3 { LL | | 4 => 1, LL | | 3 => { LL | | 2 | | ^ expected (), found integer LL | | } LL | | _ => 2 LL | | } | | -- help: consider using a semicolon here | |_____| | expected this to be `()` | = note: expected type `()` found type `{integer} ``` Fix #40799.
1 parent 18f00b9 commit 8a167ed

File tree

10 files changed

+158
-33
lines changed

10 files changed

+158
-33
lines changed

src/librustc/hir/lowering/expr.rs

+11-12
Original file line numberDiff line numberDiff line change
@@ -1039,10 +1039,9 @@ impl LoweringContext<'_> {
10391039
) -> hir::Expr {
10401040
// expand <head>
10411041
let mut head = self.lower_expr(head);
1042-
let head_sp = head.span;
10431042
let desugared_span = self.mark_span_with_reason(
10441043
DesugaringKind::ForLoop,
1045-
head_sp,
1044+
head.span,
10461045
None,
10471046
);
10481047
head.span = desugared_span;
@@ -1088,21 +1087,21 @@ impl LoweringContext<'_> {
10881087

10891088
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
10901089
let match_expr = {
1091-
let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid));
1092-
let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter);
1090+
let iter = P(self.expr_ident(desugared_span, iter, iter_pat_nid));
1091+
let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter);
10931092
let next_path = &[sym::iter, sym::Iterator, sym::next];
10941093
let next_expr = P(self.expr_call_std_path(
1095-
head_sp,
1094+
desugared_span,
10961095
next_path,
10971096
hir_vec![ref_mut_iter],
10981097
));
10991098
let arms = hir_vec![pat_arm, break_arm];
11001099

1101-
self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar)
1100+
self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
11021101
};
1103-
let match_stmt = self.stmt_expr(head_sp, match_expr);
1102+
let match_stmt = self.stmt_expr(desugared_span, match_expr);
11041103

1105-
let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid));
1104+
let next_expr = P(self.expr_ident(desugared_span, next_ident, next_pat_hid));
11061105

11071106
// `let mut __next`
11081107
let next_let = self.stmt_let_pat(
@@ -1117,7 +1116,7 @@ impl LoweringContext<'_> {
11171116
let pat = self.lower_pat(pat);
11181117
let pat_let = self.stmt_let_pat(
11191118
ThinVec::new(),
1120-
head_sp,
1119+
desugared_span,
11211120
Some(next_expr),
11221121
pat,
11231122
hir::LocalSource::ForLoopDesugar,
@@ -1154,14 +1153,14 @@ impl LoweringContext<'_> {
11541153
let into_iter_path =
11551154
&[sym::iter, sym::IntoIterator, sym::into_iter];
11561155
P(self.expr_call_std_path(
1157-
head_sp,
1156+
desugared_span,
11581157
into_iter_path,
11591158
hir_vec![head],
11601159
))
11611160
};
11621161

11631162
let match_expr = P(self.expr_match(
1164-
head_sp,
1163+
desugared_span,
11651164
into_iter_expr,
11661165
hir_vec![iter_arm],
11671166
hir::MatchSource::ForLoopDesugar,
@@ -1173,7 +1172,7 @@ impl LoweringContext<'_> {
11731172
// surrounding scope of the `match` since the `match` is not a terminating scope.
11741173
//
11751174
// Also, add the attributes to the outer returned expr node.
1176-
self.expr_drop_temps(head_sp, match_expr, e.attrs.clone())
1175+
self.expr_drop_temps(desugared_span, match_expr, e.attrs.clone())
11771176
}
11781177

11791178
/// Desugar `ExprKind::Try` from: `<expr>?` into:

src/librustc/hir/map/mod.rs

+21
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,27 @@ impl<'hir> Map<'hir> {
818818
CRATE_HIR_ID
819819
}
820820

821+
pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&Expr> {
822+
for (_, node) in ParentHirIterator::new(hir_id, &self) {
823+
match node {
824+
Node::Item(_) |
825+
Node::ForeignItem(_) |
826+
Node::TraitItem(_) |
827+
Node::ImplItem(_) => break,
828+
Node::Expr(expr) => match expr.node {
829+
ExprKind::Match(_, _, _) => return Some(expr),
830+
_ => {}
831+
},
832+
Node::Stmt(stmt) => match stmt.node {
833+
StmtKind::Local(_) => break,
834+
_ => {}
835+
}
836+
_ => {}
837+
}
838+
}
839+
None
840+
}
841+
821842
/// Returns the nearest enclosing scope. A scope is roughly an item or block.
822843
pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option<HirId> {
823844
for (hir_id, node) in ParentHirIterator::new(hir_id, &self) {

src/librustc_typeck/check/_match.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3636
// 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
3737
//
3838
// FIXME(60707): Consider removing hack with principled solution.
39-
self.check_expr_has_type_or_error(discrim, self.tcx.types.bool)
39+
self.check_expr_has_type_or_error(discrim, self.tcx.types.bool, |_| {})
4040
} else {
4141
self.demand_discriminant_type(arms, discrim)
4242
};
@@ -106,7 +106,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
106106
if let Some(g) = &arm.guard {
107107
self.diverges.set(pats_diverge);
108108
match g {
109-
hir::Guard::If(e) => self.check_expr_has_type_or_error(e, tcx.types.bool),
109+
hir::Guard::If(e) => {
110+
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {})
111+
}
110112
};
111113
}
112114

@@ -442,7 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
442444
kind: TypeVariableOriginKind::TypeInference,
443445
span: discrim.span,
444446
});
445-
self.check_expr_has_type_or_error(discrim, discrim_ty);
447+
self.check_expr_has_type_or_error(discrim, discrim_ty, |_| {});
446448
discrim_ty
447449
}
448450
}

src/librustc_typeck/check/coercion.rs

+30-13
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
//! we may want to adjust precisely when coercions occur.
5252
5353
use crate::check::{FnCtxt, Needs};
54-
use errors::DiagnosticBuilder;
54+
use errors::{Applicability, DiagnosticBuilder};
5555
use rustc::hir;
5656
use rustc::hir::def_id::DefId;
5757
use rustc::hir::ptr::P;
@@ -1163,18 +1163,20 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
11631163
fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No)
11641164
} else {
11651165
match self.expressions {
1166-
Expressions::Dynamic(ref exprs) =>
1167-
fcx.try_find_coercion_lub(cause,
1168-
exprs,
1169-
self.merged_ty(),
1170-
expression,
1171-
expression_ty),
1172-
Expressions::UpFront(ref coercion_sites) =>
1173-
fcx.try_find_coercion_lub(cause,
1174-
&coercion_sites[0..self.pushed],
1175-
self.merged_ty(),
1176-
expression,
1177-
expression_ty),
1166+
Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub(
1167+
cause,
1168+
exprs,
1169+
self.merged_ty(),
1170+
expression,
1171+
expression_ty,
1172+
),
1173+
Expressions::UpFront(ref coercion_sites) => fcx.try_find_coercion_lub(
1174+
cause,
1175+
&coercion_sites[0..self.pushed],
1176+
self.merged_ty(),
1177+
expression,
1178+
expression_ty,
1179+
),
11781180
}
11791181
}
11801182
} else {
@@ -1302,6 +1304,21 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
13021304
blk_id,
13031305
);
13041306
let parent = fcx.tcx.hir().get(parent_id);
1307+
if let (Some(match_expr), true, false) = (
1308+
fcx.tcx.hir().get_match_if_cause(expr.hir_id),
1309+
expected.is_unit(),
1310+
pointing_at_return_type,
1311+
) {
1312+
if match_expr.span.desugaring_kind().is_none() {
1313+
db.span_label(match_expr.span, "expected this to be `()`");
1314+
db.span_suggestion_short(
1315+
match_expr.span.shrink_to_hi(),
1316+
"consider using a semicolon here",
1317+
";".to_string(),
1318+
Applicability::MachineApplicable,
1319+
);
1320+
}
1321+
}
13051322
fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
13061323
} else {
13071324
fcx.get_fn_decl(parent_id)

src/librustc_typeck/check/expr.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5353
&self,
5454
expr: &'tcx hir::Expr,
5555
expected: Ty<'tcx>,
56+
extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
5657
) -> Ty<'tcx> {
57-
self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected))
58+
self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
5859
}
5960

6061
fn check_expr_meets_expectation_or_error(
6162
&self,
6263
expr: &'tcx hir::Expr,
6364
expected: Expectation<'tcx>,
65+
extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
6466
) -> Ty<'tcx> {
6567
let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
6668
let mut ty = self.check_expr_with_expectation(expr, expected);
@@ -88,6 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8890
ExprKind::DropTemps(expr) => expr,
8991
_ => expr,
9092
};
93+
extend_err(&mut err);
9194
// Error possibly reported in `check_assign` so avoid emitting error again.
9295
err.emit_unless(self.is_assign_to_bool(expr, expected_ty));
9396
}
@@ -971,7 +974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
971974
kind: TypeVariableOriginKind::MiscVariable,
972975
span: element.span,
973976
});
974-
let element_ty = self.check_expr_has_type_or_error(&element, ty);
977+
let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {});
975978
(element_ty, ty)
976979
}
977980
};
@@ -1058,7 +1061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10581061
// the fields with the base_expr. This could cause us to hit errors later
10591062
// when certain fields are assumed to exist that in fact do not.
10601063
if !error_happened {
1061-
self.check_expr_has_type_or_error(base_expr, adt_ty);
1064+
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
10621065
match adt_ty.kind {
10631066
ty::Adt(adt, substs) if adt.is_struct() => {
10641067
let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {

src/librustc_typeck/check/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -3881,7 +3881,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
38813881
hir::StmtKind::Item(_) => {}
38823882
hir::StmtKind::Expr(ref expr) => {
38833883
// Check with expected type of `()`.
3884-
self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit());
3884+
3885+
self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
3886+
err.span_suggestion_short(
3887+
expr.span.shrink_to_hi(),
3888+
"consider using a semicolon here",
3889+
";".to_string(),
3890+
Applicability::MachineApplicable,
3891+
);
3892+
});
38853893
}
38863894
hir::StmtKind::Semi(ref expr) => {
38873895
self.check_expr(&expr);

src/test/ui/struct-literal-variant-in-if.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ error[E0308]: mismatched types
5050
--> $DIR/struct-literal-variant-in-if.rs:10:20
5151
|
5252
LL | if x == E::V { field } {}
53-
| ^^^^^ expected (), found bool
53+
| ---------------^^^^^--- help: consider using a semicolon here
54+
| | |
55+
| | expected (), found bool
56+
| expected this to be `()`
5457
|
5558
= note: expected type `()`
5659
found type `bool`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-fail
2+
// run-rustfix
3+
4+
fn main() {
5+
match 3 {
6+
4 => 1,
7+
3 => {
8+
2 //~ ERROR mismatched types
9+
}
10+
_ => 2
11+
};
12+
match 3 { //~ ERROR mismatched types
13+
4 => 1,
14+
3 => 2,
15+
_ => 2
16+
};
17+
let _ = ();
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-fail
2+
// run-rustfix
3+
4+
fn main() {
5+
match 3 {
6+
4 => 1,
7+
3 => {
8+
2 //~ ERROR mismatched types
9+
}
10+
_ => 2
11+
}
12+
match 3 { //~ ERROR mismatched types
13+
4 => 1,
14+
3 => 2,
15+
_ => 2
16+
}
17+
let _ = ();
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/match-needing-semi.rs:8:13
3+
|
4+
LL | / match 3 {
5+
LL | | 4 => 1,
6+
LL | | 3 => {
7+
LL | | 2
8+
| | ^ expected (), found integer
9+
LL | | }
10+
LL | | _ => 2
11+
LL | | }
12+
| | -- help: consider using a semicolon here
13+
| |_____|
14+
| expected this to be `()`
15+
|
16+
= note: expected type `()`
17+
found type `{integer}`
18+
19+
error[E0308]: mismatched types
20+
--> $DIR/match-needing-semi.rs:12:5
21+
|
22+
LL | / match 3 {
23+
LL | | 4 => 1,
24+
LL | | 3 => 2,
25+
LL | | _ => 2
26+
LL | | }
27+
| | ^- help: consider using a semicolon here
28+
| |_____|
29+
| expected (), found integer
30+
|
31+
= note: expected type `()`
32+
found type `{integer}`
33+
34+
error: aborting due to 2 previous errors
35+
36+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)