]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/noop_method_call.rs
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
[rust.git] / compiler / rustc_lint / src / noop_method_call.rs
1 use crate::context::LintContext;
2 use crate::rustc_middle::ty::TypeVisitable;
3 use crate::LateContext;
4 use crate::LateLintPass;
5 use rustc_errors::fluent;
6 use rustc_hir::def::DefKind;
7 use rustc_hir::{Expr, ExprKind};
8 use rustc_middle::ty;
9 use rustc_span::symbol::sym;
10
11 declare_lint! {
12     /// The `noop_method_call` lint detects specific calls to noop methods
13     /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
14     ///
15     /// ### Example
16     ///
17     /// ```rust
18     /// # #![allow(unused)]
19     /// #![warn(noop_method_call)]
20     /// struct Foo;
21     /// let foo = &Foo;
22     /// let clone: &Foo = foo.clone();
23     /// ```
24     ///
25     /// {{produces}}
26     ///
27     /// ### Explanation
28     ///
29     /// Some method calls are noops meaning that they do nothing. Usually such methods
30     /// are the result of blanket implementations that happen to create some method invocations
31     /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
32     /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
33     /// as references are copy. This lint detects these calls and warns the user about them.
34     pub NOOP_METHOD_CALL,
35     Allow,
36     "detects the use of well-known noop methods"
37 }
38
39 declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
40
41 impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
42     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
43         // We only care about method calls.
44         let ExprKind::MethodCall(call, elements, _) = &expr.kind else {
45             return
46         };
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.
53                 Some(trait_id)
54                     if matches!(
55                         cx.tcx.get_diagnostic_name(trait_id),
56                         Some(sym::Borrow | sym::Clone | sym::Deref)
57                     ) =>
58                 {
59                     (trait_id, did)
60                 }
61                 _ => return,
62             },
63             _ => return,
64         };
65         let substs = cx.typeck_results().node_substs(expr.hir_id);
66         if substs.needs_subst() {
67             // We can't resolve on types that require monomorphization, so we don't handle them if
68             // we need to perform substitution.
69             return;
70         }
71         let param_env = cx.tcx.param_env(trait_id);
72         // Resolve the trait method instance.
73         let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else {
74             return
75         };
76         // (Re)check that it implements the noop diagnostic.
77         let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
78         if !matches!(
79             name,
80             sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
81         ) {
82             return;
83         }
84         let receiver = &elements[0];
85         let receiver_ty = cx.typeck_results().expr_ty(receiver);
86         let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
87         if receiver_ty != expr_ty {
88             // This lint will only trigger if the receiver type and resulting expression \
89             // type are the same, implying that the method call is unnecessary.
90             return;
91         }
92         let expr_span = expr.span;
93         let span = expr_span.with_lo(receiver.span.hi());
94         cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
95             lint.build(fluent::lint::noop_method_call)
96                 .set_arg("method", call.ident.name)
97                 .set_arg("receiver_ty", receiver_ty)
98                 .span_label(span, fluent::lint::label)
99                 .note(fluent::lint::note)
100                 .emit();
101         });
102     }
103 }