about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-09-03 07:29:57 +0000
committerMichael Goulet <michael@errs.io>2022-09-03 07:30:30 +0000
commitfdbede7ad85d4ccdabc5da1cdaa41fa93fac2457 (patch)
tree9f51c23fef328c7d15132d5698618981bae86d4f
parent0209485578807b8084127f12d57771300edff87a (diff)
downloadrust-fdbede7ad85d4ccdabc5da1cdaa41fa93fac2457.tar.gz
rust-fdbede7ad85d4ccdabc5da1cdaa41fa93fac2457.zip
Suggest copied or cloned
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs1
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs64
-rw-r--r--src/test/ui/suggestions/copied-and-cloned.fixed23
-rw-r--r--src/test/ui/suggestions/copied-and-cloned.rs23
-rw-r--r--src/test/ui/suggestions/copied-and-cloned.stderr83
5 files changed, 194 insertions, 0 deletions
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index b9054898a2e..4a3d69f5b6c 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -42,6 +42,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
         self.suggest_missing_parentheses(err, expr);
         self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
+        self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
         self.note_type_is_not_clone(err, expected, expr_ty, expr);
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 939f4612d44..d8527b9267e 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -17,6 +17,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -925,6 +926,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    pub(crate) fn suggest_copied_or_cloned(
+        &self,
+        diag: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expr_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) {
+        let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
+        let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
+        if adt_def != expected_adt_def {
+            return;
+        }
+
+        let mut suggest_copied_or_cloned = || {
+            let expr_inner_ty = substs.type_at(0);
+            let expected_inner_ty = expected_substs.type_at(0);
+            if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
+                && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok()
+            {
+                let def_path = self.tcx.def_path_str(adt_def.did());
+                if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
+                    diag.span_suggestion_verbose(
+                        expr.span.shrink_to_hi(),
+                        format!(
+                            "use `{def_path}::copied` to copy the value inside the `{def_path}`"
+                        ),
+                        ".copied()",
+                        Applicability::MachineApplicable,
+                    );
+                } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
+                    && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
+                        self,
+                        self.param_env,
+                        *ty,
+                        clone_did,
+                        expr.span
+                    )
+                {
+                    diag.span_suggestion_verbose(
+                        expr.span.shrink_to_hi(),
+                        format!(
+                            "use `{def_path}::cloned` to clone the value inside the `{def_path}`"
+                        ),
+                        ".cloned()",
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+        };
+
+        if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
+            && adt_def.did() == result_did
+            // Check that the error types are equal
+            && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
+        {
+            suggest_copied_or_cloned();
+        } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
+            && adt_def.did() == option_did
+        {
+            suggest_copied_or_cloned();
+        }
+    }
+
     /// Suggest wrapping the block in square brackets instead of curly braces
     /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
     pub(crate) fn suggest_block_to_brackets(
diff --git a/src/test/ui/suggestions/copied-and-cloned.fixed b/src/test/ui/suggestions/copied-and-cloned.fixed
new file mode 100644
index 00000000000..f801403feec
--- /dev/null
+++ b/src/test/ui/suggestions/copied-and-cloned.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+
+fn expect<T>(_: T) {}
+
+fn main() {
+    let x = Some(&());
+    expect::<Option<()>>(x.copied());
+    //~^ ERROR mismatched types
+    //~| HELP use `Option::copied` to copy the value inside the `Option`
+    let x = Ok(&());
+    expect::<Result<(), ()>>(x.copied());
+    //~^ ERROR mismatched types
+    //~| HELP use `Result::copied` to copy the value inside the `Result`
+    let s = String::new();
+    let x = Some(&s);
+    expect::<Option<String>>(x.cloned());
+    //~^ ERROR mismatched types
+    //~| HELP use `Option::cloned` to clone the value inside the `Option`
+    let x = Ok(&s);
+    expect::<Result<String, ()>>(x.cloned());
+    //~^ ERROR mismatched types
+    //~| HELP use `Result::cloned` to clone the value inside the `Result`
+}
diff --git a/src/test/ui/suggestions/copied-and-cloned.rs b/src/test/ui/suggestions/copied-and-cloned.rs
new file mode 100644
index 00000000000..640450b7655
--- /dev/null
+++ b/src/test/ui/suggestions/copied-and-cloned.rs
@@ -0,0 +1,23 @@
+// run-rustfix
+
+fn expect<T>(_: T) {}
+
+fn main() {
+    let x = Some(&());
+    expect::<Option<()>>(x);
+    //~^ ERROR mismatched types
+    //~| HELP use `Option::copied` to copy the value inside the `Option`
+    let x = Ok(&());
+    expect::<Result<(), ()>>(x);
+    //~^ ERROR mismatched types
+    //~| HELP use `Result::copied` to copy the value inside the `Result`
+    let s = String::new();
+    let x = Some(&s);
+    expect::<Option<String>>(x);
+    //~^ ERROR mismatched types
+    //~| HELP use `Option::cloned` to clone the value inside the `Option`
+    let x = Ok(&s);
+    expect::<Result<String, ()>>(x);
+    //~^ ERROR mismatched types
+    //~| HELP use `Result::cloned` to clone the value inside the `Result`
+}
diff --git a/src/test/ui/suggestions/copied-and-cloned.stderr b/src/test/ui/suggestions/copied-and-cloned.stderr
new file mode 100644
index 00000000000..a6336281b40
--- /dev/null
+++ b/src/test/ui/suggestions/copied-and-cloned.stderr
@@ -0,0 +1,83 @@
+error[E0308]: mismatched types
+  --> $DIR/copied-and-cloned.rs:7:26
+   |
+LL |     expect::<Option<()>>(x);
+   |     -------------------- ^ expected `()`, found `&()`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Option<()>`
+              found enum `Option<&()>`
+note: function defined here
+  --> $DIR/copied-and-cloned.rs:3:4
+   |
+LL | fn expect<T>(_: T) {}
+   |    ^^^^^^    ----
+help: use `Option::copied` to copy the value inside the `Option`
+   |
+LL |     expect::<Option<()>>(x.copied());
+   |                           +++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/copied-and-cloned.rs:11:30
+   |
+LL |     expect::<Result<(), ()>>(x);
+   |     ------------------------ ^ expected `()`, found `&()`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Result<(), ()>`
+              found enum `Result<&(), _>`
+note: function defined here
+  --> $DIR/copied-and-cloned.rs:3:4
+   |
+LL | fn expect<T>(_: T) {}
+   |    ^^^^^^    ----
+help: use `Result::copied` to copy the value inside the `Result`
+   |
+LL |     expect::<Result<(), ()>>(x.copied());
+   |                               +++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/copied-and-cloned.rs:16:30
+   |
+LL |     expect::<Option<String>>(x);
+   |     ------------------------ ^ expected struct `String`, found `&String`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Option<String>`
+              found enum `Option<&String>`
+note: function defined here
+  --> $DIR/copied-and-cloned.rs:3:4
+   |
+LL | fn expect<T>(_: T) {}
+   |    ^^^^^^    ----
+help: use `Option::cloned` to clone the value inside the `Option`
+   |
+LL |     expect::<Option<String>>(x.cloned());
+   |                               +++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/copied-and-cloned.rs:20:34
+   |
+LL |     expect::<Result<String, ()>>(x);
+   |     ---------------------------- ^ expected struct `String`, found `&String`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Result<String, ()>`
+              found enum `Result<&String, _>`
+note: function defined here
+  --> $DIR/copied-and-cloned.rs:3:4
+   |
+LL | fn expect<T>(_: T) {}
+   |    ^^^^^^    ----
+help: use `Result::cloned` to clone the value inside the `Result`
+   |
+LL |     expect::<Result<String, ()>>(x.cloned());
+   |                                   +++++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.