Skip to content

Commit

Permalink
suggest wrapping patterns with compatible enum variants
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Mar 27, 2022
1 parent d7aca22 commit dd6683f
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 0 deletions.
69 changes: 69 additions & 0 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use rustc_hir::def_id::DefId;
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,
Expand Down Expand Up @@ -1736,6 +1737,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
};

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);
Expand Down Expand Up @@ -1766,6 +1768,73 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
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);
Expand Down
41 changes: 41 additions & 0 deletions src/test/ui/did_you_mean/compatible-variants-in-pat.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 68 additions & 0 deletions src/test/ui/did_you_mean/compatible-variants-in-pat.stderr
Original file line number Diff line number Diff line change
@@ -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`.
8 changes: 8 additions & 0 deletions src/test/ui/issues/issue-12552.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/issues/issue-3680.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/issues/issue-5358-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit dd6683f

Please sign in to comment.