]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint; |
f20569fa XL |
2 | use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; |
3 | use rustc_lint::{LateContext, LateLintPass}; | |
f20569fa | 4 | use rustc_middle::ty::{self, Ty}; |
4b012472 | 5 | use rustc_session::declare_lint_pass; |
cdc7bbd5 | 6 | use std::iter; |
f20569fa XL |
7 | |
8 | declare_clippy_lint! { | |
94222f64 XL |
9 | /// ### What it does |
10 | /// Detects passing a mutable reference to a function that only | |
f20569fa XL |
11 | /// requires an immutable reference. |
12 | /// | |
94222f64 XL |
13 | /// ### Why is this bad? |
14 | /// The mutable reference rules out all other references to | |
f20569fa XL |
15 | /// the value. Also the code misleads about the intent of the call site. |
16 | /// | |
94222f64 | 17 | /// ### Example |
ed00b5ec | 18 | /// ```no_run |
923072b8 FG |
19 | /// # let mut vec = Vec::new(); |
20 | /// # let mut value = 5; | |
21 | /// vec.push(&mut value); | |
22 | /// ``` | |
f20569fa | 23 | /// |
923072b8 | 24 | /// Use instead: |
ed00b5ec | 25 | /// ```no_run |
923072b8 FG |
26 | /// # let mut vec = Vec::new(); |
27 | /// # let value = 5; | |
28 | /// vec.push(&value); | |
f20569fa | 29 | /// ``` |
a2a8927a | 30 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
31 | pub UNNECESSARY_MUT_PASSED, |
32 | style, | |
33 | "an argument passed as a mutable reference although the callee only demands an immutable reference" | |
34 | } | |
35 | ||
36 | declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); | |
37 | ||
38 | impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { | |
39 | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | |
add651ee FG |
40 | if e.span.from_expansion() { |
41 | // Issue #11268 | |
42 | return; | |
43 | } | |
44 | ||
f20569fa | 45 | match e.kind { |
cdc7bbd5 | 46 | ExprKind::Call(fn_expr, arguments) => { |
f20569fa XL |
47 | if let ExprKind::Path(ref path) = fn_expr.kind { |
48 | check_arguments( | |
49 | cx, | |
f2b60f7d | 50 | arguments.iter().collect(), |
f20569fa | 51 | cx.typeck_results().expr_ty(fn_expr), |
ed00b5ec | 52 | &rustc_hir_pretty::qpath_to_string(path), |
f20569fa XL |
53 | "function", |
54 | ); | |
55 | } | |
56 | }, | |
f2b60f7d | 57 | ExprKind::MethodCall(path, receiver, arguments, _) => { |
f20569fa | 58 | let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); |
add651ee FG |
59 | let args = cx.typeck_results().node_args(e.hir_id); |
60 | let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args); | |
f2b60f7d FG |
61 | check_arguments( |
62 | cx, | |
63 | std::iter::once(receiver).chain(arguments.iter()).collect(), | |
64 | method_type, | |
65 | path.ident.as_str(), | |
66 | "method", | |
67 | ); | |
f20569fa XL |
68 | }, |
69 | _ => (), | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
74 | fn check_arguments<'tcx>( | |
75 | cx: &LateContext<'tcx>, | |
f2b60f7d | 76 | arguments: Vec<&Expr<'_>>, |
f20569fa XL |
77 | type_definition: Ty<'tcx>, |
78 | name: &str, | |
79 | fn_kind: &str, | |
80 | ) { | |
81 | match type_definition.kind() { | |
82 | ty::FnDef(..) | ty::FnPtr(_) => { | |
83 | let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); | |
cdc7bbd5 | 84 | for (argument, parameter) in iter::zip(arguments, parameters) { |
f20569fa XL |
85 | match parameter.kind() { |
86 | ty::Ref(_, _, Mutability::Not) | |
87 | | ty::RawPtr(ty::TypeAndMut { | |
88 | mutbl: Mutability::Not, .. | |
89 | }) => { | |
90 | if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind { | |
91 | span_lint( | |
92 | cx, | |
93 | UNNECESSARY_MUT_PASSED, | |
94 | argument.span, | |
2b03887a | 95 | &format!("the {fn_kind} `{name}` doesn't need a mutable reference"), |
f20569fa XL |
96 | ); |
97 | } | |
98 | }, | |
99 | _ => (), | |
100 | } | |
101 | } | |
102 | }, | |
103 | _ => (), | |
104 | } | |
105 | } |