1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use crate::context::LintContext;
4 use crate::lints::NoopMethodCallDiag;
5 use crate::rustc_middle::ty::TypeVisitable;
6 use crate::LateContext;
7 use crate::LateLintPass;
8 use rustc_hir::def::DefKind;
9 use rustc_hir::{Expr, ExprKind};
11 use rustc_span::symbol::sym;
14 /// The `noop_method_call` lint detects specific calls to noop methods
15 /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
20 /// # #![allow(unused)]
21 /// #![warn(noop_method_call)]
24 /// let clone: &Foo = foo.clone();
31 /// Some method calls are noops meaning that they do nothing. Usually such methods
32 /// are the result of blanket implementations that happen to create some method invocations
33 /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
34 /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
35 /// as references are copy. This lint detects these calls and warns the user about them.
38 "detects the use of well-known noop methods"
41 declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
43 impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
44 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
45 // We only care about method calls.
46 let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else {
49 // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
50 // traits and ignore any other method call.
51 let did = match cx.typeck_results().type_dependent_def(expr.hir_id) {
52 // Verify we are dealing with a method/associated function.
53 Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
54 // Check that we're dealing with a trait method for one of the traits we care about.
57 cx.tcx.get_diagnostic_name(trait_id),
58 Some(sym::Borrow | sym::Clone | sym::Deref)
69 .normalize_erasing_regions(cx.param_env, cx.typeck_results().node_substs(expr.hir_id));
70 // Resolve the trait method instance.
71 let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, substs) else {
74 // (Re)check that it implements the noop diagnostic.
75 let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
78 sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
82 let receiver_ty = cx.typeck_results().expr_ty(receiver);
83 let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
84 if receiver_ty != expr_ty {
85 // This lint will only trigger if the receiver type and resulting expression \
86 // type are the same, implying that the method call is unnecessary.
89 let expr_span = expr.span;
90 let span = expr_span.with_lo(receiver.span.hi());
94 NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },