Files
rust/clippy_lints/src/missing_trait_methods.rs
T
Samuel Tardieu 47b987eccb Do not show spans from external crates in missing_trait_methods
Pointing to the missing method definition in external crates will use
paths which depend on the system, and even on the Rust compiler version
used when the iterator is defined in the standard library.

When the trait being implemented and whose method is missing is
external, point only to the trait implementation. The user will be able
to figure out, or even navigate using their IDE, to the proper
definition.

As a side-effect, this will remove a large lintcheck churn at every Rustup 
for this lint.
2026-01-11 10:53:25 +01:00

102 lines
3.4 KiB
Rust

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