@@ -5,9 +5,9 @@ use clippy_utils::{
55} ;
66use rustc_errors:: Applicability ;
77use rustc_hir:: def_id:: LocalDefId ;
8- use rustc_hir:: { Expr , ExprKind , ImplItem , ImplItemKind , LangItem , Node , UnOp } ;
8+ use rustc_hir:: { Block , Body , Expr , ExprKind , ImplItem , ImplItemKind , Item , LangItem , Node , UnOp } ;
99use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10- use rustc_middle:: ty:: EarlyBinder ;
10+ use rustc_middle:: ty:: { EarlyBinder , TraitRef } ;
1111use rustc_session:: declare_lint_pass;
1212use rustc_span:: sym;
1313use rustc_span:: symbol:: kw;
@@ -112,7 +112,6 @@ declare_clippy_lint! {
112112declare_lint_pass ! ( NonCanonicalImpls => [ NON_CANONICAL_CLONE_IMPL , NON_CANONICAL_PARTIAL_ORD_IMPL ] ) ;
113113
114114impl LateLintPass < ' _ > for NonCanonicalImpls {
115- #[ expect( clippy:: too_many_lines) ]
116115 fn check_impl_item < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ImplItem < ' tcx > ) {
117116 if let Node :: Item ( item) = cx. tcx . parent_hir_node ( impl_item. hir_id ( ) )
118117 && let Some ( trait_impl) = cx. tcx . impl_trait_ref ( item. owner_id ) . map ( EarlyBinder :: skip_binder)
@@ -128,114 +127,127 @@ impl LateLintPass<'_> for NonCanonicalImpls {
128127 && let Some ( copy_def_id) = cx. tcx . get_diagnostic_item ( sym:: Copy )
129128 && implements_trait ( cx, trait_impl. self_ty ( ) , copy_def_id, & [ ] )
130129 {
131- if impl_item. ident . name == sym:: clone {
132- if block. stmts . is_empty ( )
133- && let Some ( expr) = block. expr
134- && let ExprKind :: Unary ( UnOp :: Deref , deref) = expr. kind
135- && let ExprKind :: Path ( qpath) = deref. kind
136- && last_path_segment ( & qpath) . ident . name == kw:: SelfLower
137- {
138- } else {
139- span_lint_and_sugg (
140- cx,
141- NON_CANONICAL_CLONE_IMPL ,
142- block. span ,
143- "non-canonical implementation of `clone` on a `Copy` type" ,
144- "change this to" ,
145- "{ *self }" . to_owned ( ) ,
146- Applicability :: MaybeIncorrect ,
147- ) ;
148-
149- return ;
150- }
151- }
152-
153- if impl_item. ident . name == sym:: clone_from {
154- span_lint_and_sugg (
155- cx,
156- NON_CANONICAL_CLONE_IMPL ,
157- impl_item. span ,
158- "unnecessary implementation of `clone_from` on a `Copy` type" ,
159- "remove it" ,
160- String :: new ( ) ,
161- Applicability :: MaybeIncorrect ,
162- ) ;
163- }
130+ check_clone_on_copy ( cx, impl_item, block) ;
164131 } else if trait_name == Some ( sym:: PartialOrd )
165132 && impl_item. ident . name == sym:: partial_cmp
166133 && let Some ( ord_def_id) = cx. tcx . get_diagnostic_item ( sym:: Ord )
167134 && implements_trait ( cx, trait_impl. self_ty ( ) , ord_def_id, & [ ] )
168135 {
169- // If the `cmp` call likely needs to be fully qualified in the suggestion
170- // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
171- // access `cmp_expr` in the suggestion without major changes, as we lint in `else`.
172- let mut needs_fully_qualified = false ;
136+ check_partial_ord_on_ord ( cx, impl_item, item, & trait_impl, body, block) ;
137+ }
138+ }
139+ }
140+ }
173141
174- if block. stmts . is_empty ( )
175- && let Some ( expr) = block. expr
176- && expr_is_cmp ( cx, expr, impl_item, & mut needs_fully_qualified)
177- {
178- return ;
179- }
180- // Fix #12683, allow [`needless_return`] here
181- else if block. expr . is_none ( )
182- && let Some ( stmt) = block. stmts . first ( )
183- && let rustc_hir:: StmtKind :: Semi ( Expr {
184- kind : ExprKind :: Ret ( Some ( ret) ) ,
185- ..
186- } ) = stmt. kind
187- && expr_is_cmp ( cx, ret, impl_item, & mut needs_fully_qualified)
188- {
189- return ;
190- }
191- // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
192- // suggestion tons more complex.
193- else if let [ lhs, rhs, ..] = trait_impl. args . as_slice ( )
194- && lhs != rhs
195- {
196- return ;
197- }
142+ fn check_clone_on_copy ( cx : & LateContext < ' _ > , impl_item : & ImplItem < ' _ > , block : & Block < ' _ > ) {
143+ if impl_item. ident . name == sym:: clone {
144+ if block. stmts . is_empty ( )
145+ && let Some ( expr) = block. expr
146+ && let ExprKind :: Unary ( UnOp :: Deref , deref) = expr. kind
147+ && let ExprKind :: Path ( qpath) = deref. kind
148+ && last_path_segment ( & qpath) . ident . name == kw:: SelfLower
149+ {
150+ } else {
151+ span_lint_and_sugg (
152+ cx,
153+ NON_CANONICAL_CLONE_IMPL ,
154+ block. span ,
155+ "non-canonical implementation of `clone` on a `Copy` type" ,
156+ "change this to" ,
157+ "{ *self }" . to_owned ( ) ,
158+ Applicability :: MaybeIncorrect ,
159+ ) ;
160+ }
161+ }
198162
199- span_lint_and_then (
200- cx ,
201- NON_CANONICAL_PARTIAL_ORD_IMPL ,
202- item . span ,
203- "non-canonical implementation of `partial_cmp` on an `Ord` type" ,
204- |diag| {
205- let [ _ , other ] = body . params else {
206- return ;
207- } ;
208- let Some ( std_or_core ) = std_or_core ( cx ) else {
209- return ;
210- } ;
163+ if impl_item . ident . name == sym :: clone_from {
164+ span_lint_and_sugg (
165+ cx ,
166+ NON_CANONICAL_CLONE_IMPL ,
167+ impl_item . span ,
168+ "unnecessary implementation of `clone_from` on a `Copy` type" ,
169+ "remove it" ,
170+ String :: new ( ) ,
171+ Applicability :: MaybeIncorrect ,
172+ ) ;
173+ }
174+ }
211175
212- let suggs = match ( other. pat . simple_ident ( ) , needs_fully_qualified) {
213- ( Some ( other_ident) , true ) => vec ! [ (
214- block. span,
215- format!( "{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}" , other_ident. name) ,
216- ) ] ,
217- ( Some ( other_ident) , false ) => {
218- vec ! [ ( block. span, format!( "{{ Some(self.cmp({})) }}" , other_ident. name) ) ]
219- } ,
220- ( None , true ) => vec ! [
221- (
222- block. span,
223- format!( "{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}" ) ,
224- ) ,
225- ( other. pat. span, "other" . to_owned( ) ) ,
226- ] ,
227- ( None , false ) => vec ! [
228- ( block. span, "{ Some(self.cmp(other)) }" . to_owned( ) ) ,
229- ( other. pat. span, "other" . to_owned( ) ) ,
230- ] ,
231- } ;
176+ fn check_partial_ord_on_ord < ' tcx > (
177+ cx : & LateContext < ' tcx > ,
178+ impl_item : & ImplItem < ' _ > ,
179+ item : & Item < ' _ > ,
180+ trait_impl : & TraitRef < ' _ > ,
181+ body : & Body < ' _ > ,
182+ block : & Block < ' tcx > ,
183+ ) {
184+ // If the `cmp` call likely needs to be fully qualified in the suggestion
185+ // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
186+ // access `cmp_expr` in the suggestion without major changes, as we lint in `else`.
232187
233- diag. multipart_suggestion ( "change this to" , suggs, Applicability :: Unspecified ) ;
234- } ,
235- ) ;
236- }
237- }
188+ let mut needs_fully_qualified = false ;
189+ if block. stmts . is_empty ( )
190+ && let Some ( expr) = block. expr
191+ && expr_is_cmp ( cx, expr, impl_item, & mut needs_fully_qualified)
192+ {
193+ return ;
194+ }
195+ // Fix #12683, allow [`needless_return`] here
196+ else if block. expr . is_none ( )
197+ && let Some ( stmt) = block. stmts . first ( )
198+ && let rustc_hir:: StmtKind :: Semi ( Expr {
199+ kind : ExprKind :: Ret ( Some ( ret) ) ,
200+ ..
201+ } ) = stmt. kind
202+ && expr_is_cmp ( cx, ret, impl_item, & mut needs_fully_qualified)
203+ {
204+ return ;
205+ }
206+ // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
207+ // suggestion tons more complex.
208+ else if let [ lhs, rhs, ..] = trait_impl. args . as_slice ( )
209+ && lhs != rhs
210+ {
211+ return ;
238212 }
213+
214+ span_lint_and_then (
215+ cx,
216+ NON_CANONICAL_PARTIAL_ORD_IMPL ,
217+ item. span ,
218+ "non-canonical implementation of `partial_cmp` on an `Ord` type" ,
219+ |diag| {
220+ let [ _, other] = body. params else {
221+ return ;
222+ } ;
223+ let Some ( std_or_core) = std_or_core ( cx) else {
224+ return ;
225+ } ;
226+
227+ let suggs = match ( other. pat . simple_ident ( ) , needs_fully_qualified) {
228+ ( Some ( other_ident) , true ) => vec ! [ (
229+ block. span,
230+ format!( "{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}" , other_ident. name) ,
231+ ) ] ,
232+ ( Some ( other_ident) , false ) => {
233+ vec ! [ ( block. span, format!( "{{ Some(self.cmp({})) }}" , other_ident. name) ) ]
234+ } ,
235+ ( None , true ) => vec ! [
236+ (
237+ block. span,
238+ format!( "{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}" ) ,
239+ ) ,
240+ ( other. pat. span, "other" . to_owned( ) ) ,
241+ ] ,
242+ ( None , false ) => vec ! [
243+ ( block. span, "{ Some(self.cmp(other)) }" . to_owned( ) ) ,
244+ ( other. pat. span, "other" . to_owned( ) ) ,
245+ ] ,
246+ } ;
247+
248+ diag. multipart_suggestion ( "change this to" , suggs, Applicability :: Unspecified ) ;
249+ } ,
250+ ) ;
239251}
240252
241253/// Return true if `expr_kind` is a `cmp` call.
0 commit comments