about summary refs log tree commit diff
path: root/clippy_lints/src/missing_trait_methods.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src/missing_trait_methods.rs')
-rw-r--r--clippy_lints/src/missing_trait_methods.rs98
1 files changed, 98 insertions, 0 deletions
diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs
new file mode 100644
index 00000000000..68af8a672f6
--- /dev/null
+++ b/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",
+                );
+            }
+        }
+    }
+}