]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/noop_method_call.rs
Add 'compiler/rustc_codegen_gcc/' from commit 'afae271d5d3719eeb92c18bc004bb6d1965a5f3f'
[rust.git] / compiler / rustc_lint / src / noop_method_call.rs
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};
7 use rustc_middle::ty;
8 use rustc_span::symbol::sym;
9
10 declare_lint! {
11     /// The `noop_method_call` lint detects specific calls to noop methods
12     /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
13     ///
14     /// ### Example
15     ///
16     /// ```rust
17     /// # #![allow(unused)]
18     /// #![warn(noop_method_call)]
19     /// struct Foo;
20     /// let foo = &Foo;
21     /// let clone: &Foo = foo.clone();
22     /// ```
23     ///
24     /// {{produces}}
25     ///
26     /// ### Explanation
27     ///
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.
33     pub NOOP_METHOD_CALL,
34     Allow,
35     "detects the use of well-known noop methods"
36 }
37
38 declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
39
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),
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 [sym::Clone, sym::Deref, sym::Borrow]
55                         .iter()
56                         .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) =>
57                 {
58                     (trait_id, did)
59                 }
60                 _ => return,
61             },
62             _ => return,
63         };
64         let substs = cx.typeck_results().node_substs(expr.hir_id);
65         if substs.needs_subst() {
66             // We can't resolve on types that require monomorphization, so we don't handle them if
67             // we need to perfom substitution.
68             return;
69         }
70         let param_env = cx.tcx.param_env(trait_id);
71         // Resolve the trait method instance.
72         let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
73             Ok(Some(i)) => i,
74             _ => return,
75         };
76         // (Re)check that it implements the noop diagnostic.
77         for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
78             if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
79                 let method = &call.ident.name;
80                 let receiver = &elements[0];
81                 let receiver_ty = cx.typeck_results().expr_ty(receiver);
82                 let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
83                 if receiver_ty != expr_ty {
84                     // This lint will only trigger if the receiver type and resulting expression \
85                     // type are the same, implying that the method call is unnecessary.
86                     return;
87                 }
88                 let expr_span = expr.span;
89                 let note = format!(
90                     "the type `{:?}` which `{}` is being called on is the same as \
91                      the type returned from `{}`, so the method call does not do \
92                      anything and can be removed",
93                     receiver_ty, method, method,
94                 );
95
96                 let span = expr_span.with_lo(receiver.span.hi());
97                 cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
98                     let method = &call.ident.name;
99                     let message = format!(
100                         "call to `.{}()` on a reference in this situation does nothing",
101                         &method,
102                     );
103                     lint.build(&message)
104                         .span_label(span, "unnecessary method call")
105                         .note(&note)
106                         .emit()
107                 });
108             }
109         }
110     }
111 }