Skip to content

Commit 72c634b

Browse files
Rollup merge of rust-lang#107098 - compiler-errors:pat-mismatch-fn-call, r=lcnr
Suggest function call on pattern type mismatch Fixes rust-lang#101208 This could definitely be generalized to support more suggestions in pattern matches. We can't use all of [`FnCtxt::emit_type_mismatch_suggestions`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.emit_type_mismatch_suggestions), but it's on my to-do list to play around with more suggestions that would be productive in this position.
2 parents ef934d9 + f900104 commit 72c634b

File tree

6 files changed

+68
-10
lines changed

6 files changed

+68
-10
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4141
// #55810: Type check patterns first so we get types for all bindings.
4242
let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
4343
for arm in arms {
44-
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true);
44+
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
4545
}
4646

4747
// Now typecheck the blocks.

compiler/rustc_hir_typeck/src/check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
9090
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
9191
// Check the pattern.
9292
let ty_span = try { inputs_hir?.get(idx)?.span };
93-
fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
93+
fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
9494

9595
// Check that argument is Sized.
9696
// The check for a non-trivial pattern is a hack to avoid duplicate warnings

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13301330

13311331
// Does the expected pattern type originate from an expression and what is the span?
13321332
let (origin_expr, ty_span) = match (decl.ty, decl.init) {
1333-
(Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
1333+
(Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type.
13341334
(_, Some(init)) => {
1335-
(true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
1335+
(Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
13361336
} // No explicit type; so use the scrutinee.
1337-
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
1337+
_ => (None, None), // We have `let $pat;`, so the expected type is unconstrained.
13381338
};
13391339

13401340
// Type check the pattern. Override if necessary to avoid knock-on errors.

compiler/rustc_hir_typeck/src/pat.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct TopInfo<'tcx> {
4646
/// Was the origin of the `span` from a scrutinee expression?
4747
///
4848
/// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
49-
origin_expr: bool,
49+
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
5050
/// The span giving rise to the `expected` type, if one could be provided.
5151
///
5252
/// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
@@ -74,7 +74,8 @@ struct TopInfo<'tcx> {
7474

7575
impl<'tcx> FnCtxt<'_, 'tcx> {
7676
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
77-
let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
77+
let code =
78+
Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() };
7879
self.cause(cause_span, code)
7980
}
8081

@@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
8586
actual: Ty<'tcx>,
8687
ti: TopInfo<'tcx>,
8788
) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
88-
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
89+
let mut diag =
90+
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
91+
if let Some(expr) = ti.origin_expr {
92+
self.suggest_fn_call(&mut diag, expr, expected, |output| {
93+
self.can_eq(self.param_env, output, actual).is_ok()
94+
});
95+
}
96+
Some(diag)
8997
}
9098

9199
fn demand_eqtype_pat(
@@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127135
pat: &'tcx Pat<'tcx>,
128136
expected: Ty<'tcx>,
129137
span: Option<Span>,
130-
origin_expr: bool,
138+
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
131139
) {
132140
let info = TopInfo { expected, origin_expr, span };
133141
self.check_pat(pat, expected, INITIAL_BM, info);
@@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21462154
err.help("the semantics of slice patterns changed recently; see issue #62254");
21472155
} else if self.autoderef(span, expected_ty)
21482156
.any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
2149-
&& let (Some(span), true) = (ti.span, ti.origin_expr)
2157+
&& let Some(span) = ti.span
2158+
&& let Some(_) = ti.origin_expr
21502159
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
21512160
{
21522161
let ty = self.resolve_vars_if_possible(ti.expected);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
enum E {
2+
One(i32, i32),
3+
}
4+
5+
fn main() {
6+
let var = E::One;
7+
if let E::One(var1, var2) = var {
8+
//~^ ERROR mismatched types
9+
//~| HELP use parentheses to construct this tuple variant
10+
println!("{var1} {var2}");
11+
}
12+
13+
let Some(x) = Some;
14+
//~^ ERROR mismatched types
15+
//~| HELP use parentheses to construct this tuple variant
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/suggest-call-on-pat-mismatch.rs:7:12
3+
|
4+
LL | if let E::One(var1, var2) = var {
5+
| ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}`
6+
| |
7+
| expected enum constructor, found enum `E`
8+
|
9+
= note: expected enum constructor `fn(i32, i32) -> E {E::One}`
10+
found enum `E`
11+
help: use parentheses to construct this tuple variant
12+
|
13+
LL | if let E::One(var1, var2) = var(/* i32 */, /* i32 */) {
14+
| ++++++++++++++++++++++
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/suggest-call-on-pat-mismatch.rs:13:9
18+
|
19+
LL | let Some(x) = Some;
20+
| ^^^^^^^ ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}`
21+
| |
22+
| expected enum constructor, found enum `Option`
23+
|
24+
= note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}`
25+
found enum `Option<_>`
26+
help: use parentheses to construct this tuple variant
27+
|
28+
LL | let Some(x) = Some(/* value */);
29+
| +++++++++++++
30+
31+
error: aborting due to 2 previous errors
32+
33+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)