]>
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; | |
4b012472 | 8 | use rustc_session::declare_lint_pass; |
2b03887a FG |
9 | |
10 | declare_clippy_lint! { | |
11 | /// ### What it does | |
12 | /// Checks if a provided method is used implicitly by a trait | |
49aad941 | 13 | /// implementation. |
2b03887a | 14 | /// |
31ef2f64 FG |
15 | /// ### Why restrict this? |
16 | /// To ensure that a certain implementation implements every method; for example, | |
17 | /// a wrapper type where every method should delegate to the corresponding method of | |
18 | /// the inner type's implementation. | |
19 | /// | |
2b03887a FG |
20 | /// This lint should typically be enabled on a specific trait `impl` item |
21 | /// rather than globally. | |
22 | /// | |
2b03887a | 23 | /// ### Example |
ed00b5ec | 24 | /// ```no_run |
2b03887a FG |
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: | |
ed00b5ec | 38 | /// ```no_run |
2b03887a FG |
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 | ||
9c376795 FG |
83 | cx.tcx.with_stable_hashing_context(|hcx| { |
84 | for assoc in provided.values_sorted(&hcx, true) { | |
85 | let source_map = cx.tcx.sess.source_map(); | |
86 | let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); | |
2b03887a | 87 | |
9c376795 FG |
88 | span_lint_and_help( |
89 | cx, | |
90 | MISSING_TRAIT_METHODS, | |
91 | source_map.guess_head_span(item.span), | |
e8be2606 | 92 | format!("missing trait method provided by default: `{}`", assoc.name), |
9c376795 FG |
93 | Some(definition_span), |
94 | "implement the method", | |
95 | ); | |
96 | } | |
9ffffee4 | 97 | }); |
2b03887a FG |
98 | } |
99 | } | |
100 | } |