diff --git a/clippy_lints/src/to_string_trait_impl.rs b/clippy_lints/src/to_string_trait_impl.rs index e1cea99085f..59ae185c9de 100644 --- a/clippy_lints/src/to_string_trait_impl.rs +++ b/clippy_lints/src/to_string_trait_impl.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::implements_trait; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -53,6 +54,8 @@ impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl { }) = it.kind && let Some(trait_did) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::ToString, trait_did) + && let Some(display_did) = cx.tcx.get_diagnostic_item(sym::Display) + && !implements_trait(cx, cx.tcx.type_of(it.owner_id).instantiate_identity(), display_did, &[]) { span_lint_and_help( cx, diff --git a/tests/ui/to_string_trait_impl.rs b/tests/ui/to_string_trait_impl.rs index b0731632d45..4c1202d4203 100644 --- a/tests/ui/to_string_trait_impl.rs +++ b/tests/ui/to_string_trait_impl.rs @@ -1,4 +1,5 @@ #![warn(clippy::to_string_trait_impl)] +#![feature(min_specialization)] use std::fmt::{self, Display}; @@ -29,3 +30,46 @@ impl Bar { String::from("Bar") } } + +mod issue12263 { + pub struct MyStringWrapper<'a>(&'a str); + + impl std::fmt::Display for MyStringWrapper<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl ToString for MyStringWrapper<'_> { + fn to_string(&self) -> String { + self.0.to_string() + } + } + + pub struct S(T); + impl std::fmt::Display for S { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + // no specialization if the generics differ, so lint + impl ToString for S { + fn to_string(&self) -> String { + todo!() + } + } + + pub struct S2(T); + impl std::fmt::Display for S2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + todo!() + } + } + + // also specialization if the generics don't differ + impl ToString for S2 { + fn to_string(&self) -> String { + todo!() + } + } +} diff --git a/tests/ui/to_string_trait_impl.stderr b/tests/ui/to_string_trait_impl.stderr index 55fa9f12c0e..0051ea25ae0 100644 --- a/tests/ui/to_string_trait_impl.stderr +++ b/tests/ui/to_string_trait_impl.stderr @@ -1,5 +1,5 @@ error: direct implementation of `ToString` - --> $DIR/to_string_trait_impl.rs:10:1 + --> $DIR/to_string_trait_impl.rs:11:1 | LL | / impl ToString for Point { LL | | fn to_string(&self) -> String { @@ -12,5 +12,17 @@ LL | | } = note: `-D clippy::to-string-trait-impl` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::to_string_trait_impl)]` -error: aborting due to 1 previous error +error: direct implementation of `ToString` + --> $DIR/to_string_trait_impl.rs:56:5 + | +LL | / impl ToString for S { +LL | | fn to_string(&self) -> String { +LL | | todo!() +LL | | } +LL | | } + | |_____^ + | + = help: prefer implementing `Display` instead + +error: aborting due to 2 previous errors