Skip to content

Commit 3c60641

Browse files
authored
Merge pull request #2367 from etaoins/inline-fn-without-body-lint
Lint for trait methods without bodies
2 parents 26c415a + 7467b83 commit 3c60641

File tree

7 files changed

+150
-0
lines changed

7 files changed

+150
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ All notable changes to this project will be documented in this file.
583583
[`ineffective_bit_mask`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#ineffective_bit_mask
584584
[`infinite_iter`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#infinite_iter
585585
[`inline_always`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#inline_always
586+
[`inline_fn_without_body`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#inline_fn_without_body
586587
[`int_plus_one`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#int_plus_one
587588
[`integer_arithmetic`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#integer_arithmetic
588589
[`invalid_ref`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#invalid_ref
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! checks for `#[inline]` on trait methods without bodies
2+
3+
use rustc::lint::*;
4+
use rustc::hir::*;
5+
use syntax::ast::{Attribute, Name};
6+
use utils::span_lint_and_then;
7+
use utils::sugg::DiagnosticBuilderExt;
8+
9+
/// **What it does:** Checks for `#[inline]` on trait methods without bodies
10+
///
11+
/// **Why is this bad?** Only implementations of trait methods may be inlined.
12+
/// The inline attribute is ignored for trait methods without bodies.
13+
///
14+
/// **Known problems:** None.
15+
///
16+
/// **Example:**
17+
/// ```rust
18+
/// trait Animal {
19+
/// #[inline]
20+
/// fn name(&self) -> &'static str;
21+
/// }
22+
/// ```
23+
declare_lint! {
24+
pub INLINE_FN_WITHOUT_BODY,
25+
Warn,
26+
"use of `#[inline]` on trait methods without bodies"
27+
}
28+
29+
#[derive(Copy, Clone)]
30+
pub struct Pass;
31+
32+
impl LintPass for Pass {
33+
fn get_lints(&self) -> LintArray {
34+
lint_array!(INLINE_FN_WITHOUT_BODY)
35+
}
36+
}
37+
38+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
39+
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
40+
match item.node {
41+
TraitItemKind::Method(_, TraitMethod::Required(_)) => {
42+
check_attrs(cx, &item.name, &item.attrs);
43+
},
44+
_ => {},
45+
}
46+
}
47+
}
48+
49+
fn check_attrs(cx: &LateContext, name: &Name, attrs: &[Attribute]) {
50+
for attr in attrs {
51+
if attr.name().map_or(true, |n| n != "inline") {
52+
continue;
53+
}
54+
55+
span_lint_and_then(
56+
cx,
57+
INLINE_FN_WITHOUT_BODY,
58+
attr.span,
59+
&format!("use of `#[inline]` on trait method `{}` which has no body", name),
60+
|db| {
61+
db.suggest_remove_item(cx, attr.span, "remove");
62+
},
63+
);
64+
}
65+
}

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub mod identity_op;
109109
pub mod if_let_redundant_pattern_matching;
110110
pub mod if_not_else;
111111
pub mod infinite_iter;
112+
pub mod inline_fn_without_body;
112113
pub mod int_plus_one;
113114
pub mod invalid_ref;
114115
pub mod items_after_statements;
@@ -361,6 +362,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
361362
reg.register_late_lint_pass(box use_self::UseSelf);
362363
reg.register_late_lint_pass(box bytecount::ByteCount);
363364
reg.register_late_lint_pass(box infinite_iter::Pass);
365+
reg.register_late_lint_pass(box inline_fn_without_body::Pass);
364366
reg.register_late_lint_pass(box invalid_ref::InvalidRef);
365367
reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default());
366368
reg.register_late_lint_pass(box types::ImplicitHasher);
@@ -480,6 +482,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
480482
identity_op::IDENTITY_OP,
481483
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
482484
infinite_iter::INFINITE_ITER,
485+
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
483486
invalid_ref::INVALID_REF,
484487
large_enum_variant::LARGE_ENUM_VARIANT,
485488
len_zero::LEN_WITHOUT_IS_EMPTY,

clippy_lints/src/utils/sugg.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use syntax::print::pprust::token_to_string;
1515
use syntax::util::parser::AssocOp;
1616
use syntax::ast;
1717
use utils::{higher, snippet, snippet_opt};
18+
use syntax_pos::{BytePos, Pos};
1819

1920
/// A helper type to build suggestion correctly handling parenthesis.
2021
pub enum Sugg<'a> {
@@ -454,6 +455,19 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext<'a>> {
454455
/// }");
455456
/// ```
456457
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str);
458+
459+
/// Suggest to completely remove an item.
460+
///
461+
/// This will remove an item and all following whitespace until the next non-whitespace
462+
/// character. This should work correctly if item is on the same indentation level as the
463+
/// following item.
464+
///
465+
/// # Example
466+
///
467+
/// ```rust,ignore
468+
/// db.suggest_remove_item(cx, item, "remove this")
469+
/// ```
470+
fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str);
457471
}
458472

459473
impl<'a, 'b, 'c, T: LintContext<'c>> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> {
@@ -485,4 +499,21 @@ impl<'a, 'b, 'c, T: LintContext<'c>> DiagnosticBuilderExt<'c, T> for rustc_error
485499
self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent));
486500
}
487501
}
502+
503+
fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str) {
504+
let mut remove_span = item;
505+
let fmpos = cx.sess()
506+
.codemap()
507+
.lookup_byte_offset(remove_span.next_point().hi());
508+
509+
if let Some(ref src) = fmpos.fm.src {
510+
let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
511+
512+
if let Some(non_whitespace_offset) = non_whitespace_offset {
513+
remove_span = remove_span.with_hi(remove_span.hi() + BytePos(non_whitespace_offset as u32))
514+
}
515+
}
516+
517+
self.span_suggestion(remove_span, msg, String::new());
518+
}
488519
}

main

545 KB
Binary file not shown.

tests/ui/inline_fn_without_body.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
3+
4+
#![warn(inline_fn_without_body)]
5+
#![allow(inline_always)]
6+
7+
trait Foo {
8+
#[inline]
9+
fn default_inline();
10+
11+
#[inline(always)]fn always_inline();
12+
13+
#[inline(never)]
14+
15+
fn never_inline();
16+
17+
#[inline]
18+
fn has_body() {
19+
}
20+
}
21+
22+
fn main() {
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: use of `#[inline]` on trait method `default_inline` which has no body
2+
--> $DIR/inline_fn_without_body.rs:8:5
3+
|
4+
8 | #[inline]
5+
| _____-^^^^^^^^
6+
9 | | fn default_inline();
7+
| |____- help: remove
8+
|
9+
= note: `-D inline-fn-without-body` implied by `-D warnings`
10+
11+
error: use of `#[inline]` on trait method `always_inline` which has no body
12+
--> $DIR/inline_fn_without_body.rs:11:5
13+
|
14+
11 | #[inline(always)]fn always_inline();
15+
| ^^^^^^^^^^^^^^^^^ help: remove
16+
17+
error: use of `#[inline]` on trait method `never_inline` which has no body
18+
--> $DIR/inline_fn_without_body.rs:13:5
19+
|
20+
13 | #[inline(never)]
21+
| _____-^^^^^^^^^^^^^^^
22+
14 | |
23+
15 | | fn never_inline();
24+
| |____- help: remove
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)