@@ -5,9 +5,9 @@ use clippy_utils::{
5
5
} ;
6
6
use rustc_errors:: Applicability ;
7
7
use 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 } ;
9
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
- use rustc_middle:: ty:: EarlyBinder ;
10
+ use rustc_middle:: ty:: { EarlyBinder , TraitRef } ;
11
11
use rustc_session:: declare_lint_pass;
12
12
use rustc_span:: sym;
13
13
use rustc_span:: symbol:: kw;
@@ -112,7 +112,6 @@ declare_clippy_lint! {
112
112
declare_lint_pass ! ( NonCanonicalImpls => [ NON_CANONICAL_CLONE_IMPL , NON_CANONICAL_PARTIAL_ORD_IMPL ] ) ;
113
113
114
114
impl LateLintPass < ' _ > for NonCanonicalImpls {
115
- #[ expect( clippy:: too_many_lines) ]
116
115
fn check_impl_item < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ImplItem < ' tcx > ) {
117
116
if let Node :: Item ( item) = cx. tcx . parent_hir_node ( impl_item. hir_id ( ) )
118
117
&& let Some ( trait_impl) = cx. tcx . impl_trait_ref ( item. owner_id ) . map ( EarlyBinder :: skip_binder)
@@ -128,114 +127,127 @@ impl LateLintPass<'_> for NonCanonicalImpls {
128
127
&& let Some ( copy_def_id) = cx. tcx . get_diagnostic_item ( sym:: Copy )
129
128
&& implements_trait ( cx, trait_impl. self_ty ( ) , copy_def_id, & [ ] )
130
129
{
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) ;
164
131
} else if trait_name == Some ( sym:: PartialOrd )
165
132
&& impl_item. ident . name == sym:: partial_cmp
166
133
&& let Some ( ord_def_id) = cx. tcx . get_diagnostic_item ( sym:: Ord )
167
134
&& implements_trait ( cx, trait_impl. self_ty ( ) , ord_def_id, & [ ] )
168
135
{
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
+ }
173
141
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
+ }
198
162
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
+ }
211
175
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`.
232
187
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 ;
238
212
}
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
+ ) ;
239
251
}
240
252
241
253
/// Return true if `expr_kind` is a `cmp` call.
0 commit comments