| use clippy_config::Conf; | |
| use clippy_utils::diagnostics::span_lint_and_then; | |
| use clippy_utils::is_lint_allowed; | |
| use clippy_utils::macros::span_is_local; | |
| use clippy_utils::msrvs::Msrv; | |
| use clippy_utils::source::snippet_opt; | |
| use rustc_hir::def_id::DefIdSet; | |
| use rustc_hir::{Impl, Item, ItemKind}; | |
| use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks if a provided method is used implicitly by a trait | |
| /// implementation. | |
| /// | |
| /// ### Why restrict this? | |
| /// To ensure that a certain implementation implements every method; for example, | |
| /// a wrapper type where every method should delegate to the corresponding method of | |
| /// the inner type's implementation. | |
| /// | |
| /// This lint should typically be enabled on a specific trait `impl` item | |
| /// rather than globally. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// trait Trait { | |
| /// fn required(); | |
| /// | |
| /// fn provided() {} | |
| /// } | |
| /// | |
| /// # struct Type; | |
| /// #[warn(clippy::missing_trait_methods)] | |
| /// impl Trait for Type { | |
| /// fn required() { /* ... */ } | |
| /// } | |
| /// ``` | |
| /// Use instead: | |
| /// ```no_run | |
| /// 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" | |
| } | |
| impl_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); | |
| pub struct MissingTraitMethods { | |
| msrv: Msrv, | |
| } | |
| impl MissingTraitMethods { | |
| pub fn new(conf: &'static Conf) -> Self { | |
| Self { msrv: conf.msrv.into() } | |
| } | |
| } | |
| 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 { | |
| of_trait: Some(of_trait), | |
| .. | |
| }) = item.kind | |
| && let Some(trait_id) = of_trait.trait_ref.trait_def_id() | |
| { | |
| let trait_item_ids: DefIdSet = cx | |
| .tcx | |
| .associated_items(item.owner_id) | |
| .in_definition_order() | |
| .filter_map(|assoc_item| assoc_item.expect_trait_impl().ok()) | |
| .collect(); | |
| for assoc in cx | |
| .tcx | |
| .provided_trait_methods(trait_id) | |
| .filter(|assoc| !trait_item_ids.contains(&assoc.def_id)) | |
| .filter(|assoc| self.msrv.is_stable(cx, assoc.def_id)) | |
| { | |
| span_lint_and_then( | |
| cx, | |
| MISSING_TRAIT_METHODS, | |
| cx.tcx.def_span(item.owner_id), | |
| format!("missing trait method provided by default: `{}`", assoc.name()), | |
| |diag| { | |
| if assoc.def_id.is_local() { | |
| diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method"); | |
| } else if let Some(snippet) = snippet_opt(cx, of_trait.trait_ref.path.span) { | |
| diag.help(format!( | |
| "implement the missing `{}` method of the `{snippet}` trait", | |
| assoc.name(), | |
| )); | |
| } | |
| }, | |
| ); | |
| } | |
| } | |
| } | |
| } |