]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/missing_inline.rs
add missing_inline lint
[rust.git] / clippy_lints / src / missing_inline.rs
1 //   Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 //   file at the top-level directory of this distribution and at
3 //   http://rust-lang.org/COPYRIGHT.
4 //
5 //   Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 //   http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 //   <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 //   option. This file may not be copied, modified, or distributed
9 //   except according to those terms.
10 //
11
12 use rustc::hir;
13 use rustc::lint::*;
14 use syntax::ast;
15 use syntax::codemap::Span;
16
17 /// **What it does:** it lints if an exported function, method, trait method with default impl,
18 /// or trait method impl is not `#[inline]`.
19 ///
20 /// **Why is this bad?** In general, it is not. Functions can be inlined across
21 /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
22 /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
23 /// might intend for most of the methods in their public API to be able to be inlined across
24 /// crates even when LTO is disabled. For these types of crates, enabling this lint might make sense.
25 /// It allows the crate to require all exported methods to be `#[inline]` by default, and then opt
26 /// out for specific methods where this might not make sense.
27 ///
28 /// **Known problems:** None.
29 declare_clippy_lint! {
30     pub MISSING_INLINE_IN_PUBLIC_ITEMS,
31     restriction,
32     "detects missing #[inline] attribute for public callables (functions, trait methods, methods...)"
33 }
34
35 pub struct MissingInline {}
36
37 impl ::std::default::Default for MissingInline {
38     fn default() -> Self {
39         Self::new()
40     }
41 }
42
43 impl MissingInline {
44     pub fn new() -> Self {
45         Self {}
46     }
47
48     fn check_missing_inline_attrs(&self, cx: &LateContext,
49                                   attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
50         // If we're building a test harness, FIXME: is this relevant?
51         // if cx.sess().opts.test {
52         //    return;
53         // }
54
55         let has_inline = attrs
56             .iter()
57             .any(|a| a.name() == "inline" );
58         if !has_inline {
59             cx.span_lint(
60                 MISSING_INLINE_IN_PUBLIC_ITEMS,
61                 sp,
62                 &format!("missing `#[inline]` for {}", desc),
63             );
64         }
65     }
66 }
67
68 impl LintPass for MissingInline {
69     fn get_lints(&self) -> LintArray {
70         lint_array![MISSING_INLINE_IN_PUBLIC_ITEMS]
71     }
72 }
73
74 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
75     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
76         if !cx.access_levels.is_exported(it.id) {
77             return;
78         }
79         match it.node {
80             hir::ItemFn(..) => {
81                 // ignore main()
82                 if it.name == "main" {
83                     let def_id = cx.tcx.hir.local_def_id(it.id);
84                     let def_key = cx.tcx.hir.def_key(def_id);
85                     if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
86                         return;
87                     }
88                 }
89                 let desc = "a function";
90                 self.check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
91             },
92             hir::ItemTrait(ref _is_auto, ref _unsafe, ref _generics,
93                            ref _bounds, ref trait_items)  => {
94                 for tit in trait_items {
95                     let tit_ = cx.tcx.hir.trait_item(tit.id);
96                     match tit_.node {
97                         hir::TraitItemKind::Const(..) |
98                         hir::TraitItemKind::Type(..) => {},
99                         hir::TraitItemKind::Method(..) => {
100                             if tit.defaultness.has_value() {
101                                 // trait method with default body needs inline in case
102                                 // an impl is not provided
103                                 let desc = "a default trait method";
104                                 let item = cx.tcx.hir.expect_trait_item(tit.id.node_id);
105                                 self.check_missing_inline_attrs(cx, &item.attrs,
106                                                                 item.span, desc);
107                             }
108                         },
109                     }
110                 }
111             }
112             hir::ItemConst(..) |
113             hir::ItemEnum(..) |
114             hir::ItemMod(..) |
115             hir::ItemStatic(..) |
116             hir::ItemStruct(..) |
117             hir::ItemTraitAlias(..) |
118             hir::ItemGlobalAsm(..) |
119             hir::ItemTy(..) |
120             hir::ItemUnion(..) |
121             hir::ItemExistential(..) |
122             hir::ItemExternCrate(..) |
123             hir::ItemForeignMod(..) |
124             hir::ItemImpl(..) |
125             hir::ItemUse(..) => {},
126         };
127     }
128
129     fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
130         use rustc::ty::{TraitContainer, ImplContainer};
131
132         // If the item being implemented is not exported, then we don't need #[inline]
133         if !cx.access_levels.is_exported(impl_item.id) {
134             return;
135         }
136
137         let def_id = cx.tcx.hir.local_def_id(impl_item.id);
138         match cx.tcx.associated_item(def_id).container {
139             TraitContainer(cid) => {
140                 let n = cx.tcx.hir.as_local_node_id(cid);
141                 if n.is_some() {
142                     if !cx.access_levels.is_exported(n.unwrap()) {
143                         // If a trait is being implemented for an item, and the
144                         // trait is not exported, we don't need #[inline]
145                         return;
146                     }
147                 }
148             },
149             ImplContainer(cid) => {
150                 if cx.tcx.impl_trait_ref(cid).is_some() {
151                     let trait_ref = cx.tcx.impl_trait_ref(cid).unwrap();
152                     let n = cx.tcx.hir.as_local_node_id(trait_ref.def_id);
153                     if n.is_some() {
154                         if !cx.access_levels.is_exported(n.unwrap()) {
155                             // If a trait is being implemented for an item, and the
156                             // trait is not exported, we don't need #[inline]
157                             return;
158                         }
159                     }
160                 }
161             },
162         }
163
164         let desc = match impl_item.node {
165             hir::ImplItemKind::Method(..) => "a method",
166             hir::ImplItemKind::Const(..) |
167             hir::ImplItemKind::Type(_) => return,
168         };
169         self.check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
170     }
171 }