about summary refs log tree commit diff
path: root/compiler/rustc_lint/src/noop_method_call.rs
blob: b9b5009d9dd95e9a52dd87860952065ee4e51d47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use crate::context::LintContext;
use crate::rustc_middle::ty::TypeFoldable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;

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_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);

impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // We only care about method calls
        if let ExprKind::MethodCall(..) = expr.kind {
            // Get the `DefId` only when dealing with an `AssocFn`
            if let Some((DefKind::AssocFn, did)) =
                cx.typeck_results().type_dependent_def(expr.hir_id)
            {
                // Check that we're dealing with a trait method
                if let Some(trait_id) = cx.tcx.trait_of_item(did) {
                    // Check we're dealing with one of the traits we care about
                    if ![sym::Clone, sym::Deref, sym::Borrow]
                        .iter()
                        .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id))
                    {
                        return;
                    }

                    let substs = cx.typeck_results().node_substs(expr.hir_id);
                    // We can't resolve on types that recursively require monomorphization,
                    // so check that we don't need to perfom substitution
                    if !substs.needs_subst() {
                        let param_env = cx.tcx.param_env(trait_id);
                        // Resolve the trait method instance
                        if let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) {
                            // Check that it implements the noop diagnostic
                            if [
                                sym::noop_method_borrow,
                                sym::noop_method_clone,
                                sym::noop_method_deref,
                            ]
                            .iter()
                            .any(|s| cx.tcx.is_diagnostic_item(*s, i.def_id()))
                            {
                                let expr_span = expr.span;

                                cx.struct_span_lint(NOOP_METHOD_CALL, expr_span, |lint| {
                                    let message = "call to method that does nothing";
                                    lint.build(&message)
                                        .span_label(expr_span, "unnecessary method call")
                                        .emit()
                                });
                            }
                        }
                    }
                }
            }
        }
    }
}