]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/mut_reference.rs
Merge branch 'macro-use' into HEAD
[rust.git] / clippy_lints / src / mut_reference.rs
1 use rustc::lint::*;
2 use rustc::{declare_lint, lint_array};
3 use rustc::ty::{self, Ty};
4 use rustc::ty::subst::Subst;
5 use rustc::hir::*;
6 use crate::utils::span_lint;
7
8 /// **What it does:** Detects giving a mutable reference to a function that only
9 /// requires an immutable reference.
10 ///
11 /// **Why is this bad?** The immutable reference rules out all other references
12 /// to the value. Also the code misleads about the intent of the call site.
13 ///
14 /// **Known problems:** None.
15 ///
16 /// **Example:**
17 /// ```rust
18 /// my_vec.push(&mut value)
19 /// ```
20 declare_clippy_lint! {
21     pub UNNECESSARY_MUT_PASSED,
22     style,
23     "an argument passed as a mutable reference although the callee only demands an \
24      immutable reference"
25 }
26
27
28 #[derive(Copy, Clone)]
29 pub struct UnnecessaryMutPassed;
30
31 impl LintPass for UnnecessaryMutPassed {
32     fn get_lints(&self) -> LintArray {
33         lint_array!(UNNECESSARY_MUT_PASSED)
34     }
35 }
36
37 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
38     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
39         match e.node {
40             ExprKind::Call(ref fn_expr, ref arguments) => if let ExprKind::Path(ref path) = fn_expr.node {
41                 check_arguments(
42                     cx,
43                     arguments,
44                     cx.tables.expr_ty(fn_expr),
45                     &print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
46                 );
47             },
48             ExprKind::MethodCall(ref path, _, ref arguments) => {
49                 let def_id = cx.tables.type_dependent_defs()[e.hir_id].def_id();
50                 let substs = cx.tables.node_substs(e.hir_id);
51                 let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
52                 check_arguments(cx, arguments, method_type, &path.ident.as_str())
53             },
54             _ => (),
55         }
56     }
57 }
58
59 fn check_arguments<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arguments: &[Expr], type_definition: Ty<'tcx>, name: &str) {
60     match type_definition.sty {
61         ty::TyFnDef(..) | ty::TyFnPtr(_) => {
62             let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
63             for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
64                 match parameter.sty {
65                     ty::TyRef(
66                         _,
67                         _,
68                         MutImmutable,
69                     ) |
70                     ty::TyRawPtr(ty::TypeAndMut {
71                         mutbl: MutImmutable,
72                         ..
73                     }) => if let ExprKind::AddrOf(MutMutable, _) = argument.node {
74                         span_lint(
75                             cx,
76                             UNNECESSARY_MUT_PASSED,
77                             argument.span,
78                             &format!("The function/method `{}` doesn't need a mutable reference", name),
79                         );
80                     },
81                     _ => (),
82                 }
83             }
84         },
85         _ => (),
86     }
87 }