-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add missing_inline lint #2895
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add missing_inline lint #2895
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
// | ||
|
||
use rustc::hir; | ||
use rustc::lint::*; | ||
use syntax::ast; | ||
use syntax::codemap::Span; | ||
|
||
/// **What it does:** it lints if an exported function, method, trait method with default impl, | ||
/// or trait method impl is not `#[inline]`. | ||
/// | ||
/// **Why is this bad?** In general, it is not. Functions can be inlined across | ||
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled, | ||
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates | ||
/// might intend for most of the methods in their public API to be able to be inlined across | ||
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make sense. | ||
/// It allows the crate to require all exported methods to be `#[inline]` by default, and then opt | ||
/// out for specific methods where this might not make sense. | ||
/// | ||
/// **Known problems:** None. | ||
/// | ||
/// **Example:** | ||
/// ```rust | ||
/// pub fn foo() {} // missing #[inline] | ||
/// fn ok() {} // ok | ||
/// #[inline] pub fn bar() {} // ok | ||
/// #[inline(always)] pub fn baz() {} // ok | ||
/// | ||
/// pub trait Bar { | ||
/// fn bar(); // ok | ||
/// fn def_bar() {} // missing #[inline] | ||
/// } | ||
/// | ||
/// struct Baz; | ||
/// impl Baz { | ||
/// fn priv() {} // ok | ||
/// } | ||
/// | ||
/// impl Bar for Baz { | ||
/// fn bar() {} // ok - Baz is not exported | ||
/// } | ||
/// | ||
/// pub struct PubBaz; | ||
/// impl PubBaz { | ||
/// fn priv() {} // ok | ||
/// pub not_ptriv() {} // missing #[inline] | ||
/// } | ||
/// | ||
/// impl Bar for PubBaz { | ||
/// fn bar() {} // missing #[inline] | ||
/// fn def_bar() {} // missing #[inline] | ||
/// } | ||
/// ``` | ||
declare_clippy_lint! { | ||
pub MISSING_INLINE_IN_PUBLIC_ITEMS, | ||
restriction, | ||
"detects missing #[inline] attribute for public callables (functions, trait methods, methods...)" | ||
} | ||
|
||
pub struct MissingInline; | ||
|
||
fn check_missing_inline_attrs(cx: &LateContext, | ||
attrs: &[ast::Attribute], sp: Span, desc: &'static str) { | ||
let has_inline = attrs | ||
.iter() | ||
.any(|a| a.name() == "inline" ); | ||
if !has_inline { | ||
cx.span_lint( | ||
MISSING_INLINE_IN_PUBLIC_ITEMS, | ||
sp, | ||
&format!("missing `#[inline]` for {}", desc), | ||
); | ||
} | ||
} | ||
|
||
fn is_executable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>) -> bool { | ||
use rustc::session::config::CrateType; | ||
|
||
cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| { | ||
match t { | ||
CrateType::CrateTypeExecutable => true, | ||
_ => false, | ||
} | ||
}) | ||
} | ||
|
||
impl LintPass for MissingInline { | ||
fn get_lints(&self) -> LintArray { | ||
lint_array![MISSING_INLINE_IN_PUBLIC_ITEMS] | ||
} | ||
} | ||
|
||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline { | ||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) { | ||
if is_executable(cx) { | ||
return; | ||
} | ||
|
||
if !cx.access_levels.is_exported(it.id) { | ||
return; | ||
} | ||
match it.node { | ||
hir::ItemFn(..) => { | ||
let desc = "a function"; | ||
check_missing_inline_attrs(cx, &it.attrs, it.span, desc); | ||
}, | ||
hir::ItemTrait(ref _is_auto, ref _unsafe, ref _generics, | ||
ref _bounds, ref trait_items) => { | ||
// note: we need to check if the trait is exported so we can't use | ||
// `LateLintPass::check_trait_item` here. | ||
for tit in trait_items { | ||
let tit_ = cx.tcx.hir.trait_item(tit.id); | ||
match tit_.node { | ||
hir::TraitItemKind::Const(..) | | ||
hir::TraitItemKind::Type(..) => {}, | ||
hir::TraitItemKind::Method(..) => { | ||
if tit.defaultness.has_value() { | ||
// trait method with default body needs inline in case | ||
// an impl is not provided | ||
let desc = "a default trait method"; | ||
let item = cx.tcx.hir.expect_trait_item(tit.id.node_id); | ||
check_missing_inline_attrs(cx, &item.attrs, | ||
item.span, desc); | ||
} | ||
}, | ||
} | ||
} | ||
} | ||
hir::ItemConst(..) | | ||
hir::ItemEnum(..) | | ||
hir::ItemMod(..) | | ||
hir::ItemStatic(..) | | ||
hir::ItemStruct(..) | | ||
hir::ItemTraitAlias(..) | | ||
hir::ItemGlobalAsm(..) | | ||
hir::ItemTy(..) | | ||
hir::ItemUnion(..) | | ||
hir::ItemExistential(..) | | ||
hir::ItemExternCrate(..) | | ||
hir::ItemForeignMod(..) | | ||
hir::ItemImpl(..) | | ||
hir::ItemUse(..) => {}, | ||
}; | ||
} | ||
|
||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) { | ||
use rustc::ty::{TraitContainer, ImplContainer}; | ||
if is_executable(cx) { | ||
return; | ||
} | ||
|
||
// If the item being implemented is not exported, then we don't need #[inline] | ||
if !cx.access_levels.is_exported(impl_item.id) { | ||
return; | ||
} | ||
|
||
let desc = match impl_item.node { | ||
hir::ImplItemKind::Method(..) => "a method", | ||
hir::ImplItemKind::Const(..) | | ||
hir::ImplItemKind::Type(_) => return, | ||
}; | ||
|
||
let def_id = cx.tcx.hir.local_def_id(impl_item.id); | ||
match cx.tcx.associated_item(def_id).container { | ||
TraitContainer(cid) => { | ||
if let Some(n) = cx.tcx.hir.as_local_node_id(cid) { | ||
if !cx.access_levels.is_exported(n) { | ||
// If a trait is being implemented for an item, and the | ||
// trait is not exported, we don't need #[inline] | ||
return; | ||
} | ||
} | ||
}, | ||
ImplContainer(cid) => { | ||
if cx.tcx.impl_trait_ref(cid).is_some() { | ||
let trait_ref = cx.tcx.impl_trait_ref(cid).unwrap(); | ||
if let Some(n) = cx.tcx.hir.as_local_node_id(trait_ref.def_id) { | ||
if !cx.access_levels.is_exported(n) { | ||
// If a trait is being implemented for an item, and the | ||
// trait is not exported, we don't need #[inline] | ||
return; | ||
} | ||
} | ||
} | ||
}, | ||
} | ||
|
||
check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* This file incorporates work covered by the following copyright and | ||
* permission notice: | ||
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT | ||
* file at the top-level directory of this distribution and at | ||
* http://rust-lang.org/COPYRIGHT. | ||
* | ||
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
* option. This file may not be copied, modified, or distributed | ||
* except according to those terms. | ||
*/ | ||
#![warn(missing_inline_in_public_items)] | ||
#![crate_type = "dylib"] | ||
// When denying at the crate level, be sure to not get random warnings from the | ||
// injected intrinsics by the compiler. | ||
#![allow(dead_code, non_snake_case)] | ||
|
||
type Typedef = String; | ||
pub type PubTypedef = String; | ||
|
||
struct Foo {} // ok | ||
pub struct PubFoo { } // ok | ||
enum FooE {} // ok | ||
pub enum PubFooE {} // ok | ||
|
||
mod module {} // ok | ||
pub mod pub_module {} // ok | ||
|
||
fn foo() {} | ||
pub fn pub_foo() {} // missing #[inline] | ||
#[inline] pub fn pub_foo_inline() {} // ok | ||
#[inline(always)] pub fn pub_foo_inline_always() {} // ok | ||
|
||
#[allow(missing_inline_in_public_items)] | ||
pub fn pub_foo_no_inline() {} | ||
|
||
trait Bar { | ||
fn Bar_a(); // ok | ||
fn Bar_b() {} // ok | ||
} | ||
|
||
|
||
pub trait PubBar { | ||
fn PubBar_a(); // ok | ||
fn PubBar_b() {} // missing #[inline] | ||
#[inline] fn PubBar_c() {} // ok | ||
} | ||
|
||
// none of these need inline because Foo is not exported | ||
impl PubBar for Foo { | ||
fn PubBar_a() {} // ok | ||
fn PubBar_b() {} // ok | ||
fn PubBar_c() {} // ok | ||
} | ||
|
||
// all of these need inline because PubFoo is exported | ||
impl PubBar for PubFoo { | ||
fn PubBar_a() {} // missing #[inline] | ||
fn PubBar_b() {} // missing #[inline] | ||
fn PubBar_c() {} // missing #[inline] | ||
} | ||
|
||
// do not need inline because Foo is not exported | ||
impl Foo { | ||
fn FooImpl() {} // ok | ||
} | ||
|
||
// need inline because PubFoo is exported | ||
impl PubFoo { | ||
pub fn PubFooImpl() {} // missing #[inline] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
error: missing `#[inline]` for a function | ||
--> $DIR/missing_inline.rs:31:1 | ||
| | ||
31 | pub fn pub_foo() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: `-D missing-inline-in-public-items` implied by `-D warnings` | ||
|
||
error: missing `#[inline]` for a default trait method | ||
--> $DIR/missing_inline.rs:46:5 | ||
| | ||
46 | fn PubBar_b() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
error: missing `#[inline]` for a method | ||
--> $DIR/missing_inline.rs:59:5 | ||
| | ||
59 | fn PubBar_a() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
error: missing `#[inline]` for a method | ||
--> $DIR/missing_inline.rs:60:5 | ||
| | ||
60 | fn PubBar_b() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
error: missing `#[inline]` for a method | ||
--> $DIR/missing_inline.rs:61:5 | ||
| | ||
61 | fn PubBar_c() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^ | ||
|
||
error: missing `#[inline]` for a method | ||
--> $DIR/missing_inline.rs:71:5 | ||
| | ||
71 | pub fn PubFooImpl() {} // missing #[inline] | ||
| ^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 6 previous errors | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even when the example is trivial there should be an example in the documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an example of a lint that does this? I based this lint on
missing_docs
, which doesn't provide anything :/There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added some examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's more than enough examples! Thanks!
You're right the
missing_docs
lint is missing an example. Every other lint does have one AFAIK. Even when it is obvious what the lint does: https://rust-lang-nursery.github.io/rust-clippy/current/index.html#assign_ops