-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathmissing_inline.rs
More file actions
104 lines (98 loc) · 3.71 KB
/
Copy pathmissing_inline.rs
File metadata and controls
104 lines (98 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use clippy_utils::diagnostics::span_lint;
use rustc_attr_ir::find_attr;
use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, OwnerId, TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, declare_lint_pass};
use rustc_span::Span;
use rustc_structures::CrateType;
declare_clippy_lint! {
/// ### What it does
/// It lints if an exported function, method, trait method with default impl,
/// or trait method impl is not `#[inline]`.
///
/// ### Why restrict this?
/// When a function is not marked `#[inline]`, it is not
/// [a “small” candidate for automatic inlining][small], and LTO is not in use, then it is not
/// possible for the function to be inlined into the code of any crate other than the one in
/// which it is defined. Depending on the role of the function and the relationship of the crates,
/// this could significantly reduce performance.
///
/// 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.
/// This lint allows those crates to require all exported methods to be `#[inline]` by default, and
/// then opt out for specific methods where this might not make sense.
///
/// ### Example
/// ```no_run
/// 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 private() {} // ok
/// }
///
/// impl Bar for Baz {
/// fn bar() {} // ok - Baz is not exported
/// }
///
/// pub struct PubBaz;
/// impl PubBaz {
/// fn private() {} // ok
/// pub fn not_private() {} // missing #[inline]
/// }
///
/// impl Bar for PubBaz {
/// fn bar() {} // missing #[inline]
/// fn def_bar() {} // missing #[inline]
/// }
/// ```
///
/// [small]: https://github.com/rust-lang/rust/pull/116505
#[clippy::version = "pre 1.29.0"]
pub MISSING_INLINE_IN_PUBLIC_ITEMS,
restriction,
"detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
}
declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
fn check(cx: &LateContext<'_>, item: OwnerId, sp: Span) {
if cx.effective_visibilities.is_exported(item.def_id)
&& !find_attr!(cx.tcx.hir_attrs(item.into()), Inline(..))
// Rust `inline` doesn't mean anything with external linkage.
&& !cx.tcx.codegen_fn_attrs(item.def_id).contains_extern_indicator()
&& !cx.tcx.crate_types().iter().any(|&t| matches!(t, CrateType::ProcMacro))
&& !sp.in_external_macro(cx.tcx.sess.source_map())
{
span_lint(
cx,
MISSING_INLINE_IN_PUBLIC_ITEMS,
sp,
"missing `#[inline]` on a publicly callable function",
);
}
}
impl<'tcx> LateLintPass<'tcx> for MissingInline {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
if let ItemKind::Fn { .. } = it.kind {
check(cx, it.owner_id, it.span);
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(_, f) = item.kind
&& let TraitFn::Provided(_) = f
{
check(cx, item.owner_id, item.span);
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if let ImplItemKind::Fn(..) = item.kind {
check(cx, item.owner_id, item.span);
}
}
}