Skip to content

Commit 5987c7d

Browse files
committed
cmp_owned: avoid FP when PartialEq is not implemented symmetrically
1 parent 583d644 commit 5987c7d

File tree

2 files changed

+71
-15
lines changed

2 files changed

+71
-15
lines changed

clippy_lints/src/misc.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use rustc_ast::ast::LitKind;
33
use rustc_errors::Applicability;
44
use rustc_hir::intravisit::FnKind;
55
use rustc_hir::{
6-
def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty,
7-
TyKind, UnOp,
6+
self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
7+
StmtKind, TyKind, UnOp,
88
};
99
use rustc_lint::{LateContext, LateLintPass};
10-
use rustc_middle::ty;
10+
use rustc_middle::ty::{self, Ty};
1111
use rustc_session::{declare_lint_pass, declare_tool_lint};
1212
use rustc_span::hygiene::DesugaringKind;
1313
use rustc_span::source_map::{ExpnKind, Span};
@@ -571,6 +571,15 @@ fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
571571
}
572572

573573
fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
574+
fn symmetric_partial_eq<'tcx>(cx: &LateContext<'_, 'tcx>, lhs: Ty<'tcx>, rhs: Ty<'tcx>) -> bool {
575+
if let Some(trait_def_id) = cx.tcx.lang_items().eq_trait() {
576+
return implements_trait(cx, lhs, trait_def_id, &[rhs.into()])
577+
&& implements_trait(cx, rhs, trait_def_id, &[lhs.into()]);
578+
}
579+
580+
false
581+
}
582+
574583
let (arg_ty, snip) = match expr.kind {
575584
ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
576585
if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
@@ -594,18 +603,14 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
594603
};
595604

596605
let other_ty = cx.tables.expr_ty_adjusted(other);
597-
let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
598-
Some(id) => id,
599-
None => return,
600-
};
601606

602-
let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| {
603-
implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()])
604-
});
605-
let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| {
606-
implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()])
607-
});
608-
let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
607+
let deref_arg_impl_partial_eq_other = arg_ty
608+
.builtin_deref(true)
609+
.map_or(false, |tam| symmetric_partial_eq(cx, tam.ty, other_ty));
610+
let arg_impl_partial_eq_deref_other = other_ty
611+
.builtin_deref(true)
612+
.map_or(false, |tam| symmetric_partial_eq(cx, arg_ty, tam.ty));
613+
let arg_impl_partial_eq_other = symmetric_partial_eq(cx, arg_ty, other_ty);
609614

610615
if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other {
611616
return;
@@ -694,7 +699,7 @@ fn non_macro_local(cx: &LateContext<'_, '_>, res: def::Res) -> bool {
694699
}
695700
}
696701

697-
fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
702+
fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
698703
if_chain! {
699704
if let TyKind::Ptr(ref mut_ty) = ty.kind;
700705
if let ExprKind::Lit(ref lit) = e.kind;

tests/ui/cmp_owned/issue_4874.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![allow(clippy::redundant_clone)] // See #5700
2+
3+
#[derive(PartialEq)]
4+
struct Foo;
5+
6+
struct Bar;
7+
8+
impl std::fmt::Display for Bar {
9+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10+
write!(f, "bar")
11+
}
12+
}
13+
14+
// NOTE: PartialEq<Bar> for T can't be implemented due to the orphan rules
15+
impl<T> PartialEq<T> for Bar
16+
where
17+
T: AsRef<str> + ?Sized,
18+
{
19+
fn eq(&self, _: &T) -> bool {
20+
true
21+
}
22+
}
23+
24+
// NOTE: PartialEq<Bar> for Foo is not implemented
25+
impl PartialEq<Foo> for Bar {
26+
fn eq(&self, _: &Foo) -> bool {
27+
true
28+
}
29+
}
30+
31+
impl ToOwned for Bar {
32+
type Owned = Foo;
33+
fn to_owned(&self) -> Foo {
34+
Foo
35+
}
36+
}
37+
38+
impl std::borrow::Borrow<Bar> for Foo {
39+
fn borrow(&self) -> &Bar {
40+
static BAR: Bar = Bar;
41+
&BAR
42+
}
43+
}
44+
45+
fn main() {
46+
let b = Bar {};
47+
if "Hi" == b.to_string() {}
48+
49+
let f = Foo {};
50+
if f == b.to_owned() {}
51+
}

0 commit comments

Comments
 (0)