| 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", |
| ); |
| } |
| } |
| } |
| } |