]>
Commit | Line | Data |
---|---|---|
2b03887a FG |
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 | } |