Rollup merge of #99986 - WaffleLapkin:record_struct_wrap_suggestion, r=compiler-errors

Add wrap suggestions for record variants

This PR adds a suggestions to wrap an expression in a record struct/variant when encountering mismatched types, similarly to a suggestion to wrap expression in a tuple struct that was added before.

An example:
```rust
struct B {
    f: u8,
}

enum E {
    A(u32),
    B { f: u8 },
}

fn main() {
    let _: B = 1;
    let _: E = 1;
}
```
```text
error[E0308]: mismatched types
  --> ./t.rs:11:16
   |
11 |     let _: B = 1;
   |            -   ^ expected struct `B`, found integer
   |            |
   |            expected due to this
   |
help: try wrapping the expression in `B`
   |
11 |     let _: B = B { f: 1 };
   |                ++++++   +

error[E0308]: mismatched types
  --> ./t.rs:12:16
   |
12 |     let _: E = 1;
   |            -   ^ expected enum `E`, found integer
   |            |
   |            expected due to this
   |
help: try wrapping the expression in a variant of `E`
   |
12 |     let _: E = E::A(1);
   |                +++++ +
12 |     let _: E = E::B { f: 1 };
   |                +++++++++   +
```

r? `@compiler-errors`
This commit is contained in:
Matthias Krüger 2022-07-31 23:39:44 +02:00 committed by GitHub
commit 20a5e9fd7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 24 deletions

View File

@ -1,5 +1,6 @@
use crate::check::FnCtxt;
use rustc_infer::infer::InferOk;
use rustc_middle::middle::stability::EvalResult;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::ObligationCause;
@ -363,18 +364,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
let compatible_variants: Vec<(String, Option<String>)> = expected_adt
let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
.variants()
.iter()
.filter(|variant| {
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
variant.fields.len() == 1
})
.filter_map(|variant| {
let sole_field = &variant.fields[0];
let field_is_local = sole_field.did.is_local();
let field_is_accessible =
sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx);
sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
// Skip suggestions for unstable public fields (for example `Pin::pointer`)
&& matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
if !field_is_local && !field_is_accessible {
return None;
@ -391,33 +394,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(path) = variant_path.strip_prefix("std::prelude::")
&& let Some((_, path)) = path.split_once("::")
{
return Some((path.to_string(), note_about_variant_field_privacy));
return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy));
}
Some((variant_path, note_about_variant_field_privacy))
Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy))
} else {
None
}
})
.collect();
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
Some(ident) => format!("{ident}: "),
None => String::new(),
let suggestions_for = |variant: &_, ctor, field_name| {
let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
Some(ident) => format!("{ident}: "),
None => String::new(),
};
let (open, close) = match ctor {
hir::def::CtorKind::Fn => ("(".to_owned(), ")"),
hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"),
// unit variants don't have fields
hir::def::CtorKind::Const => unreachable!(),
};
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
(expr.span.shrink_to_hi(), close.to_owned()),
]
};
match &compatible_variants[..] {
[] => { /* No variants to format */ }
[(variant, note)] => {
[(variant, ctor_kind, field_name, note)] => {
// Just a single matching variant.
err.multipart_suggestion_verbose(
&format!(
"try wrapping the expression in `{variant}`{note}",
note = note.as_deref().unwrap_or("")
),
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
(expr.span.shrink_to_hi(), ")".to_string()),
],
suggestions_for(&**variant, *ctor_kind, *field_name),
Applicability::MaybeIncorrect,
);
}
@ -428,12 +443,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"try wrapping the expression in a variant of `{}`",
self.tcx.def_path_str(expected_adt.did())
),
compatible_variants.into_iter().map(|(variant, _)| {
vec![
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
(expr.span.shrink_to_hi(), ")".to_string()),
]
}),
compatible_variants.into_iter().map(
|(variant, ctor_kind, field_name, _)| {
suggestions_for(&variant, ctor_kind, field_name)
},
),
Applicability::MaybeIncorrect,
);
}

View File

@ -66,7 +66,7 @@ fn main() {
}
enum A {
B { b: B},
B { b: B },
}
struct A2(B);
@ -77,13 +77,12 @@ enum B {
}
fn foo() {
// We don't want to suggest `A::B(B::Fst)` here.
let a: A = B::Fst;
//~^ ERROR mismatched types
//~| HELP try wrapping
}
fn bar() {
// But we _do_ want to suggest `A2(B::Fst)` here!
let a: A2 = B::Fst;
//~^ ERROR mismatched types
//~| HELP try wrapping

View File

@ -191,15 +191,20 @@ LL | let _ = Foo { bar: Some(bar) };
| ++++++++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:81:16
--> $DIR/compatible-variants.rs:80:16
|
LL | let a: A = B::Fst;
| - ^^^^^^ expected enum `A`, found enum `B`
| |
| expected due to this
|
help: try wrapping the expression in `A::B`
|
LL | let a: A = A::B { b: B::Fst };
| +++++++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:87:17
--> $DIR/compatible-variants.rs:86:17
|
LL | let a: A2 = B::Fst;
| -- ^^^^^^ expected struct `A2`, found enum `B`

View File

@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper }
fn overton() {
let _c = Context { wrapper: Payload{} };
//~^ ERROR mismatched types
//~| try wrapping the expression in `Wrapper`
}

View File

@ -25,6 +25,11 @@ error[E0308]: mismatched types
|
LL | let _c = Context { wrapper: Payload{} };
| ^^^^^^^^^ expected struct `Wrapper`, found struct `Payload`
|
help: try wrapping the expression in `Wrapper`
|
LL | let _c = Context { wrapper: Wrapper { payload: Payload{} } };
| ++++++++++++++++++ +
error: aborting due to 2 previous errors