]> git.lizzy.rs Git - rust.git/commitdiff
Suggest possible clone when we have &T
authoryukang <moorekang@gmail.com>
Tue, 3 Jan 2023 05:27:49 +0000 (13:27 +0800)
committeryukang <moorekang@gmail.com>
Fri, 6 Jan 2023 20:43:57 +0000 (04:43 +0800)
compiler/rustc_hir_typeck/src/demand.rs
compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.stderr [new file with mode: 0644]
src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.stderr [new file with mode: 0644]

index 0f191c21a0a63e483618da407f8696eff96c3944..52778747987d3098c2a9c979c81a8576a0f1b836 100644 (file)
@@ -57,6 +57,7 @@ pub fn emit_type_mismatch_suggestions(
             || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
             || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
             || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+            || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
             || self.suggest_into(err, expr, expr_ty, expected)
             || self.suggest_floating_point_literal(err, expr, expected);
         if !suggested {
index 066e98c74578fe047d2fcce200a3c1839158fdc7..991b0399542bf3037b19024fb9b7e37899c9fe48 100644 (file)
@@ -1014,6 +1014,35 @@ pub(crate) fn suggest_block_to_brackets_peeling_refs(
         }
     }
 
+    pub(crate) fn suggest_clone_for_ref(
+        &self,
+        diag: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expr_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> bool {
+        if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind() &&
+            let Some(clone_trait_def) = self.tcx.lang_items().clone_trait() &&
+                expected_ty == *inner_ty &&
+                self
+                .infcx
+                .type_implements_trait(
+                    clone_trait_def,
+                    [self.tcx.erase_regions(expected_ty)],
+                    self.param_env
+                )
+                .must_apply_modulo_regions() {
+                    diag.span_suggestion_verbose(
+                        expr.span.shrink_to_hi(),
+                        "consider using clone here",
+                        ".clone()",
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                }
+        false
+    }
+
     pub(crate) fn suggest_copied_or_cloned(
         &self,
         diag: &mut Diagnostic,
index 17331a50105523c3a4712b51c09b2f3f1a8a7b76..9c098e1a2fc12b6beaee3ce595e38a714b367a41 100644 (file)
@@ -873,6 +873,11 @@ fn report_selection_error(
                             );
                         }
 
+                        if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) {
+                            err.emit();
+                            return;
+                        }
+
                         if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) {
                             err.emit();
                             return;
index 28f76b141469d53351069991cc60665db3c07ccf..d15196a9577aa63bf2552f2028171f5f50a3e100 100644 (file)
@@ -10,7 +10,7 @@
 use crate::traits::{NormalizeExt, ObligationCtxt};
 
 use hir::def::CtorOf;
-use hir::HirId;
+use hir::{Expr, HirId};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
@@ -206,11 +206,15 @@ fn check_for_binding_assigned_block_without_tail_expression(
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
 
-    fn suggest_add_reference_to_arg(
+    fn suggest_add_clone_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
+    );
+
+    fn suggest_add_reference_to_arg(
+        err: &mut Diagnostic,
         has_custom_message: bool,
     ) -> bool;
 
@@ -1102,6 +1106,70 @@ fn check_for_binding_assigned_block_without_tail_expression(
         }
     }
 
+    fn suggest_add_clone_to_arg(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diagnostic,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) -> bool {
+        let span = obligation.cause.span;
+        let body_id = obligation.cause.body_id;
+        let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+        let ty = self.tcx.erase_late_bound_regions(self_ty);
+        let owner = self.tcx.hir().get_parent_item(body_id);
+        if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() &&
+            let arg_node = self.tcx.hir().get(*arg_hir_id) &&
+            let Node::Expr(Expr { kind: hir::ExprKind::Path(_), ..}) = arg_node &&
+            let Some(generics) = self.tcx.hir().get_generics(owner.def_id) &&
+            let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() &&
+            let ty::Param(param) = inner_ty.kind() &&
+            let Some(generic_param) =
+                    generics.params.iter().find(|p| p.name.ident().as_str() == param.name.as_str())
+        {
+            let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
+            let has_clone = self
+                .type_implements_trait(clone_trait, [ty], obligation.param_env)
+                .must_apply_modulo_regions();
+
+            let trait_pred_and_suggested_ty =
+                trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty));
+            let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+                    obligation.param_env,
+                    trait_pred_and_suggested_ty,
+            );
+
+            if  has_clone && self.predicate_may_hold(&new_obligation) {
+                let clone_bound = generics.bounds_for_param(generic_param.def_id)
+                                                    .flat_map(|bp| bp.bounds)
+                                                    .any(|bound| {
+                    if let hir::GenericBound::Trait( hir::PolyTraitRef { trait_ref, ..}, ..) = bound {
+                            Some(clone_trait) == trait_ref.trait_def_id()
+                        } else {
+                            false
+                        }
+                    });
+                if !clone_bound {
+                    suggest_constraining_type_param(
+                        self.tcx,
+                        generics,
+                        err,
+                        param.name.as_str(),
+                        "Clone",
+                        Some(clone_trait)
+                    );
+                }
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "consider using clone here",
+                    ".clone()".to_string(),
+                    Applicability::MaybeIncorrect,
+                );
+                return true;
+            }
+        }
+        false
+    }
+
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.rs b/src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.rs
new file mode 100644 (file)
index 0000000..48efdb8
--- /dev/null
@@ -0,0 +1,23 @@
+#[derive(Clone)]
+struct S;
+
+// without Clone
+struct T;
+
+fn foo(_: S) {}
+
+fn test1() {
+    let s = &S;
+    foo(s); //~ ERROR mismatched types
+}
+
+fn bar(_: T) {}
+fn test2() {
+    let t = &T;
+    bar(t); //~ ERROR mismatched types
+}
+
+fn main() {
+    test1();
+    test2();
+}
diff --git a/src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.stderr b/src/test/ui/suggestions/issue-106443-sugg-clone-for-arg.stderr
new file mode 100644 (file)
index 0000000..1e66fe3
--- /dev/null
@@ -0,0 +1,35 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-106443-sugg-clone-for-arg.rs:11:9
+   |
+LL |     foo(s);
+   |     --- ^ expected struct `S`, found `&S`
+   |     |
+   |     arguments to this function are incorrect
+   |
+note: function defined here
+  --> $DIR/issue-106443-sugg-clone-for-arg.rs:7:4
+   |
+LL | fn foo(_: S) {}
+   |    ^^^ ----
+help: consider using clone here
+   |
+LL |     foo(s.clone());
+   |          ++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-106443-sugg-clone-for-arg.rs:17:9
+   |
+LL |     bar(t);
+   |     --- ^ expected struct `T`, found `&T`
+   |     |
+   |     arguments to this function are incorrect
+   |
+note: function defined here
+  --> $DIR/issue-106443-sugg-clone-for-arg.rs:14:4
+   |
+LL | fn bar(_: T) {}
+   |    ^^^ ----
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.rs b/src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.rs
new file mode 100644 (file)
index 0000000..4e4d4c0
--- /dev/null
@@ -0,0 +1,20 @@
+#[derive(Clone)]
+struct S;
+
+trait X {}
+
+impl X for S {}
+
+fn foo<T: X>(_: T) {}
+fn bar<T: X>(s: &T)  {
+    foo(s); //~ ERROR the trait bound `&T: X` is not satisfied
+}
+
+fn bar_with_clone<T: X + Clone>(s: &T)  {
+    foo(s); //~ ERROR the trait bound `&T: X` is not satisfied
+}
+
+fn main() {
+    let s = &S;
+    bar(s);
+}
diff --git a/src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.stderr b/src/test/ui/suggestions/issue-106443-sugg-clone-for-bound.stderr
new file mode 100644 (file)
index 0000000..22bafc6
--- /dev/null
@@ -0,0 +1,29 @@
+error[E0277]: the trait bound `&T: X` is not satisfied
+  --> $DIR/issue-106443-sugg-clone-for-bound.rs:10:9
+   |
+LL |     foo(s);
+   |         ^ the trait `X` is not implemented for `&T`
+   |
+help: consider further restricting this bound
+   |
+LL | fn bar<T: X + Clone>(s: &T)  {
+   |             +++++++
+help: consider using clone here
+   |
+LL |     foo(s.clone());
+   |          ++++++++
+
+error[E0277]: the trait bound `&T: X` is not satisfied
+  --> $DIR/issue-106443-sugg-clone-for-bound.rs:14:9
+   |
+LL |     foo(s);
+   |         ^ the trait `X` is not implemented for `&T`
+   |
+help: consider using clone here
+   |
+LL |     foo(s.clone());
+   |          ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.