blob: 7a791f94994366752d947144c8783bd6e9f7d145 [file] [log] [blame]
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_lint_allowed;
use clippy_utils::macros::span_is_local;
use clippy_utils::source::snippet_opt;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_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"
}
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 {
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))
{
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(),
));
}
},
);
}
}
}
}