Skip to content

Commit a0d2862

Browse files
authored
Rollup merge of #95386 - compiler-errors:try-wrapping, r=oli-obk
Suggest wrapping patterns in enum variants Structured suggestion to wrap a pattern in a single-field enum or struct: ```diff struct A; enum B { A(A), } fn main(b: B) { match b { - A => {} + B::A(A) => {} } } ``` Half of #94942, the other half I'm not exactly sure how to fix. Also includes two drive-by changes (that I am open to splitting out into another PR, but thought they could be rolled up into this one): - 07776c1: Makes sure not to suggest wrapping if it doesn't have tuple field constructor (i.e. has named fields) - 8f2bbb18fd53e5008bb488302dbd354577698ede: Also suggest wrapping expressions in a tuple struct (not just enum variants)
2 parents 3208ed7 + fc289a0 commit a0d2862

File tree

9 files changed

+243
-6
lines changed

9 files changed

+243
-6
lines changed

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

+69
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use rustc_hir::def_id::DefId;
6565
use rustc_hir::lang_items::LangItem;
6666
use rustc_hir::{Item, ItemKind, Node};
6767
use rustc_middle::dep_graph::DepContext;
68+
use rustc_middle::ty::print::with_no_trimmed_paths;
6869
use rustc_middle::ty::{
6970
self,
7071
error::TypeError,
@@ -1736,6 +1737,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17361737
};
17371738

17381739
if should_suggest_fixes {
1740+
self.suggest_tuple_pattern(cause, &exp_found, diag);
17391741
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
17401742
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
17411743
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
@@ -1766,6 +1768,73 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
17661768
self.note_error_origin(diag, cause, exp_found, terr);
17671769
}
17681770

1771+
fn suggest_tuple_pattern(
1772+
&self,
1773+
cause: &ObligationCause<'tcx>,
1774+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
1775+
diag: &mut Diagnostic,
1776+
) {
1777+
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
1778+
// some modifications due to that being in typeck and this being in infer.
1779+
if let ObligationCauseCode::Pattern { .. } = cause.code() {
1780+
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
1781+
let compatible_variants: Vec<_> = expected_adt
1782+
.variants()
1783+
.iter()
1784+
.filter(|variant| {
1785+
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
1786+
})
1787+
.filter_map(|variant| {
1788+
let sole_field = &variant.fields[0];
1789+
let sole_field_ty = sole_field.ty(self.tcx, substs);
1790+
if same_type_modulo_infer(sole_field_ty, exp_found.found) {
1791+
let variant_path =
1792+
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
1793+
// FIXME #56861: DRYer prelude filtering
1794+
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
1795+
if let Some((_, path)) = path.split_once("::") {
1796+
return Some(path.to_string());
1797+
}
1798+
}
1799+
Some(variant_path)
1800+
} else {
1801+
None
1802+
}
1803+
})
1804+
.collect();
1805+
match &compatible_variants[..] {
1806+
[] => {}
1807+
[variant] => {
1808+
diag.multipart_suggestion_verbose(
1809+
&format!("try wrapping the pattern in `{}`", variant),
1810+
vec![
1811+
(cause.span.shrink_to_lo(), format!("{}(", variant)),
1812+
(cause.span.shrink_to_hi(), ")".to_string()),
1813+
],
1814+
Applicability::MaybeIncorrect,
1815+
);
1816+
}
1817+
_ => {
1818+
// More than one matching variant.
1819+
diag.multipart_suggestions(
1820+
&format!(
1821+
"try wrapping the pattern in a variant of `{}`",
1822+
self.tcx.def_path_str(expected_adt.did())
1823+
),
1824+
compatible_variants.into_iter().map(|variant| {
1825+
vec![
1826+
(cause.span.shrink_to_lo(), format!("{}(", variant)),
1827+
(cause.span.shrink_to_hi(), ")".to_string()),
1828+
]
1829+
}),
1830+
Applicability::MaybeIncorrect,
1831+
);
1832+
}
1833+
}
1834+
}
1835+
}
1836+
}
1837+
17691838
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
17701839
if let ty::Opaque(def_id, substs) = ty.kind() {
17711840
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);

compiler/rustc_typeck/src/check/demand.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
268268
expr_ty: Ty<'tcx>,
269269
) {
270270
if let ty::Adt(expected_adt, substs) = expected.kind() {
271-
if !expected_adt.is_enum() {
272-
return;
273-
}
274-
275271
// If the expression is of type () and it's the return expression of a block,
276272
// we suggest adding a separate return expression instead.
277273
// (To avoid things like suggesting `Ok(while .. { .. })`.)
@@ -336,7 +332,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
336332
let compatible_variants: Vec<String> = expected_adt
337333
.variants()
338334
.iter()
339-
.filter(|variant| variant.fields.len() == 1)
335+
.filter(|variant| {
336+
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
337+
})
340338
.filter_map(|variant| {
341339
let sole_field = &variant.fields[0];
342340
let sole_field_ty = sole_field.ty(self.tcx, substs);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
enum Foo {
2+
Bar(Bar),
3+
}
4+
struct Bar {
5+
x: i32,
6+
}
7+
8+
fn a(f: Foo) {
9+
match f {
10+
Bar { x } => {
11+
//~^ ERROR mismatched types
12+
//~| HELP try wrapping
13+
}
14+
}
15+
}
16+
17+
struct S;
18+
19+
fn b(s: Option<S>) {
20+
match s {
21+
S => {
22+
//~^ ERROR mismatched types
23+
//~| HELP try wrapping
24+
//~| HELP introduce a new binding instead
25+
}
26+
_ => {}
27+
}
28+
}
29+
30+
fn c(s: Result<S, S>) {
31+
match s {
32+
S => {
33+
//~^ ERROR mismatched types
34+
//~| HELP try wrapping
35+
//~| HELP introduce a new binding instead
36+
}
37+
_ => {}
38+
}
39+
}
40+
41+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/compatible-variants-in-pat.rs:10:9
3+
|
4+
LL | match f {
5+
| - this expression has type `Foo`
6+
LL | Bar { x } => {
7+
| ^^^^^^^^^ expected enum `Foo`, found struct `Bar`
8+
|
9+
help: try wrapping the pattern in `Foo::Bar`
10+
|
11+
LL | Foo::Bar(Bar { x }) => {
12+
| +++++++++ +
13+
14+
error[E0308]: mismatched types
15+
--> $DIR/compatible-variants-in-pat.rs:21:9
16+
|
17+
LL | struct S;
18+
| --------- unit struct defined here
19+
...
20+
LL | match s {
21+
| - this expression has type `Option<S>`
22+
LL | S => {
23+
| ^
24+
| |
25+
| expected enum `Option`, found struct `S`
26+
| `S` is interpreted as a unit struct, not a new binding
27+
|
28+
= note: expected enum `Option<S>`
29+
found struct `S`
30+
help: try wrapping the pattern in `Some`
31+
|
32+
LL | Some(S) => {
33+
| +++++ +
34+
help: introduce a new binding instead
35+
|
36+
LL | other_s => {
37+
| ~~~~~~~
38+
39+
error[E0308]: mismatched types
40+
--> $DIR/compatible-variants-in-pat.rs:32:9
41+
|
42+
LL | struct S;
43+
| --------- unit struct defined here
44+
...
45+
LL | match s {
46+
| - this expression has type `Result<S, S>`
47+
LL | S => {
48+
| ^
49+
| |
50+
| expected enum `Result`, found struct `S`
51+
| `S` is interpreted as a unit struct, not a new binding
52+
|
53+
= note: expected enum `Result<S, S>`
54+
found struct `S`
55+
help: try wrapping the pattern in a variant of `Result`
56+
|
57+
LL | Ok(S) => {
58+
| +++ +
59+
LL | Err(S) => {
60+
| ++++ +
61+
help: introduce a new binding instead
62+
|
63+
LL | other_s => {
64+
| ~~~~~~~
65+
66+
error: aborting due to 3 previous errors
67+
68+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/did_you_mean/compatible-variants.rs

+24
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,27 @@ fn main() {
6464
//~^ ERROR mismatched types
6565
//~| HELP try wrapping
6666
}
67+
68+
enum A {
69+
B { b: B},
70+
}
71+
72+
struct A2(B);
73+
74+
enum B {
75+
Fst,
76+
Snd,
77+
}
78+
79+
fn foo() {
80+
// We don't want to suggest `A::B(B::Fst)` here.
81+
let a: A = B::Fst;
82+
//~^ ERROR mismatched types
83+
}
84+
85+
fn bar() {
86+
// But we _do_ want to suggest `A2(B::Fst)` here!
87+
let a: A2 = B::Fst;
88+
//~^ ERROR mismatched types
89+
//~| HELP try wrapping
90+
}

src/test/ui/did_you_mean/compatible-variants.stderr

+22-1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,27 @@ help: try wrapping the expression in `Some`
190190
LL | let _ = Foo { bar: Some(bar) };
191191
| ++++++++++ +
192192

193-
error: aborting due to 11 previous errors
193+
error[E0308]: mismatched types
194+
--> $DIR/compatible-variants.rs:81:16
195+
|
196+
LL | let a: A = B::Fst;
197+
| - ^^^^^^ expected enum `A`, found enum `B`
198+
| |
199+
| expected due to this
200+
201+
error[E0308]: mismatched types
202+
--> $DIR/compatible-variants.rs:87:17
203+
|
204+
LL | let a: A2 = B::Fst;
205+
| -- ^^^^^^ expected struct `A2`, found enum `B`
206+
| |
207+
| expected due to this
208+
|
209+
help: try wrapping the expression in `A2`
210+
|
211+
LL | let a: A2 = A2(B::Fst);
212+
| +++ +
213+
214+
error: aborting due to 13 previous errors
194215

195216
For more information about this error, try `rustc --explain E0308`.

src/test/ui/issues/issue-12552.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | Some(k) => match k {
88
|
99
= note: expected enum `Result<_, {integer}>`
1010
found enum `Option<_>`
11+
help: try wrapping the pattern in `Ok`
12+
|
13+
LL | Ok(Some(k)) => match k {
14+
| +++ +
1115

1216
error[E0308]: mismatched types
1317
--> $DIR/issue-12552.rs:9:5
@@ -20,6 +24,10 @@ LL | None => ()
2024
|
2125
= note: expected enum `Result<_, {integer}>`
2226
found enum `Option<_>`
27+
help: try wrapping the pattern in `Ok`
28+
|
29+
LL | Ok(None) => ()
30+
| +++ +
2331

2432
error: aborting due to 2 previous errors
2533

src/test/ui/issues/issue-3680.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | Err(_) => ()
88
|
99
= note: expected enum `Option<_>`
1010
found enum `Result<_, _>`
11+
help: try wrapping the pattern in `Some`
12+
|
13+
LL | Some(Err(_)) => ()
14+
| +++++ +
1115

1216
error: aborting due to previous error
1317

src/test/ui/issues/issue-5358-1.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | Either::Right(_) => {}
88
|
99
= note: expected struct `S`
1010
found enum `Either<_, _>`
11+
help: try wrapping the pattern in `S`
12+
|
13+
LL | S(Either::Right(_)) => {}
14+
| ++ +
1115
help: you might have meant to use field `0` whose type is `Either<usize, usize>`
1216
|
1317
LL | match S(Either::Left(5)).0 {

0 commit comments

Comments
 (0)