4 use rustc::hir::map::*;
5 use rustc::hir::intravisit::FnKind;
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;
13 use crate::utils::{in_macro, is_copy, is_self, span_lint_and_sugg, snippet};
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
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
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.
28 /// The configuration option `trivial_copy_size_limit` can be set to override
29 /// this limit for a project.
34 /// assert_eq!(v, 42);
38 /// assert_eq!(v, 42);
41 declare_clippy_lint! {
42 pub TRIVIALLY_COPY_PASS_BY_REF,
44 "functions taking small copyable arguments by reference"
47 pub struct TriviallyCopyPassByRef {
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
66 impl LintPass for TriviallyCopyPassByRef {
67 fn get_lints(&self) -> LintArray {
68 lint_array![TRIVIALLY_COPY_PASS_BY_REF]
72 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
75 cx: &LateContext<'a, 'tcx>,
87 FnKind::ItemFn(.., header, _, attrs) => {
88 if header.abi != Abi::Rust {
92 if a.meta_item_list().is_some() && a.name() == "proc_macro_derive" {
97 FnKind::Method(..) => (),
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(_), _, _) |
110 let fn_def_id = cx.tcx.hir.local_def_id(node_id);
112 let fn_sig = cx.tcx.fn_sig(fn_def_id);
113 let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
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 {
122 if let TypeVariants::TyRef(_, ty, Mutability::MutImmutable) = ty.sty;
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;
128 let value_type = if is_self(arg) {
131 snippet(cx, decl_ty.span, "_").into()
135 TRIVIALLY_COPY_PASS_BY_REF,
137 "this argument is passed by reference, but would be more efficient if passed by value",
138 "consider passing by value instead",