]> git.lizzy.rs Git - rust.git/blob - src/mut_reference.rs
Merge pull request #921 from afck/master
[rust.git] / src / mut_reference.rs
1 use rustc::lint::*;
2 use rustc::ty::{TypeAndMut, TypeVariants, MethodCall, TyS};
3 use rustc::hir::*;
4 use syntax::ptr::P;
5 use utils::span_lint;
6
7 /// **What it does:** This lint detects giving a mutable reference to a function that only requires an immutable reference.
8 ///
9 /// **Why is this bad?** The immutable reference rules out all other references to the value. Also the code misleads about the intent of the call site.
10 ///
11 /// **Known problems:** None
12 ///
13 /// **Example** `my_vec.push(&mut value)`
14 declare_lint! {
15     pub UNNECESSARY_MUT_PASSED,
16     Warn,
17     "an argument is passed as a mutable reference although the function/method only demands an \
18      immutable reference"
19 }
20
21
22 #[derive(Copy,Clone)]
23 pub struct UnnecessaryMutPassed;
24
25 impl LintPass for UnnecessaryMutPassed {
26     fn get_lints(&self) -> LintArray {
27         lint_array!(UNNECESSARY_MUT_PASSED)
28     }
29 }
30
31 impl LateLintPass for UnnecessaryMutPassed {
32     fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
33         let borrowed_table = cx.tcx.tables.borrow();
34         match e.node {
35             ExprCall(ref fn_expr, ref arguments) => {
36                 let function_type = borrowed_table.node_types
37                                                   .get(&fn_expr.id)
38                                                   .expect("A function with an unknown type is called. \
39                                                            If this happened, the compiler would have \
40                                                            aborted the compilation long ago");
41                 if let ExprPath(_, ref path) = fn_expr.node {
42                     check_arguments(cx, arguments, function_type, &path.to_string());
43                 }
44             }
45             ExprMethodCall(ref name, _, ref arguments) => {
46                 let method_call = MethodCall::expr(e.id);
47                 let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
48                 check_arguments(cx, arguments, method_type.ty, &name.node.as_str())
49             }
50             _ => (),
51         }
52     }
53 }
54
55 fn check_arguments(cx: &LateContext, arguments: &[P<Expr>], type_definition: &TyS, name: &str) {
56     match type_definition.sty {
57         TypeVariants::TyFnDef(_, _, ref fn_type) |
58         TypeVariants::TyFnPtr(ref fn_type) => {
59             let parameters = &fn_type.sig.skip_binder().inputs;
60             for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
61                 match parameter.sty {
62                     TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |
63                     TypeVariants::TyRawPtr(TypeAndMut { mutbl: MutImmutable, .. }) => {
64                         if let ExprAddrOf(MutMutable, _) = argument.node {
65                             span_lint(cx,
66                                       UNNECESSARY_MUT_PASSED,
67                                       argument.span,
68                                       &format!("The function/method \"{}\" doesn't need a mutable reference", name));
69                         }
70                     }
71                     _ => (),
72                 }
73             }
74         }
75         _ => (),
76     }
77 }