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):
- 07776c111f
: 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)
This commit is contained in:
commit
a0d2862ca4
@ -65,6 +65,7 @@
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{Item, ItemKind, Node};
|
||||
use rustc_middle::dep_graph::DepContext;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
error::TypeError,
|
||||
@ -1736,6 +1737,7 @@ enum Mismatch<'a> {
|
||||
};
|
||||
|
||||
if should_suggest_fixes {
|
||||
self.suggest_tuple_pattern(cause, &exp_found, diag);
|
||||
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
|
||||
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
|
||||
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
|
||||
@ -1766,6 +1768,73 @@ enum Mismatch<'a> {
|
||||
self.note_error_origin(diag, cause, exp_found, terr);
|
||||
}
|
||||
|
||||
fn suggest_tuple_pattern(
|
||||
&self,
|
||||
cause: &ObligationCause<'tcx>,
|
||||
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
|
||||
// some modifications due to that being in typeck and this being in infer.
|
||||
if let ObligationCauseCode::Pattern { .. } = cause.code() {
|
||||
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
|
||||
let compatible_variants: Vec<_> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.fields[0];
|
||||
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
||||
if same_type_modulo_infer(sole_field_ty, exp_found.found) {
|
||||
let variant_path =
|
||||
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
|
||||
// FIXME #56861: DRYer prelude filtering
|
||||
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
|
||||
if let Some((_, path)) = path.split_once("::") {
|
||||
return Some(path.to_string());
|
||||
}
|
||||
}
|
||||
Some(variant_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
match &compatible_variants[..] {
|
||||
[] => {}
|
||||
[variant] => {
|
||||
diag.multipart_suggestion_verbose(
|
||||
&format!("try wrapping the pattern in `{}`", variant),
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
// More than one matching variant.
|
||||
diag.multipart_suggestions(
|
||||
&format!(
|
||||
"try wrapping the pattern in a variant of `{}`",
|
||||
self.tcx.def_path_str(expected_adt.did())
|
||||
),
|
||||
compatible_variants.into_iter().map(|variant| {
|
||||
vec![
|
||||
(cause.span.shrink_to_lo(), format!("{}(", variant)),
|
||||
(cause.span.shrink_to_hi(), ")".to_string()),
|
||||
]
|
||||
}),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
|
||||
if let ty::Opaque(def_id, substs) = ty.kind() {
|
||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||
|
@ -268,10 +268,6 @@ fn suggest_compatible_variants(
|
||||
expr_ty: Ty<'tcx>,
|
||||
) {
|
||||
if let ty::Adt(expected_adt, substs) = expected.kind() {
|
||||
if !expected_adt.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the expression is of type () and it's the return expression of a block,
|
||||
// we suggest adding a separate return expression instead.
|
||||
// (To avoid things like suggesting `Ok(while .. { .. })`.)
|
||||
@ -336,7 +332,9 @@ fn suggest_compatible_variants(
|
||||
let compatible_variants: Vec<String> = expected_adt
|
||||
.variants()
|
||||
.iter()
|
||||
.filter(|variant| variant.fields.len() == 1)
|
||||
.filter(|variant| {
|
||||
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
|
||||
})
|
||||
.filter_map(|variant| {
|
||||
let sole_field = &variant.fields[0];
|
||||
let sole_field_ty = sole_field.ty(self.tcx, substs);
|
||||
|
41
src/test/ui/did_you_mean/compatible-variants-in-pat.rs
Normal file
41
src/test/ui/did_you_mean/compatible-variants-in-pat.rs
Normal file
@ -0,0 +1,41 @@
|
||||
enum Foo {
|
||||
Bar(Bar),
|
||||
}
|
||||
struct Bar {
|
||||
x: i32,
|
||||
}
|
||||
|
||||
fn a(f: Foo) {
|
||||
match f {
|
||||
Bar { x } => {
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
fn b(s: Option<S>) {
|
||||
match s {
|
||||
S => {
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
//~| HELP introduce a new binding instead
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn c(s: Result<S, S>) {
|
||||
match s {
|
||||
S => {
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
//~| HELP introduce a new binding instead
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
68
src/test/ui/did_you_mean/compatible-variants-in-pat.stderr
Normal file
68
src/test/ui/did_you_mean/compatible-variants-in-pat.stderr
Normal file
@ -0,0 +1,68 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants-in-pat.rs:10:9
|
||||
|
|
||||
LL | match f {
|
||||
| - this expression has type `Foo`
|
||||
LL | Bar { x } => {
|
||||
| ^^^^^^^^^ expected enum `Foo`, found struct `Bar`
|
||||
|
|
||||
help: try wrapping the pattern in `Foo::Bar`
|
||||
|
|
||||
LL | Foo::Bar(Bar { x }) => {
|
||||
| +++++++++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants-in-pat.rs:21:9
|
||||
|
|
||||
LL | struct S;
|
||||
| --------- unit struct defined here
|
||||
...
|
||||
LL | match s {
|
||||
| - this expression has type `Option<S>`
|
||||
LL | S => {
|
||||
| ^
|
||||
| |
|
||||
| expected enum `Option`, found struct `S`
|
||||
| `S` is interpreted as a unit struct, not a new binding
|
||||
|
|
||||
= note: expected enum `Option<S>`
|
||||
found struct `S`
|
||||
help: try wrapping the pattern in `Some`
|
||||
|
|
||||
LL | Some(S) => {
|
||||
| +++++ +
|
||||
help: introduce a new binding instead
|
||||
|
|
||||
LL | other_s => {
|
||||
| ~~~~~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants-in-pat.rs:32:9
|
||||
|
|
||||
LL | struct S;
|
||||
| --------- unit struct defined here
|
||||
...
|
||||
LL | match s {
|
||||
| - this expression has type `Result<S, S>`
|
||||
LL | S => {
|
||||
| ^
|
||||
| |
|
||||
| expected enum `Result`, found struct `S`
|
||||
| `S` is interpreted as a unit struct, not a new binding
|
||||
|
|
||||
= note: expected enum `Result<S, S>`
|
||||
found struct `S`
|
||||
help: try wrapping the pattern in a variant of `Result`
|
||||
|
|
||||
LL | Ok(S) => {
|
||||
| +++ +
|
||||
LL | Err(S) => {
|
||||
| ++++ +
|
||||
help: introduce a new binding instead
|
||||
|
|
||||
LL | other_s => {
|
||||
| ~~~~~~~
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -64,3 +64,27 @@ fn main() {
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
}
|
||||
|
||||
enum A {
|
||||
B { b: B},
|
||||
}
|
||||
|
||||
struct A2(B);
|
||||
|
||||
enum B {
|
||||
Fst,
|
||||
Snd,
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
// We don't want to suggest `A::B(B::Fst)` here.
|
||||
let a: A = B::Fst;
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
// But we _do_ want to suggest `A2(B::Fst)` here!
|
||||
let a: A2 = B::Fst;
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP try wrapping
|
||||
}
|
||||
|
@ -190,6 +190,27 @@ help: try wrapping the expression in `Some`
|
||||
LL | let _ = Foo { bar: Some(bar) };
|
||||
| ++++++++++ +
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:81:16
|
||||
|
|
||||
LL | let a: A = B::Fst;
|
||||
| - ^^^^^^ expected enum `A`, found enum `B`
|
||||
| |
|
||||
| expected due to this
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/compatible-variants.rs:87:17
|
||||
|
|
||||
LL | let a: A2 = B::Fst;
|
||||
| -- ^^^^^^ expected struct `A2`, found enum `B`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
help: try wrapping the expression in `A2`
|
||||
|
|
||||
LL | let a: A2 = A2(B::Fst);
|
||||
| +++ +
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
@ -8,6 +8,10 @@ LL | Some(k) => match k {
|
||||
|
|
||||
= note: expected enum `Result<_, {integer}>`
|
||||
found enum `Option<_>`
|
||||
help: try wrapping the pattern in `Ok`
|
||||
|
|
||||
LL | Ok(Some(k)) => match k {
|
||||
| +++ +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-12552.rs:9:5
|
||||
@ -20,6 +24,10 @@ LL | None => ()
|
||||
|
|
||||
= note: expected enum `Result<_, {integer}>`
|
||||
found enum `Option<_>`
|
||||
help: try wrapping the pattern in `Ok`
|
||||
|
|
||||
LL | Ok(None) => ()
|
||||
| +++ +
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -8,6 +8,10 @@ LL | Err(_) => ()
|
||||
|
|
||||
= note: expected enum `Option<_>`
|
||||
found enum `Result<_, _>`
|
||||
help: try wrapping the pattern in `Some`
|
||||
|
|
||||
LL | Some(Err(_)) => ()
|
||||
| +++++ +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -8,6 +8,10 @@ LL | Either::Right(_) => {}
|
||||
|
|
||||
= note: expected struct `S`
|
||||
found enum `Either<_, _>`
|
||||
help: try wrapping the pattern in `S`
|
||||
|
|
||||
LL | S(Either::Right(_)) => {}
|
||||
| ++ +
|
||||
help: you might have meant to use field `0` whose type is `Either<usize, usize>`
|
||||
|
|
||||
LL | match S(Either::Left(5)).0 {
|
||||
|
Loading…
Reference in New Issue
Block a user