Skip to content

Commit 1ba6587

Browse files
authored
Merge pull request #2816 from mockersf/multiple-impl
adding to restriction a lint that check for multiple inherent implementations
2 parents 41f23f5 + d372f16 commit 1ba6587

File tree

5 files changed

+176
-4
lines changed

5 files changed

+176
-4
lines changed

.travis.yml

+7-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ before_install:
2424
fi
2525
2626
install:
27-
- . $HOME/.nvm/nvm.sh
28-
- nvm install stable
29-
- nvm use stable
30-
- npm install remark-cli remark-lint
27+
- |
28+
if [ -z ${INTEGRATION} ]; then
29+
. $HOME/.nvm/nvm.sh
30+
nvm install stable
31+
nvm use stable
32+
npm install remark-cli remark-lint
33+
fi
3134
3235
matrix:
3336
include:

clippy_lints/src/inherent_impl.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//! lint on inherent implementations
2+
3+
use rustc::hir::*;
4+
use rustc::lint::*;
5+
use std::collections::HashMap;
6+
use std::default::Default;
7+
use syntax_pos::Span;
8+
9+
/// **What it does:** Checks for multiple inherent implementations of a struct
10+
///
11+
/// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.
12+
///
13+
/// **Known problems:** None.
14+
///
15+
/// **Example:**
16+
/// ```rust
17+
/// struct X;
18+
/// impl X {
19+
/// fn one() {}
20+
/// }
21+
/// impl X {
22+
/// fn other() {}
23+
/// }
24+
/// ```
25+
///
26+
/// Could be written:
27+
///
28+
/// ```rust
29+
/// struct X;
30+
/// impl X {
31+
/// fn one() {}
32+
/// fn other() {}
33+
/// }
34+
/// ```
35+
declare_clippy_lint! {
36+
pub MULTIPLE_INHERENT_IMPL,
37+
restriction,
38+
"Multiple inherent impl that could be grouped"
39+
}
40+
41+
pub struct Pass {
42+
impls: HashMap<def_id::DefId, (Span, Generics)>,
43+
}
44+
45+
impl Default for Pass {
46+
fn default() -> Self {
47+
Pass { impls: HashMap::new() }
48+
}
49+
}
50+
51+
impl LintPass for Pass {
52+
fn get_lints(&self) -> LintArray {
53+
lint_array!(MULTIPLE_INHERENT_IMPL)
54+
}
55+
}
56+
57+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
58+
fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item) {
59+
if let Item_::ItemImpl(_, _, _, ref generics, None, _, _) = item.node {
60+
// Remember for each inherent implementation encoutered its span and generics
61+
self.impls
62+
.insert(item.hir_id.owner_def_id(), (item.span, generics.clone()));
63+
}
64+
}
65+
66+
fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx Crate) {
67+
if let Some(item) = krate.items.values().nth(0) {
68+
// Retrieve all inherent implementations from the crate, grouped by type
69+
for impls in cx
70+
.tcx
71+
.crate_inherent_impls(item.hir_id.owner_def_id().krate)
72+
.inherent_impls
73+
.values()
74+
{
75+
// Filter out implementations that have generic params (type or lifetime)
76+
let mut impl_spans = impls
77+
.iter()
78+
.filter_map(|impl_def| self.impls.get(impl_def))
79+
.filter(|(_, generics)| generics.params.len() == 0)
80+
.map(|(span, _)| span);
81+
if let Some(initial_span) = impl_spans.nth(0) {
82+
impl_spans.for_each(|additional_span| {
83+
cx.span_lint_note(
84+
MULTIPLE_INHERENT_IMPL,
85+
*additional_span,
86+
"Multiple implementations of this structure",
87+
*initial_span,
88+
"First implementation here",
89+
)
90+
})
91+
}
92+
}
93+
}
94+
}
95+
}

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ pub mod if_let_redundant_pattern_matching;
139139
pub mod if_not_else;
140140
pub mod infallible_destructuring_match;
141141
pub mod infinite_iter;
142+
pub mod inherent_impl;
142143
pub mod inline_fn_without_body;
143144
pub mod int_plus_one;
144145
pub mod invalid_ref;
@@ -417,6 +418,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
417418
reg.register_early_lint_pass(box multiple_crate_versions::Pass);
418419
reg.register_late_lint_pass(box map_unit_fn::Pass);
419420
reg.register_late_lint_pass(box infallible_destructuring_match::Pass);
421+
reg.register_late_lint_pass(box inherent_impl::Pass::default());
420422

421423

422424
reg.register_lint_group("clippy_restriction", vec![
@@ -425,6 +427,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
425427
array_indexing::INDEXING_SLICING,
426428
assign_ops::ASSIGN_OPS,
427429
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
430+
inherent_impl::MULTIPLE_INHERENT_IMPL,
428431
literal_representation::DECIMAL_LITERAL_REPRESENTATION,
429432
mem_forget::MEM_FORGET,
430433
methods::CLONE_ON_REF_PTR,

tests/ui/impl.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![allow(dead_code)]
2+
#![warn(multiple_inherent_impl)]
3+
4+
struct MyStruct;
5+
6+
impl MyStruct {
7+
fn first() {}
8+
}
9+
10+
impl MyStruct {
11+
fn second() {}
12+
}
13+
14+
impl<'a> MyStruct {
15+
fn lifetimed() {}
16+
}
17+
18+
mod submod {
19+
struct MyStruct;
20+
impl MyStruct {
21+
fn other() {}
22+
}
23+
24+
impl super::MyStruct {
25+
fn third() {}
26+
}
27+
}
28+
29+
use std::fmt;
30+
impl fmt::Debug for MyStruct {
31+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32+
write!(f, "MyStruct {{ }}")
33+
}
34+
}
35+
36+
fn main() {}

tests/ui/impl.stderr

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: Multiple implementations of this structure
2+
--> $DIR/impl.rs:10:1
3+
|
4+
10 | / impl MyStruct {
5+
11 | | fn second() {}
6+
12 | | }
7+
| |_^
8+
|
9+
= note: `-D multiple-inherent-impl` implied by `-D warnings`
10+
note: First implementation here
11+
--> $DIR/impl.rs:6:1
12+
|
13+
6 | / impl MyStruct {
14+
7 | | fn first() {}
15+
8 | | }
16+
| |_^
17+
18+
error: Multiple implementations of this structure
19+
--> $DIR/impl.rs:24:5
20+
|
21+
24 | / impl super::MyStruct {
22+
25 | | fn third() {}
23+
26 | | }
24+
| |_____^
25+
|
26+
note: First implementation here
27+
--> $DIR/impl.rs:6:1
28+
|
29+
6 | / impl MyStruct {
30+
7 | | fn first() {}
31+
8 | | }
32+
| |_^
33+
34+
error: aborting due to 2 previous errors
35+

0 commit comments

Comments
 (0)