summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
diff options
context:
space:
mode:
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.rs98
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",
+ );
+ }
+ }
+ }
+}