diff options
| author | Michael Howell <michael@notriddle.com> | 2024-03-07 20:01:28 -0700 |
|---|---|---|
| committer | Michael Howell <michael@notriddle.com> | 2024-03-08 09:34:38 -0700 |
| commit | c2cc90402b6a896c3273a57e506d6c02a1ec6038 (patch) | |
| tree | a885090b424beba1b3193ece4f39e60d3717e6f8 /compiler/rustc_trait_selection/src | |
| parent | 9c3ad802d9b9633d60d3a74668eb1be819212d34 (diff) | |
| download | rust-c2cc90402b6a896c3273a57e506d6c02a1ec6038.tar.gz rust-c2cc90402b6a896c3273a57e506d6c02a1ec6038.zip | |
diagnostics: suggest `Clone` bounds when noop `clone()`
Diffstat (limited to 'compiler/rustc_trait_selection/src')
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 60 |
1 files changed, 50 insertions, 10 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 1241227a5af..0dc59a4578a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -987,10 +987,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { else { return false; }; - let arg_node = self.tcx.hir_node(*arg_hir_id); - let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { - return false; - }; let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None); let has_clone = |ty| { @@ -998,6 +994,39 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .must_apply_modulo_regions() }; + let existing_clone_call = match self.tcx.hir_node(*arg_hir_id) { + // It's just a variable. Propose cloning it. + Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) => None, + // It's already a call to `clone()`. We might be able to suggest + // adding a `+ Clone` bound, though. + Node::Expr(Expr { + kind: + hir::ExprKind::MethodCall( + hir::PathSegment { ident, .. }, + _receiver, + &[], + call_span, + ), + hir_id, + .. + }) if ident.name == sym::clone + && !call_span.from_expansion() + && !has_clone(*inner_ty) => + { + // We only care about method calls corresponding to the real `Clone` trait. + let Some(typeck_results) = self.typeck_results.as_ref() else { return false }; + let Some((DefKind::AssocFn, did)) = typeck_results.type_dependent_def(*hir_id) + else { + return false; + }; + if self.tcx.trait_of_item(did) != Some(clone_trait) { + return false; + } + Some(ident.span) + } + _ => return false, + }; + let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)), @@ -1015,12 +1044,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { None, ); } - err.span_suggestion_verbose( - obligation.cause.span.shrink_to_hi(), - "consider using clone here", - ".clone()".to_string(), - Applicability::MaybeIncorrect, - ); + if let Some(existing_clone_call) = existing_clone_call { + err.span_note( + existing_clone_call, + format!( + "this `clone()` copies the reference, \ + which does not do anything, \ + because `{inner_ty}` does not implement `Clone`" + ), + ); + } else { + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_hi(), + "consider using clone here", + ".clone()".to_string(), + Applicability::MaybeIncorrect, + ); + } return true; } false |
