@@ -3,10 +3,11 @@ use clippy_utils::res::{MaybeDef, MaybeQPath};
33use clippy_utils:: ty:: implements_trait;
44use clippy_utils:: { is_from_proc_macro, last_path_segment, std_or_core} ;
55use rustc_errors:: Applicability ;
6- use rustc_hir:: { Block , Body , Expr , ExprKind , ImplItem , ImplItemKind , Item , LangItem , Node , UnOp } ;
6+ use rustc_hir:: def_id:: DefId ;
7+ use rustc_hir:: { Block , Body , Expr , ExprKind , ImplItem , ImplItemKind , Item , ItemKind , LangItem , UnOp } ;
78use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
8- use rustc_middle:: ty:: { EarlyBinder , TraitRef , TypeckResults } ;
9- use rustc_session:: declare_lint_pass ;
9+ use rustc_middle:: ty:: { EarlyBinder , TyCtxt , TypeckResults } ;
10+ use rustc_session:: impl_lint_pass ;
1011use rustc_span:: sym;
1112use rustc_span:: symbol:: kw;
1213
@@ -107,33 +108,96 @@ declare_clippy_lint! {
107108 suspicious,
108109 "non-canonical implementation of `PartialOrd` on an `Ord` type"
109110}
110- declare_lint_pass ! ( NonCanonicalImpls => [ NON_CANONICAL_CLONE_IMPL , NON_CANONICAL_PARTIAL_ORD_IMPL ] ) ;
111+ impl_lint_pass ! ( NonCanonicalImpls => [ NON_CANONICAL_CLONE_IMPL , NON_CANONICAL_PARTIAL_ORD_IMPL ] ) ;
112+
113+ #[ expect(
114+ clippy:: struct_field_names,
115+ reason = "`_trait` suffix is meaningful on its own, \
116+ and creating an inner `StoredTraits` struct would just add a level of indirection"
117+ ) ]
118+ pub ( crate ) struct NonCanonicalImpls {
119+ partial_ord_trait : Option < DefId > ,
120+ ord_trait : Option < DefId > ,
121+ clone_trait : Option < DefId > ,
122+ copy_trait : Option < DefId > ,
123+ }
124+
125+ impl NonCanonicalImpls {
126+ pub ( crate ) fn new ( tcx : TyCtxt < ' _ > ) -> Self {
127+ let lang_items = tcx. lang_items ( ) ;
128+ Self {
129+ partial_ord_trait : lang_items. partial_ord_trait ( ) ,
130+ ord_trait : tcx. get_diagnostic_item ( sym:: Ord ) ,
131+ clone_trait : lang_items. clone_trait ( ) ,
132+ copy_trait : lang_items. copy_trait ( ) ,
133+ }
134+ }
135+ }
136+
137+ /// The traits that this lint looks at
138+ enum Trait {
139+ Clone ,
140+ PartialOrd ,
141+ }
111142
112143impl LateLintPass < ' _ > for NonCanonicalImpls {
113- fn check_impl_item < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ImplItem < ' tcx > ) {
114- if let ImplItemKind :: Fn ( _, impl_item_id) = impl_item. kind
115- && let Node :: Item ( item) = cx. tcx . parent_hir_node ( impl_item. hir_id ( ) )
116- && let Some ( trait_impl) = cx. tcx . impl_trait_ref ( item. owner_id ) . map ( EarlyBinder :: skip_binder)
117- && let trait_name = cx. tcx . get_diagnostic_name ( trait_impl. def_id )
118- // NOTE: check this early to avoid expensive checks that come after this one
119- && matches ! ( trait_name, Some ( sym:: Clone | sym:: PartialOrd ) )
144+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
145+ if let ItemKind :: Impl ( impl_) = item. kind
146+ // Both `PartialOrd` and `Clone` have one required method, and `PartialOrd` can have 5 methods in total
147+ && ( 1 ..=5 ) . contains ( & impl_. items . len ( ) )
148+ && let Some ( of_trait) = impl_. of_trait
149+ && let Some ( trait_did) = of_trait. trait_ref . trait_def_id ( )
150+ // Check this early to hopefully bail out as soon as possible
151+ && let trait_ = if Some ( trait_did) == self . clone_trait {
152+ Trait :: Clone
153+ } else if Some ( trait_did) == self . partial_ord_trait {
154+ Trait :: PartialOrd
155+ } else {
156+ return ;
157+ }
120158 && !cx. tcx . is_automatically_derived ( item. owner_id . to_def_id ( ) )
121- && let body = cx. tcx . hir_body ( impl_item_id)
122- && let ExprKind :: Block ( block, ..) = body. value . kind
123- && !block. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
124- && !is_from_proc_macro ( cx, impl_item)
125159 {
126- if trait_name == Some ( sym:: Clone )
127- && let Some ( copy_def_id) = cx. tcx . get_diagnostic_item ( sym:: Copy )
128- && implements_trait ( cx, trait_impl. self_ty ( ) , copy_def_id, & [ ] )
129- {
130- check_clone_on_copy ( cx, impl_item, block) ;
131- } else if trait_name == Some ( sym:: PartialOrd )
132- && impl_item. ident . name == sym:: partial_cmp
133- && let Some ( ord_def_id) = cx. tcx . get_diagnostic_item ( sym:: Ord )
134- && implements_trait ( cx, trait_impl. self_ty ( ) , ord_def_id, & [ ] )
135- {
136- check_partial_ord_on_ord ( cx, impl_item, item, & trait_impl, body, block) ;
160+ let mut assoc_fns = impl_
161+ . items
162+ . iter ( )
163+ . map ( |id| cx. tcx . hir_impl_item ( * id) )
164+ . filter_map ( |assoc| {
165+ if let ImplItemKind :: Fn ( _, body_id) = assoc. kind
166+ && let body = cx. tcx . hir_body ( body_id)
167+ && let ExprKind :: Block ( block, ..) = body. value . kind
168+ && !block. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
169+ {
170+ Some ( ( assoc, body, block) )
171+ } else {
172+ None
173+ }
174+ } ) ;
175+
176+ match trait_ {
177+ Trait :: Clone => {
178+ if let Some ( copy_trait) = self . copy_trait
179+ && let Some ( trait_impl) = cx. tcx . impl_trait_ref ( item. owner_id ) . map ( EarlyBinder :: skip_binder)
180+ && implements_trait ( cx, trait_impl. self_ty ( ) , copy_trait, & [ ] )
181+ {
182+ for ( assoc, _, block) in assoc_fns {
183+ check_clone_on_copy ( cx, assoc, block) ;
184+ }
185+ }
186+ } ,
187+ Trait :: PartialOrd => {
188+ if let Some ( trait_impl) = cx. tcx . impl_trait_ref ( item. owner_id ) . map ( EarlyBinder :: skip_binder)
189+ // If `Self` and `Rhs` are not the same type, then a corresponding `Ord` impl is not possible,
190+ // since it doesn't have an `Rhs`
191+ && let [ lhs, rhs] = trait_impl. args . as_slice ( )
192+ && lhs == rhs
193+ && let Some ( ord_trait) = self . ord_trait
194+ && implements_trait ( cx, trait_impl. self_ty ( ) , ord_trait, & [ ] )
195+ && let Some ( ( assoc, body, block) ) =
196+ assoc_fns. find ( |( assoc, _, _) | assoc. ident . name == sym:: partial_cmp)
197+ {
198+ check_partial_ord_on_ord ( cx, assoc, item, body, block) ;
199+ }
200+ } ,
137201 }
138202 }
139203 }
@@ -151,6 +215,10 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B
151215 return ;
152216 }
153217
218+ if is_from_proc_macro ( cx, impl_item) {
219+ return ;
220+ }
221+
154222 span_lint_and_sugg (
155223 cx,
156224 NON_CANONICAL_CLONE_IMPL ,
@@ -162,7 +230,7 @@ fn check_clone_on_copy(cx: &LateContext<'_>, impl_item: &ImplItem<'_>, block: &B
162230 ) ;
163231 }
164232
165- if impl_item. ident . name == sym:: clone_from {
233+ if impl_item. ident . name == sym:: clone_from && ! is_from_proc_macro ( cx , impl_item ) {
166234 span_lint_and_sugg (
167235 cx,
168236 NON_CANONICAL_CLONE_IMPL ,
@@ -179,7 +247,6 @@ fn check_partial_ord_on_ord<'tcx>(
179247 cx : & LateContext < ' tcx > ,
180248 impl_item : & ImplItem < ' _ > ,
181249 item : & Item < ' _ > ,
182- trait_impl : & TraitRef < ' _ > ,
183250 body : & Body < ' _ > ,
184251 block : & Block < ' tcx > ,
185252) {
@@ -204,12 +271,7 @@ fn check_partial_ord_on_ord<'tcx>(
204271 && expr_is_cmp ( cx, ret, impl_item, & mut needs_fully_qualified)
205272 {
206273 return ;
207- }
208- // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
209- // suggestion tons more complex.
210- else if let [ lhs, rhs, ..] = trait_impl. args . as_slice ( )
211- && lhs != rhs
212- {
274+ } else if is_from_proc_macro ( cx, impl_item) {
213275 return ;
214276 }
215277
0 commit comments