Skip to content

Commit 6c70013

Browse files
authored
Merge pull request #2895 from gnzlbg/lint_no_inline
Add missing_inline lint
2 parents 30a9879 + 3fec3b4 commit 6c70013

File tree

6 files changed

+315
-1
lines changed

6 files changed

+315
-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);
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: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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+
///
30+
/// **Example:**
31+
/// ```rust
32+
/// pub fn foo() {} // missing #[inline]
33+
/// fn ok() {} // ok
34+
/// #[inline] pub fn bar() {} // ok
35+
/// #[inline(always)] pub fn baz() {} // ok
36+
///
37+
/// pub trait Bar {
38+
/// fn bar(); // ok
39+
/// fn def_bar() {} // missing #[inline]
40+
/// }
41+
///
42+
/// struct Baz;
43+
/// impl Baz {
44+
/// fn priv() {} // ok
45+
/// }
46+
///
47+
/// impl Bar for Baz {
48+
/// fn bar() {} // ok - Baz is not exported
49+
/// }
50+
///
51+
/// pub struct PubBaz;
52+
/// impl PubBaz {
53+
/// fn priv() {} // ok
54+
/// pub not_ptriv() {} // missing #[inline]
55+
/// }
56+
///
57+
/// impl Bar for PubBaz {
58+
/// fn bar() {} // missing #[inline]
59+
/// fn def_bar() {} // missing #[inline]
60+
/// }
61+
/// ```
62+
declare_clippy_lint! {
63+
pub MISSING_INLINE_IN_PUBLIC_ITEMS,
64+
restriction,
65+
"detects missing #[inline] attribute for public callables (functions, trait methods, methods...)"
66+
}
67+
68+
pub struct MissingInline;
69+
70+
fn check_missing_inline_attrs(cx: &LateContext,
71+
attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
72+
let has_inline = attrs
73+
.iter()
74+
.any(|a| a.name() == "inline" );
75+
if !has_inline {
76+
cx.span_lint(
77+
MISSING_INLINE_IN_PUBLIC_ITEMS,
78+
sp,
79+
&format!("missing `#[inline]` for {}", desc),
80+
);
81+
}
82+
}
83+
84+
fn is_executable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>) -> bool {
85+
use rustc::session::config::CrateType;
86+
87+
cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| {
88+
match t {
89+
CrateType::CrateTypeExecutable => true,
90+
_ => false,
91+
}
92+
})
93+
}
94+
95+
impl LintPass for MissingInline {
96+
fn get_lints(&self) -> LintArray {
97+
lint_array![MISSING_INLINE_IN_PUBLIC_ITEMS]
98+
}
99+
}
100+
101+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
102+
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
103+
if is_executable(cx) {
104+
return;
105+
}
106+
107+
if !cx.access_levels.is_exported(it.id) {
108+
return;
109+
}
110+
match it.node {
111+
hir::ItemFn(..) => {
112+
let desc = "a function";
113+
check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
114+
},
115+
hir::ItemTrait(ref _is_auto, ref _unsafe, ref _generics,
116+
ref _bounds, ref trait_items) => {
117+
// note: we need to check if the trait is exported so we can't use
118+
// `LateLintPass::check_trait_item` here.
119+
for tit in trait_items {
120+
let tit_ = cx.tcx.hir.trait_item(tit.id);
121+
match tit_.node {
122+
hir::TraitItemKind::Const(..) |
123+
hir::TraitItemKind::Type(..) => {},
124+
hir::TraitItemKind::Method(..) => {
125+
if tit.defaultness.has_value() {
126+
// trait method with default body needs inline in case
127+
// an impl is not provided
128+
let desc = "a default trait method";
129+
let item = cx.tcx.hir.expect_trait_item(tit.id.node_id);
130+
check_missing_inline_attrs(cx, &item.attrs,
131+
item.span, desc);
132+
}
133+
},
134+
}
135+
}
136+
}
137+
hir::ItemConst(..) |
138+
hir::ItemEnum(..) |
139+
hir::ItemMod(..) |
140+
hir::ItemStatic(..) |
141+
hir::ItemStruct(..) |
142+
hir::ItemTraitAlias(..) |
143+
hir::ItemGlobalAsm(..) |
144+
hir::ItemTy(..) |
145+
hir::ItemUnion(..) |
146+
hir::ItemExistential(..) |
147+
hir::ItemExternCrate(..) |
148+
hir::ItemForeignMod(..) |
149+
hir::ItemImpl(..) |
150+
hir::ItemUse(..) => {},
151+
};
152+
}
153+
154+
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
155+
use rustc::ty::{TraitContainer, ImplContainer};
156+
if is_executable(cx) {
157+
return;
158+
}
159+
160+
// If the item being implemented is not exported, then we don't need #[inline]
161+
if !cx.access_levels.is_exported(impl_item.id) {
162+
return;
163+
}
164+
165+
let desc = match impl_item.node {
166+
hir::ImplItemKind::Method(..) => "a method",
167+
hir::ImplItemKind::Const(..) |
168+
hir::ImplItemKind::Type(_) => return,
169+
};
170+
171+
let def_id = cx.tcx.hir.local_def_id(impl_item.id);
172+
match cx.tcx.associated_item(def_id).container {
173+
TraitContainer(cid) => {
174+
if let Some(n) = cx.tcx.hir.as_local_node_id(cid) {
175+
if !cx.access_levels.is_exported(n) {
176+
// If a trait is being implemented for an item, and the
177+
// trait is not exported, we don't need #[inline]
178+
return;
179+
}
180+
}
181+
},
182+
ImplContainer(cid) => {
183+
if cx.tcx.impl_trait_ref(cid).is_some() {
184+
let trait_ref = cx.tcx.impl_trait_ref(cid).unwrap();
185+
if let Some(n) = cx.tcx.hir.as_local_node_id(trait_ref.def_id) {
186+
if !cx.access_levels.is_exported(n) {
187+
// If a trait is being implemented for an item, and the
188+
// trait is not exported, we don't need #[inline]
189+
return;
190+
}
191+
}
192+
}
193+
},
194+
}
195+
196+
check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
197+
}
198+
}

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+
#![crate_type = "dylib"]
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+
38+
trait Bar {
39+
fn Bar_a(); // ok
40+
fn Bar_b() {} // ok
41+
}
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)