]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/trivially_copy_pass_by_ref.rs
Merge pull request #2871 from flip1995/gen_gen_nightly
[rust.git] / clippy_lints / src / trivially_copy_pass_by_ref.rs
1 use std::cmp;
2
3 use rustc::hir::*;
4 use rustc::hir::map::*;
5 use rustc::hir::intravisit::FnKind;
6 use rustc::lint::*;
7 use rustc::ty::TypeVariants;
8 use rustc::session::config::Config as SessionConfig;
9 use rustc_target::spec::abi::Abi;
10 use rustc_target::abi::LayoutOf;
11 use syntax::ast::NodeId;
12 use syntax_pos::Span;
13 use crate::utils::{in_macro, is_copy, is_self, span_lint_and_sugg, snippet};
14
15 /// **What it does:** Checks for functions taking arguments by reference, where
16 /// the argument type is `Copy` and small enough to be more efficient to always
17 /// pass by value.
18 ///
19 /// **Why is this bad?** In many calling conventions instances of structs will
20 /// be passed through registers if they fit into two or less general purpose
21 /// registers.
22 ///
23 /// **Known problems:** This lint is target register size dependent, it is
24 /// limited to 32-bit to try and reduce portability problems between 32 and
25 /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
26 /// will be different.
27 ///
28 /// The configuration option `trivial_copy_size_limit` can be set to override
29 /// this limit for a project.
30 ///
31 /// **Example:**
32 /// ```rust
33 /// fn foo(v: &u32) {
34 ///     assert_eq!(v, 42);
35 /// }
36 /// // should be
37 /// fn foo(v: u32) {
38 ///     assert_eq!(v, 42);
39 /// }
40 /// ```
41 declare_clippy_lint! {
42     pub TRIVIALLY_COPY_PASS_BY_REF,
43     perf,
44     "functions taking small copyable arguments by reference"
45 }
46
47 pub struct TriviallyCopyPassByRef {
48     limit: u64,
49 }
50
51 impl TriviallyCopyPassByRef {
52     pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
53         let limit = limit.unwrap_or_else(|| {
54             let bit_width = target.usize_ty.bit_width().expect("usize should have a width") as u64;
55             // Cap the calculated bit width at 32-bits to reduce
56             // portability problems between 32 and 64-bit targets
57             let bit_width = cmp::min(bit_width, 32);
58             let byte_width = bit_width / 8;
59             // Use a limit of 2 times the register bit width
60             byte_width * 2
61         });
62         Self { limit }
63     }
64 }
65
66 impl LintPass for TriviallyCopyPassByRef {
67     fn get_lints(&self) -> LintArray {
68         lint_array![TRIVIALLY_COPY_PASS_BY_REF]
69     }
70 }
71
72 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
73     fn check_fn(
74         &mut self,
75         cx: &LateContext<'a, 'tcx>,
76         kind: FnKind<'tcx>,
77         decl: &'tcx FnDecl,
78         body: &'tcx Body,
79         span: Span,
80         node_id: NodeId,
81     ) {
82         if in_macro(span) {
83             return;
84         }
85
86         match kind {
87             FnKind::ItemFn(.., header, _, attrs) => {
88                 if header.abi != Abi::Rust {
89                     return;
90                 }
91                 for a in attrs {
92                     if a.meta_item_list().is_some() && a.name() == "proc_macro_derive" {
93                         return;
94                     }
95                 }
96             },
97             FnKind::Method(..) => (),
98             _ => return,
99         }
100
101         // Exclude non-inherent impls
102         if let Some(NodeItem(item)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(node_id)) {
103             if matches!(item.node, ItemImpl(_, _, _, _, Some(_), _, _) |
104                 ItemTrait(..))
105             {
106                 return;
107             }
108         }
109
110         let fn_def_id = cx.tcx.hir.local_def_id(node_id);
111
112         let fn_sig = cx.tcx.fn_sig(fn_def_id);
113         let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
114
115         for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {
116             // All spans generated from a proc-macro invocation are the same...
117             if span == input.span {
118                 return;
119             }
120
121             if_chain! {
122                 if let TypeVariants::TyRef(_, ty, Mutability::MutImmutable) = ty.sty;
123                 if is_copy(cx, ty);
124                 if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
125                 if size <= self.limit;
126                 if let Ty_::TyRptr(_, MutTy { ty: ref decl_ty, .. }) = input.node;
127                 then {
128                     let value_type = if is_self(arg) {
129                         "self".into()
130                     } else {
131                         snippet(cx, decl_ty.span, "_").into()
132                     };
133                     span_lint_and_sugg(
134                         cx,
135                         TRIVIALLY_COPY_PASS_BY_REF,
136                         input.span,
137                         "this argument is passed by reference, but would be more efficient if passed by value",
138                         "consider passing by value instead",
139                         value_type);
140                 }
141             }
142         }
143     }
144 }