diff options
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/missing_trait_methods.rs')
-rw-r--r-- | src/tools/clippy/clippy_lints/src/missing_trait_methods.rs | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 000000000..68af8a672 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + for assoc in provided.values() { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + } + } +} |