1 use crate::context::LintContext;
2 use crate::rustc_middle::ty::TypeFoldable;
3 use crate::LateContext;
4 use crate::LateLintPass;
5 use rustc_hir::def::DefKind;
6 use rustc_hir::{Expr, ExprKind};
8 use rustc_span::symbol::sym;
11 /// The `noop_method_call` lint detects specific calls to noop methods
12 /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
17 /// # #![allow(unused)]
18 /// #![warn(noop_method_call)]
21 /// let clone: &Foo = foo.clone();
28 /// Some method calls are noops meaning that they do nothing. Usually such methods
29 /// are the result of blanket implementations that happen to create some method invocations
30 /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
31 /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
32 /// as references are copy. This lint detects these calls and warns the user about them.
35 "detects the use of well-known noop methods"
38 declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
40 impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
41 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
42 // We only care about method calls.
43 let (call, elements) = match expr.kind {
44 ExprKind::MethodCall(call, _, elements, _) => (call, elements),
47 // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
48 // traits and ignore any other method call.
49 let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
50 // Verify we are dealing with a method/associated function.
51 Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
52 // Check that we're dealing with a trait method for one of the traits we care about.
55 cx.tcx.get_diagnostic_name(trait_id),
56 Some(sym::Borrow | sym::Clone | sym::Deref)
65 let substs = cx.typeck_results().node_substs(expr.hir_id);
66 if substs.definitely_needs_subst(cx.tcx) {
67 // We can't resolve on types that require monomorphization, so we don't handle them if
68 // we need to perfom substitution.
71 let param_env = cx.tcx.param_env(trait_id);
72 // Resolve the trait method instance.
73 let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
77 // (Re)check that it implements the noop diagnostic.
78 for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
79 if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
80 let method = &call.ident.name;
81 let receiver = &elements[0];
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;
91 "the type `{:?}` which `{}` is being called on is the same as \
92 the type returned from `{}`, so the method call does not do \
93 anything and can be removed",
94 receiver_ty, method, method,
97 let span = expr_span.with_lo(receiver.span.hi());
98 cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
99 let method = &call.ident.name;
100 let message = format!(
101 "call to `.{}()` on a reference in this situation does nothing",
105 .span_label(span, "unnecessary method call")