diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-07-31 23:39:44 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-31 23:39:44 +0200 |
| commit | 20a5e9fd7cdf3f9b3e60f1f4d52148b5ff45819e (patch) | |
| tree | 7a857226acc4b887c04914a6693071fbcc7aa833 | |
| parent | e4fcee579ea33fb2b38ddb947850d5ee612e6479 (diff) | |
| parent | 1c2ea78f29f6c24d210728ccee177d13d235a9df (diff) | |
| download | rust-20a5e9fd7cdf3f9b3e60f1f4d52148b5ff45819e.tar.gz rust-20a5e9fd7cdf3f9b3e60f1f4d52148b5ff45819e.zip | |
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`
| -rw-r--r-- | compiler/rustc_typeck/src/check/demand.rs | 52 | ||||
| -rw-r--r-- | src/test/ui/did_you_mean/compatible-variants.rs | 5 | ||||
| -rw-r--r-- | src/test/ui/did_you_mean/compatible-variants.stderr | 9 | ||||
| -rw-r--r-- | src/test/ui/did_you_mean/issue-42764.rs | 1 | ||||
| -rw-r--r-- | src/test/ui/did_you_mean/issue-42764.stderr | 5 |
5 files changed, 48 insertions, 24 deletions
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 58b0399c5c9..f0110645551 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -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, ); } diff --git a/src/test/ui/did_you_mean/compatible-variants.rs b/src/test/ui/did_you_mean/compatible-variants.rs index 5d7c611980f..b1c7dc2a7f6 100644 --- a/src/test/ui/did_you_mean/compatible-variants.rs +++ b/src/test/ui/did_you_mean/compatible-variants.rs @@ -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 diff --git a/src/test/ui/did_you_mean/compatible-variants.stderr b/src/test/ui/did_you_mean/compatible-variants.stderr index a16cdee4462..fe81da19833 100644 --- a/src/test/ui/did_you_mean/compatible-variants.stderr +++ b/src/test/ui/did_you_mean/compatible-variants.stderr @@ -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` diff --git a/src/test/ui/did_you_mean/issue-42764.rs b/src/test/ui/did_you_mean/issue-42764.rs index 6da640b2b7c..eb96c248063 100644 --- a/src/test/ui/did_you_mean/issue-42764.rs +++ b/src/test/ui/did_you_mean/issue-42764.rs @@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper } fn overton() { let _c = Context { wrapper: Payload{} }; //~^ ERROR mismatched types + //~| try wrapping the expression in `Wrapper` } diff --git a/src/test/ui/did_you_mean/issue-42764.stderr b/src/test/ui/did_you_mean/issue-42764.stderr index 95b572133c5..6a7fd8fe251 100644 --- a/src/test/ui/did_you_mean/issue-42764.stderr +++ b/src/test/ui/did_you_mean/issue-42764.stderr @@ -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 |
