]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
Rollup merge of #106260 - chenyukang:yukang/fix-106213-doc, r=GuillaumeGomez
[rust.git] / src / tools / clippy / clippy_lints / src / missing_trait_methods.rs
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 }