Skip to content

Commit 4612fdf

Browse files
committed
Auto merge of #9670 - Alexendoo:missing-trait-methods, r=Jarcho
Add `missing_trait_methods` lint Closes #9661 changelog: new lint: [`missing_trait_methods`]
2 parents a4e8726 + b6a860e commit 4612fdf

9 files changed

+221
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4049,6 +4049,7 @@ Released 2018-09-13
40494049
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
40504050
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
40514051
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
4052+
[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
40524053
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
40534054
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
40544055
[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ store.register_lints(&[
407407
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
408408
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
409409
missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
410+
missing_trait_methods::MISSING_TRAIT_METHODS,
410411
mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
411412
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
412413
module_style::MOD_MODULE_FILES,

clippy_lints/src/lib.register_restriction.rs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
4747
LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
4848
LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
4949
LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
50+
LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS),
5051
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
5152
LintId::of(module_style::MOD_MODULE_FILES),
5253
LintId::of(module_style::SELF_NAMED_MODULE_FILES),

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ mod missing_const_for_fn;
289289
mod missing_doc;
290290
mod missing_enforced_import_rename;
291291
mod missing_inline;
292+
mod missing_trait_methods;
292293
mod mixed_read_write_in_expression;
293294
mod module_style;
294295
mod multi_assignments;
@@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
916917
store.register_late_pass(|_| Box::new(box_default::BoxDefault));
917918
store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
918919
store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
920+
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
919921
// add lints here, do not remove this comment, it's used in `new_lint`
920922
}
921923

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::is_lint_allowed;
3+
use clippy_utils::macros::span_is_local;
4+
use rustc_hir::def_id::DefIdMap;
5+
use rustc_hir::{Impl, Item, ItemKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_middle::ty::AssocItem;
8+
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks if a provided method is used implicitly by a trait
13+
/// implementation. A usage example would be a wrapper where every method
14+
/// should perform some operation before delegating to the inner type's
15+
/// implemenation.
16+
///
17+
/// This lint should typically be enabled on a specific trait `impl` item
18+
/// rather than globally.
19+
///
20+
/// ### Why is this bad?
21+
/// Indicates that a method is missing.
22+
///
23+
/// ### Example
24+
/// ```rust
25+
/// trait Trait {
26+
/// fn required();
27+
///
28+
/// fn provided() {}
29+
/// }
30+
///
31+
/// # struct Type;
32+
/// #[warn(clippy::missing_trait_methods)]
33+
/// impl Trait for Type {
34+
/// fn required() { /* ... */ }
35+
/// }
36+
/// ```
37+
/// Use instead:
38+
/// ```rust
39+
/// trait Trait {
40+
/// fn required();
41+
///
42+
/// fn provided() {}
43+
/// }
44+
///
45+
/// # struct Type;
46+
/// #[warn(clippy::missing_trait_methods)]
47+
/// impl Trait for Type {
48+
/// fn required() { /* ... */ }
49+
///
50+
/// fn provided() { /* ... */ }
51+
/// }
52+
/// ```
53+
#[clippy::version = "1.66.0"]
54+
pub MISSING_TRAIT_METHODS,
55+
restriction,
56+
"trait implementation uses default provided method"
57+
}
58+
declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]);
59+
60+
impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
61+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
62+
if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id())
63+
&& span_is_local(item.span)
64+
&& let ItemKind::Impl(Impl {
65+
items,
66+
of_trait: Some(trait_ref),
67+
..
68+
}) = item.kind
69+
&& let Some(trait_id) = trait_ref.trait_def_id()
70+
{
71+
let mut provided: DefIdMap<&AssocItem> = cx
72+
.tcx
73+
.provided_trait_methods(trait_id)
74+
.map(|assoc| (assoc.def_id, assoc))
75+
.collect();
76+
77+
for impl_item in *items {
78+
if let Some(def_id) = impl_item.trait_item_def_id {
79+
provided.remove(&def_id);
80+
}
81+
}
82+
83+
for assoc in provided.values() {
84+
let source_map = cx.tcx.sess.source_map();
85+
let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
86+
87+
span_lint_and_help(
88+
cx,
89+
MISSING_TRAIT_METHODS,
90+
source_map.guess_head_span(item.span),
91+
&format!("missing trait method provided by default: `{}`", assoc.name),
92+
Some(definition_span),
93+
"implement the method",
94+
);
95+
}
96+
}
97+
}
98+
}

src/docs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ docs! {
316316
"missing_panics_doc",
317317
"missing_safety_doc",
318318
"missing_spin_loop",
319+
"missing_trait_methods",
319320
"mistyped_literal_suffixes",
320321
"mixed_case_hex_literals",
321322
"mixed_read_write_in_expression",

src/docs/missing_trait_methods.txt

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
### What it does
2+
Checks if a provided method is used implicitly by a trait
3+
implementation. A usage example would be a wrapper where every method
4+
should perform some operation before delegating to the inner type's
5+
implemenation.
6+
7+
This lint should typically be enabled on a specific trait `impl` item
8+
rather than globally.
9+
10+
### Why is this bad?
11+
Indicates that a method is missing.
12+
13+
### Example
14+
```
15+
trait Trait {
16+
fn required();
17+
18+
fn provided() {}
19+
}
20+
21+
#[warn(clippy::missing_trait_methods)]
22+
impl Trait for Type {
23+
fn required() { /* ... */ }
24+
}
25+
```
26+
Use instead:
27+
```
28+
trait Trait {
29+
fn required();
30+
31+
fn provided() {}
32+
}
33+
34+
#[warn(clippy::missing_trait_methods)]
35+
impl Trait for Type {
36+
fn required() { /* ... */ }
37+
38+
fn provided() { /* ... */ }
39+
}
40+
```

tests/ui/missing_trait_methods.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![allow(unused, clippy::needless_lifetimes)]
2+
#![warn(clippy::missing_trait_methods)]
3+
4+
trait A {
5+
fn provided() {}
6+
}
7+
8+
trait B {
9+
fn required();
10+
11+
fn a(_: usize) -> usize {
12+
1
13+
}
14+
15+
fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] {
16+
a.as_ref()
17+
}
18+
}
19+
20+
struct Partial;
21+
22+
impl A for Partial {}
23+
24+
impl B for Partial {
25+
fn required() {}
26+
27+
fn a(_: usize) -> usize {
28+
2
29+
}
30+
}
31+
32+
struct Complete;
33+
34+
impl A for Complete {
35+
fn provided() {}
36+
}
37+
38+
impl B for Complete {
39+
fn required() {}
40+
41+
fn a(_: usize) -> usize {
42+
2
43+
}
44+
45+
fn b<T: AsRef<[u8]>>(a: &T) -> &[u8] {
46+
a.as_ref()
47+
}
48+
}
49+
50+
fn main() {}

tests/ui/missing_trait_methods.stderr

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: missing trait method provided by default: `provided`
2+
--> $DIR/missing_trait_methods.rs:22:1
3+
|
4+
LL | impl A for Partial {}
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
help: implement the method
8+
--> $DIR/missing_trait_methods.rs:5:5
9+
|
10+
LL | fn provided() {}
11+
| ^^^^^^^^^^^^^
12+
= note: `-D clippy::missing-trait-methods` implied by `-D warnings`
13+
14+
error: missing trait method provided by default: `b`
15+
--> $DIR/missing_trait_methods.rs:24:1
16+
|
17+
LL | impl B for Partial {
18+
| ^^^^^^^^^^^^^^^^^^
19+
|
20+
help: implement the method
21+
--> $DIR/missing_trait_methods.rs:15:5
22+
|
23+
LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] {
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
26+
error: aborting due to 2 previous errors
27+

0 commit comments

Comments
 (0)