about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-07-31 23:39:44 +0200
committerGitHub <noreply@github.com>2022-07-31 23:39:44 +0200
commit20a5e9fd7cdf3f9b3e60f1f4d52148b5ff45819e (patch)
tree7a857226acc4b887c04914a6693071fbcc7aa833
parente4fcee579ea33fb2b38ddb947850d5ee612e6479 (diff)
parent1c2ea78f29f6c24d210728ccee177d13d235a9df (diff)
downloadrust-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.rs52
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.rs5
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.stderr9
-rw-r--r--src/test/ui/did_you_mean/issue-42764.rs1
-rw-r--r--src/test/ui/did_you_mean/issue-42764.stderr5
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