Skip to content

Commit 7c4ec40

Browse files
committed
add missing_inline lint
When turned on, the lint warns on all exported functions, methods, trait methods (default impls, impls), that are not `#[inline]`. Closes #1503.
1 parent 30a9879 commit 7c4ec40

File tree

6 files changed

+288
-1
lines changed

6 files changed

+288
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ All notable changes to this project will be documented in this file.
744744
[`misaligned_transmute`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#misaligned_transmute
745745
[`misrefactored_assign_op`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#misrefactored_assign_op
746746
[`missing_docs_in_private_items`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
747+
[`missing_inline_in_public_items`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
747748
[`mixed_case_hex_literals`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
748749
[`module_inception`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#module_inception
749750
[`modulo_one`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#modulo_one

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in
99

1010
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
1111

12-
[There are 272 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
12+
[There are 273 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)
1313

1414
We have a bunch of lint categories to allow you to choose how much clippy is supposed to ~~annoy~~ help you:
1515

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ pub mod minmax;
134134
pub mod misc;
135135
pub mod misc_early;
136136
pub mod missing_doc;
137+
pub mod missing_inline;
137138
pub mod multiple_crate_versions;
138139
pub mod mut_mut;
139140
pub mod mut_reference;
@@ -364,6 +365,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
364365
reg.register_late_lint_pass(box let_if_seq::LetIfSeq);
365366
reg.register_late_lint_pass(box eval_order_dependence::EvalOrderDependence);
366367
reg.register_late_lint_pass(box missing_doc::MissingDoc::new());
368+
reg.register_late_lint_pass(box missing_inline::MissingInline::new());
367369
reg.register_late_lint_pass(box ok_if_let::Pass);
368370
reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass);
369371
reg.register_late_lint_pass(box partialeq_ne_impl::Pass);
@@ -422,6 +424,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
422424
methods::WRONG_PUB_SELF_CONVENTION,
423425
misc::FLOAT_CMP_CONST,
424426
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
427+
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
425428
panic_unimplemented::UNIMPLEMENTED,
426429
shadow::SHADOW_REUSE,
427430
shadow::SHADOW_SAME,

clippy_lints/src/missing_inline.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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+
}

tests/ui/missing_inline.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* This file incorporates work covered by the following copyright and
2+
* permission notice:
3+
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
4+
* file at the top-level directory of this distribution and at
5+
* http://rust-lang.org/COPYRIGHT.
6+
*
7+
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
8+
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
9+
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
10+
* option. This file may not be copied, modified, or distributed
11+
* except according to those terms.
12+
*/
13+
#![warn(missing_inline_in_public_items)]
14+
15+
// When denying at the crate level, be sure to not get random warnings from the
16+
// injected intrinsics by the compiler.
17+
#![allow(dead_code, non_snake_case)]
18+
19+
type Typedef = String;
20+
pub type PubTypedef = String;
21+
22+
struct Foo {} // ok
23+
pub struct PubFoo { } // ok
24+
enum FooE {} // ok
25+
pub enum PubFooE {} // ok
26+
27+
mod module {} // ok
28+
pub mod pub_module {} // ok
29+
30+
fn foo() {}
31+
pub fn pub_foo() {} // missing #[inline]
32+
#[inline] pub fn pub_foo_inline() {} // ok
33+
#[inline(always)] pub fn pub_foo_inline_always() {} // ok
34+
35+
#[allow(missing_inline_in_public_items)]
36+
pub fn pub_foo_no_inline() {}
37+
fn main() {}
38+
39+
trait Bar {
40+
fn Bar_a(); // ok
41+
fn Bar_b() {} // ok
42+
}
43+
44+
pub trait PubBar {
45+
fn PubBar_a(); // ok
46+
fn PubBar_b() {} // missing #[inline]
47+
#[inline] fn PubBar_c() {} // ok
48+
}
49+
50+
// none of these need inline because Foo is not exported
51+
impl PubBar for Foo {
52+
fn PubBar_a() {} // ok
53+
fn PubBar_b() {} // ok
54+
fn PubBar_c() {} // ok
55+
}
56+
57+
// all of these need inline because PubFoo is exported
58+
impl PubBar for PubFoo {
59+
fn PubBar_a() {} // missing #[inline]
60+
fn PubBar_b() {} // missing #[inline]
61+
fn PubBar_c() {} // missing #[inline]
62+
}
63+
64+
// do not need inline because Foo is not exported
65+
impl Foo {
66+
fn FooImpl() {} // ok
67+
}
68+
69+
// need inline because PubFoo is exported
70+
impl PubFoo {
71+
pub fn PubFooImpl() {} // missing #[inline]
72+
}

tests/ui/missing_inline.stderr

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: missing `#[inline]` for a function
2+
--> $DIR/missing_inline.rs:31:1
3+
|
4+
31 | pub fn pub_foo() {} // missing #[inline]
5+
| ^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D missing-inline-in-public-items` implied by `-D warnings`
8+
9+
error: missing `#[inline]` for a default trait method
10+
--> $DIR/missing_inline.rs:46:5
11+
|
12+
46 | fn PubBar_b() {} // missing #[inline]
13+
| ^^^^^^^^^^^^^^^^
14+
15+
error: missing `#[inline]` for a method
16+
--> $DIR/missing_inline.rs:59:5
17+
|
18+
59 | fn PubBar_a() {} // missing #[inline]
19+
| ^^^^^^^^^^^^^^^^
20+
21+
error: missing `#[inline]` for a method
22+
--> $DIR/missing_inline.rs:60:5
23+
|
24+
60 | fn PubBar_b() {} // missing #[inline]
25+
| ^^^^^^^^^^^^^^^^
26+
27+
error: missing `#[inline]` for a method
28+
--> $DIR/missing_inline.rs:61:5
29+
|
30+
61 | fn PubBar_c() {} // missing #[inline]
31+
| ^^^^^^^^^^^^^^^^
32+
33+
error: missing `#[inline]` for a method
34+
--> $DIR/missing_inline.rs:71:5
35+
|
36+
71 | pub fn PubFooImpl() {} // missing #[inline]
37+
| ^^^^^^^^^^^^^^^^^^^^^^
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)