diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 841d561b371c..12b22c53928c 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -10,11 +10,11 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemId, ImplItemKind, Item, ItemKind, PatKind, Path, - PathSegment, Ty, TyKind, + PathSegment, Ty as HirTy, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -80,6 +80,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _)) && self.msrv.meets(cx, msrvs::RE_REBALANCING_COHERENCE) + // skip if there's a blanket From impl, the suggested impl would conflict + && !has_blanket_from_impl(cx, middle_trait_ref.self_ty()) { span_lint_and_then( cx, @@ -163,11 +165,21 @@ impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> { } } +fn has_blanket_from_impl<'tcx>(cx: &LateContext<'tcx>, self_ty: Ty<'tcx>) -> bool { + let Some(from_def_id) = cx.tcx.get_diagnostic_item(sym::From) else { + return false; + }; + cx.tcx.non_blanket_impls_for_ty(from_def_id, self_ty).any(|impl_id| { + let impl_trait_ref = cx.tcx.impl_trait_ref(impl_id).instantiate_identity(); + matches!(impl_trait_ref.args.type_at(1).kind(), ty::Param(_)) + }) +} + fn convert_to_from( cx: &LateContext<'_>, into_trait_seg: &PathSegment<'_>, - target_ty: &Ty<'_>, - self_ty: &Ty<'_>, + target_ty: &HirTy<'_>, + self_ty: &HirTy<'_>, impl_item_ref: ImplItemId, ) -> Option> { if !target_ty.find_self_aliases().is_empty() { diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 7229e5a2d358..3eed0ad52e9a 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -116,4 +116,24 @@ fn issue_112502() { } } +fn issue_16823() { + pub struct Foo(pub String); + + impl From for Foo + where + String: From, + { + fn from(val: T) -> Self { + Self(String::from(val)) + } + } + + // no lint, From for String would conflict with the blanket impl above + impl Into for Foo { + fn into(self) -> String { + self.0 + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 9c75969c5c13..b4f58df0a437 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -116,4 +116,24 @@ fn issue_112502() { } } +fn issue_16823() { + pub struct Foo(pub String); + + impl From for Foo + where + String: From, + { + fn from(val: T) -> Self { + Self(String::from(val)) + } + } + + // no lint, From for String would conflict with the blanket impl above + impl Into for Foo { + fn into(self) -> String { + self.0 + } + } +} + fn main() {}