@@ -69,14 +69,6 @@ fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) {
69
69
) ;
70
70
}
71
71
72
- fn get_ty_def_id ( ty : Ty < ' _ > ) -> Option < DefId > {
73
- match ty. peel_refs ( ) . kind ( ) {
74
- ty:: Adt ( adt, _) => Some ( adt. did ( ) ) ,
75
- ty:: Foreign ( def_id) => Some ( * def_id) ,
76
- _ => None ,
77
- }
78
- }
79
-
80
72
fn get_hir_ty_def_id < ' tcx > ( tcx : TyCtxt < ' tcx > , hir_ty : rustc_hir:: Ty < ' tcx > ) -> Option < DefId > {
81
73
let TyKind :: Path ( qpath) = hir_ty. kind else { return None } ;
82
74
match qpath {
@@ -131,21 +123,49 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
131
123
}
132
124
}
133
125
134
- #[ allow( clippy:: unnecessary_def_path) ]
126
+ /// When we have `x == y` where `x = &T` and `y = &T`, then that resolves to
127
+ /// `<&T as PartialEq<&T>>::eq`, which is not the same as `<T as PartialEq<T>>::eq`,
128
+ /// however we still would want to treat it the same, because we know that it's a blanket impl
129
+ /// that simply delegates to the `PartialEq` impl with one reference removed.
130
+ ///
131
+ /// Still, we can't just do `lty.peel_refs() == rty.peel_refs()` because when we have `x = &T` and
132
+ /// `y = &&T`, this is not necessarily the same as `<T as PartialEq<T>>::eq`
133
+ ///
134
+ /// So to avoid these FNs and FPs, we keep removing a layer of references from *both* sides
135
+ /// until both sides match the expected LHS and RHS type (or they don't).
136
+ fn matches_ty < ' tcx > (
137
+ mut left : Ty < ' tcx > ,
138
+ mut right : Ty < ' tcx > ,
139
+ expected_left : Ty < ' tcx > ,
140
+ expected_right : Ty < ' tcx > ,
141
+ ) -> bool {
142
+ while let ( & ty:: Ref ( _, lty, _) , & ty:: Ref ( _, rty, _) ) = ( left. kind ( ) , right. kind ( ) ) {
143
+ if lty == expected_left && rty == expected_right {
144
+ return true ;
145
+ }
146
+ left = lty;
147
+ right = rty;
148
+ }
149
+ false
150
+ }
151
+
135
152
fn check_partial_eq ( cx : & LateContext < ' _ > , method_span : Span , method_def_id : LocalDefId , name : Ident , expr : & Expr < ' _ > ) {
136
- let args = cx
137
- . tcx
138
- . instantiate_bound_regions_with_erased ( cx. tcx . fn_sig ( method_def_id) . skip_binder ( ) )
139
- . inputs ( ) ;
153
+ let Some ( sig) = cx
154
+ . typeck_results ( )
155
+ . liberated_fn_sigs ( )
156
+ . get ( cx. tcx . local_def_id_to_hir_id ( method_def_id) )
157
+ else {
158
+ return ;
159
+ } ;
160
+
140
161
// That has two arguments.
141
- if let [ self_arg, other_arg] = args
142
- && let Some ( self_arg) = get_ty_def_id ( * self_arg)
143
- && let Some ( other_arg) = get_ty_def_id ( * other_arg)
162
+ if let [ self_arg, other_arg] = sig . inputs ( )
163
+ && let & ty :: Ref ( _ , self_arg, _ ) = self_arg. kind ( )
164
+ && let & ty :: Ref ( _ , other_arg, _ ) = other_arg. kind ( )
144
165
// The two arguments are of the same type.
145
- && self_arg == other_arg
146
166
&& let Some ( trait_def_id) = get_impl_trait_def_id ( cx, method_def_id)
147
167
// The trait is `PartialEq`.
148
- && Some ( trait_def_id ) == get_trait_def_id ( cx , & [ "core" , "cmp" , "PartialEq" ] )
168
+ && cx . tcx . is_diagnostic_item ( sym :: PartialEq , trait_def_id )
149
169
{
150
170
let to_check_op = if name. name == sym:: eq {
151
171
BinOpKind :: Eq
@@ -154,31 +174,19 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
154
174
} ;
155
175
let is_bad = match expr. kind {
156
176
ExprKind :: Binary ( op, left, right) if op. node == to_check_op => {
157
- // Then we check if the left-hand element is of the same type as `self`.
158
- if let Some ( left_ty) = cx. typeck_results ( ) . expr_ty_opt ( left)
159
- && let Some ( left_id) = get_ty_def_id ( left_ty)
160
- && self_arg == left_id
161
- && let Some ( right_ty) = cx. typeck_results ( ) . expr_ty_opt ( right)
162
- && let Some ( right_id) = get_ty_def_id ( right_ty)
163
- && other_arg == right_id
164
- {
165
- true
166
- } else {
167
- false
168
- }
177
+ // Then we check if the LHS matches self_arg and RHS matches other_arg
178
+ let left_ty = cx. typeck_results ( ) . expr_ty_adjusted ( left) ;
179
+ let right_ty = cx. typeck_results ( ) . expr_ty_adjusted ( right) ;
180
+ matches_ty ( left_ty, right_ty, self_arg, other_arg)
169
181
} ,
170
- ExprKind :: MethodCall ( segment, receiver, & [ _arg] , _) if segment. ident . name == name. name => {
171
- if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( receiver)
172
- && let Some ( ty_id) = get_ty_def_id ( ty)
173
- && self_arg != ty_id
174
- {
175
- // Since this called on a different type, the lint should not be
176
- // triggered here.
177
- return ;
178
- }
182
+ ExprKind :: MethodCall ( segment, receiver, [ arg] , _) if segment. ident . name == name. name => {
183
+ let receiver_ty = cx. typeck_results ( ) . expr_ty_adjusted ( receiver) ;
184
+ let arg_ty = cx. typeck_results ( ) . expr_ty_adjusted ( arg) ;
185
+
179
186
if let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
180
187
&& let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
181
188
&& trait_id == trait_def_id
189
+ && matches_ty ( receiver_ty, arg_ty, self_arg, other_arg)
182
190
{
183
191
true
184
192
} else {
0 commit comments