Skip to content

Commit

Permalink
Rollup merge of #99986 - WaffleLapkin:record_struct_wrap_suggestion, …
Browse files Browse the repository at this point in the history
…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`
  • Loading branch information
matthiaskrgr authored Jul 31, 2022
2 parents e4fcee5 + 1c2ea78 commit 20a5e9f
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 24 deletions.
52 changes: 33 additions & 19 deletions compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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,
);
}
Expand All @@ -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,
);
}
Expand Down
5 changes: 2 additions & 3 deletions src/test/ui/did_you_mean/compatible-variants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn main() {
}

enum A {
B { b: B},
B { b: B },
}

struct A2(B);
Expand All @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/test/ui/did_you_mean/compatible-variants.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
1 change: 1 addition & 0 deletions src/test/ui/did_you_mean/issue-42764.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper }
fn overton() {
let _c = Context { wrapper: Payload{} };
//~^ ERROR mismatched types
//~| try wrapping the expression in `Wrapper`
}
5 changes: 5 additions & 0 deletions src/test/ui/did_you_mean/issue-42764.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 20a5e9f

Please sign in to comment.