| use rustc_hir::def::DefKind; | 
 | use rustc_hir::{Expr, ExprKind}; | 
 | use rustc_middle::ty; | 
 | use rustc_middle::ty::adjustment::Adjust; | 
 | use rustc_session::{declare_lint, declare_lint_pass}; | 
 | use rustc_span::sym; | 
 |  | 
 | use crate::context::LintContext; | 
 | use crate::lints::{ | 
 |     NoopMethodCallDiag, SuspiciousDoubleRefCloneDiag, SuspiciousDoubleRefDerefDiag, | 
 | }; | 
 | use crate::{LateContext, LateLintPass}; | 
 |  | 
 | declare_lint! { | 
 |     /// The `noop_method_call` lint detects specific calls to noop methods | 
 |     /// such as a calling `<&T as Clone>::clone` where `T: !Clone`. | 
 |     /// | 
 |     /// ### Example | 
 |     /// | 
 |     /// ```rust | 
 |     /// # #![allow(unused)] | 
 |     /// struct Foo; | 
 |     /// let foo = &Foo; | 
 |     /// let clone: &Foo = foo.clone(); | 
 |     /// ``` | 
 |     /// | 
 |     /// {{produces}} | 
 |     /// | 
 |     /// ### Explanation | 
 |     /// | 
 |     /// Some method calls are noops meaning that they do nothing. Usually such methods | 
 |     /// are the result of blanket implementations that happen to create some method invocations | 
 |     /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but | 
 |     /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything | 
 |     /// as references are copy. This lint detects these calls and warns the user about them. | 
 |     pub NOOP_METHOD_CALL, | 
 |     Warn, | 
 |     "detects the use of well-known noop methods" | 
 | } | 
 |  | 
 | declare_lint! { | 
 |     /// The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()` | 
 |     /// on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`, | 
 |     /// instead of performing the operation on the underlying `T` and can be confusing. | 
 |     /// | 
 |     /// ### Example | 
 |     /// | 
 |     /// ```rust | 
 |     /// # #![allow(unused)] | 
 |     /// struct Foo; | 
 |     /// let foo = &&Foo; | 
 |     /// let clone: &Foo = foo.clone(); | 
 |     /// ``` | 
 |     /// | 
 |     /// {{produces}} | 
 |     /// | 
 |     /// ### Explanation | 
 |     /// | 
 |     /// Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double | 
 |     /// reference, instead of cloning the inner type which should be what was intended. | 
 |     pub SUSPICIOUS_DOUBLE_REF_OP, | 
 |     Warn, | 
 |     "suspicious call of trait method on `&&T`" | 
 | } | 
 |  | 
 | declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]); | 
 |  | 
 | impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { | 
 |     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | 
 |         // We only care about method calls. | 
 |         let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else { | 
 |             return; | 
 |         }; | 
 |  | 
 |         if call_span.from_expansion() { | 
 |             return; | 
 |         } | 
 |  | 
 |         // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` | 
 |         // traits and ignore any other method call. | 
 |  | 
 |         let Some((DefKind::AssocFn, did)) = cx.typeck_results().type_dependent_def(expr.hir_id) | 
 |         else { | 
 |             return; | 
 |         }; | 
 |  | 
 |         let Some(trait_id) = cx.tcx.trait_of_item(did) else { return }; | 
 |  | 
 |         let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return }; | 
 |  | 
 |         if !matches!(trait_, sym::Borrow | sym::Clone | sym::Deref) { | 
 |             return; | 
 |         }; | 
 |  | 
 |         let args = cx | 
 |             .tcx | 
 |             .normalize_erasing_regions(cx.typing_env(), cx.typeck_results().node_args(expr.hir_id)); | 
 |         // Resolve the trait method instance. | 
 |         let Ok(Some(i)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), did, args) else { | 
 |             return; | 
 |         }; | 
 |         // (Re)check that it implements the noop diagnostic. | 
 |         let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; | 
 |         if !matches!( | 
 |             name, | 
 |             sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref | 
 |         ) { | 
 |             return; | 
 |         } | 
 |  | 
 |         let receiver_ty = cx.typeck_results().expr_ty(receiver); | 
 |         let expr_ty = cx.typeck_results().expr_ty_adjusted(expr); | 
 |         let arg_adjustments = cx.typeck_results().expr_adjustments(receiver); | 
 |  | 
 |         // If there is any user defined auto-deref step, then we don't want to warn. | 
 |         // https://github.com/rust-lang/rust-clippy/issues/9272 | 
 |         if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { | 
 |             return; | 
 |         } | 
 |  | 
 |         let expr_span = expr.span; | 
 |         let span = expr_span.with_lo(receiver.span.hi()); | 
 |  | 
 |         let orig_ty = expr_ty.peel_refs(); | 
 |  | 
 |         if receiver_ty == expr_ty { | 
 |             let suggest_derive = match orig_ty.kind() { | 
 |                 ty::Adt(def, _) => Some(cx.tcx.def_span(def.did()).shrink_to_lo()), | 
 |                 _ => None, | 
 |             }; | 
 |             cx.emit_span_lint( | 
 |                 NOOP_METHOD_CALL, | 
 |                 span, | 
 |                 NoopMethodCallDiag { | 
 |                     method: call.ident, | 
 |                     orig_ty, | 
 |                     trait_, | 
 |                     label: span, | 
 |                     suggest_derive, | 
 |                 }, | 
 |             ); | 
 |         } else { | 
 |             match name { | 
 |                 // If `type_of(x) == T` and `x.borrow()` is used to get `&T`, | 
 |                 // then that should be allowed | 
 |                 sym::noop_method_borrow => return, | 
 |                 sym::noop_method_clone => cx.emit_span_lint( | 
 |                     SUSPICIOUS_DOUBLE_REF_OP, | 
 |                     span, | 
 |                     SuspiciousDoubleRefCloneDiag { ty: expr_ty }, | 
 |                 ), | 
 |                 sym::noop_method_deref => cx.emit_span_lint( | 
 |                     SUSPICIOUS_DOUBLE_REF_OP, | 
 |                     span, | 
 |                     SuspiciousDoubleRefDerefDiag { ty: expr_ty }, | 
 |                 ), | 
 |                 _ => return, | 
 |             } | 
 |         } | 
 |     } | 
 | } |