@@ -69,14 +69,6 @@ fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) {
6969 ) ;
7070}
7171
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-
8072fn get_hir_ty_def_id < ' tcx > ( tcx : TyCtxt < ' tcx > , hir_ty : rustc_hir:: Ty < ' tcx > ) -> Option < DefId > {
8173 let TyKind :: Path ( qpath) = hir_ty. kind else { return None } ;
8274 match qpath {
@@ -131,21 +123,49 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
131123 }
132124}
133125
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+
135152fn 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+
140161 // 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 ( )
144165 // The two arguments are of the same type.
145- && self_arg == other_arg
146166 && let Some ( trait_def_id) = get_impl_trait_def_id ( cx, method_def_id)
147167 // 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 )
149169 {
150170 let to_check_op = if name. name == sym:: eq {
151171 BinOpKind :: Eq
@@ -154,31 +174,19 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
154174 } ;
155175 let is_bad = match expr. kind {
156176 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)
169181 } ,
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+
179186 if let Some ( fn_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
180187 && let Some ( trait_id) = cx. tcx . trait_of_item ( fn_id)
181188 && trait_id == trait_def_id
189+ && matches_ty ( receiver_ty, arg_ty, self_arg, other_arg)
182190 {
183191 true
184192 } else {
0 commit comments