]> git.lizzy.rs Git - rust.git/commitdiff
Suggest accessing field when code compiles with it
authorHirochika Matsumoto <matsujika@gmail.com>
Thu, 28 Jan 2021 20:43:35 +0000 (05:43 +0900)
committerHirochika Matsumoto <matsujika@gmail.com>
Thu, 28 Jan 2021 21:52:49 +0000 (06:52 +0900)
compiler/rustc_infer/src/infer/error_reporting/mod.rs
src/test/ui/suggestions/field-access.rs [new file with mode: 0644]
src/test/ui/suggestions/field-access.stderr [new file with mode: 0644]

index c39daea0811e021bbfc800c4d3fbbd5962fff837..68ffe3cd70fa81ac384a570f3d62910c32b4e589 100644 (file)
@@ -1661,6 +1661,7 @@ enum Mismatch<'a> {
         debug!("exp_found {:?} terr {:?}", exp_found, terr);
         if let Some(exp_found) = exp_found {
             self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+            self.suggest_field_where_appropriate(cause, &exp_found, diag);
             self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
         }
 
@@ -1819,6 +1820,46 @@ fn suggest_await_on_expect_found(
         }
     }
 
+    fn suggest_field_where_appropriate(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut DiagnosticBuilder<'tcx>,
+    ) {
+        debug!("suggest_field_where_appropriate(cause={:?}, exp_found={:?})", cause, exp_found);
+        if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
+            if expected_def.is_enum() {
+                return;
+            }
+
+            if let Some((name, ty)) = expected_def
+                .non_enum_variant()
+                .fields
+                .iter()
+                .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
+                .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs)))
+                .inspect(|(name, ty)| {
+                    debug!("suggest_field_where_appropriate: name={:?}, ty={:?}", name, ty)
+                })
+                .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found))
+            {
+                if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                        diag.span_suggestion(
+                            span,
+                            &format!(
+                                "you might have meant to use field `{}` of type `{}`",
+                                name, ty
+                            ),
+                            format!("{}.{}", snippet, name),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
     /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
     /// suggests it.
     fn suggest_as_ref_where_appropriate(
diff --git a/src/test/ui/suggestions/field-access.rs b/src/test/ui/suggestions/field-access.rs
new file mode 100644 (file)
index 0000000..822f66f
--- /dev/null
@@ -0,0 +1,15 @@
+struct A {
+    b: B,
+}
+
+enum B {
+    Fst,
+    Snd,
+}
+
+fn main() {
+    let a = A { b: B::Fst };
+    if let B::Fst = a {};
+    //~^ ERROR mismatched types [E0308]
+    // note: you might have meant to use field `b` of type `B`
+}
diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr
new file mode 100644 (file)
index 0000000..58bc6d3
--- /dev/null
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/field-access.rs:12:12
+   |
+LL |     Fst,
+   |     --- unit variant defined here
+...
+LL |     if let B::Fst = a {};
+   |            ^^^^^^   - this expression has type `A`
+   |            |
+   |            expected struct `A`, found enum `B`
+   |
+help: you might have meant to use field `b` of type `B`
+   |
+LL |     if let B::Fst = a.b {};
+   |                     ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.