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
use rustc::lint::*;
use rustc::ty::{TypeAndMut, TypeVariants, MethodCall, TyS};
use rustc::hir::*;
use utils::span_lint;

/// **What it does:** Detects giving a mutable reference to a function that only
/// requires an immutable reference.
///
/// **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.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// my_vec.push(&mut value)
/// ```
declare_lint! {
    pub UNNECESSARY_MUT_PASSED,
    Warn,
    "an argument passed as a mutable reference although the callee only demands an \
     immutable reference"
}


#[derive(Copy,Clone)]
pub struct UnnecessaryMutPassed;

impl LintPass for UnnecessaryMutPassed {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNNECESSARY_MUT_PASSED)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        let borrowed_table = cx.tcx.tables.borrow();
        match e.node {
            ExprCall(ref fn_expr, ref arguments) => {
                let function_type = borrowed_table.node_types
                    .get(&fn_expr.id)
                    .expect("A function with an unknown type is called. If this happened, the compiler would have \
                             aborted the compilation long ago");
                if let ExprPath(ref path) = fn_expr.node {
                    check_arguments(cx,
                                    arguments,
                                    function_type,
                                    &print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
                }
            },
            ExprMethodCall(ref name, _, ref arguments) => {
                let method_call = MethodCall::expr(e.id);
                let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
                check_arguments(cx, arguments, method_type.ty, &name.node.as_str())
            },
            _ => (),
        }
    }
}

fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS, name: &str) {
    match type_definition.sty {
        TypeVariants::TyFnDef(_, _, fn_type) |
        TypeVariants::TyFnPtr(fn_type) => {
            let parameters = fn_type.sig.skip_binder().inputs();
            for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
                match parameter.sty {
                    TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |
                    TypeVariants::TyRawPtr(TypeAndMut { mutbl: MutImmutable, .. }) => {
                        if let ExprAddrOf(MutMutable, _) = argument.node {
                            span_lint(cx,
                                      UNNECESSARY_MUT_PASSED,
                                      argument.span,
                                      &format!("The function/method \"{}\" doesn't need a mutable reference", name));
                        }
                    },
                    _ => (),
                }
            }
        },
        _ => (),
    }
}