1
1
use clippy_config:: Conf ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_then;
3
3
use clippy_utils:: mir:: { PossibleBorrowerMap , enclosing_mir, expr_local, local_assignments, used_exactly_once} ;
4
- use clippy_utils:: msrvs:: { self , Msrv } ;
4
+ use clippy_utils:: msrvs:: Msrv ;
5
5
use clippy_utils:: source:: snippet_with_context;
6
- use clippy_utils:: ty:: { implements_trait, is_copy} ;
6
+ use clippy_utils:: ty:: { build_check_predicates_with_new_ty_closure , implements_trait, is_copy} ;
7
7
use clippy_utils:: { DefinedTy , ExprUseNode , expr_use_ctxt, peel_n_hir_expr_refs} ;
8
8
use rustc_errors:: Applicability ;
9
9
use rustc_hir:: def:: { DefKind , Res } ;
10
10
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
11
- use rustc_hir:: { Body , Expr , ExprKind , Mutability , Path , QPath } ;
12
- use rustc_index:: bit_set:: DenseBitSet ;
13
- use rustc_infer:: infer:: TyCtxtInferExt ;
11
+ use rustc_hir:: { Body , Expr , ExprKind , Path , QPath } ;
14
12
use rustc_lint:: { LateContext , LateLintPass } ;
15
13
use rustc_middle:: mir:: { Rvalue , StatementKind } ;
16
- use rustc_middle:: ty:: {
17
- self , ClauseKind , EarlyBinder , FnSig , GenericArg , GenericArgKind , ParamTy , ProjectionPredicate , Ty ,
18
- } ;
14
+ use rustc_middle:: ty:: { self , ParamTy } ;
19
15
use rustc_session:: impl_lint_pass;
20
- use rustc_span:: symbol:: sym;
21
- use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt as _;
22
- use rustc_trait_selection:: traits:: { Obligation , ObligationCause } ;
23
- use std:: collections:: VecDeque ;
24
16
25
17
declare_clippy_lint ! {
26
18
/// ### What it does
@@ -161,7 +153,7 @@ fn path_has_args(p: &QPath<'_>) -> bool {
161
153
/// - `Copy` itself, or
162
154
/// - the only use of a mutable reference, or
163
155
/// - not a variable (created by a function call)
164
- #[ expect( clippy:: too_many_arguments, clippy :: too_many_lines ) ]
156
+ #[ expect( clippy:: too_many_arguments) ]
165
157
fn needless_borrow_count < ' tcx > (
166
158
cx : & LateContext < ' tcx > ,
167
159
possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
@@ -172,122 +164,33 @@ fn needless_borrow_count<'tcx>(
172
164
mut expr : & Expr < ' tcx > ,
173
165
msrv : Msrv ,
174
166
) -> usize {
175
- let destruct_trait_def_id = cx. tcx . lang_items ( ) . destruct_trait ( ) ;
176
- let sized_trait_def_id = cx. tcx . lang_items ( ) . sized_trait ( ) ;
177
- let meta_sized_trait_def_id = cx. tcx . lang_items ( ) . meta_sized_trait ( ) ;
178
- let drop_trait_def_id = cx. tcx . lang_items ( ) . drop_trait ( ) ;
179
-
180
- let fn_sig = cx. tcx . fn_sig ( fn_id) . instantiate_identity ( ) . skip_binder ( ) ;
181
- let predicates = cx. tcx . param_env ( fn_id) . caller_bounds ( ) ;
182
- let projection_predicates = predicates
183
- . iter ( )
184
- . filter_map ( |predicate| {
185
- if let ClauseKind :: Projection ( projection_predicate) = predicate. kind ( ) . skip_binder ( ) {
186
- Some ( projection_predicate)
187
- } else {
188
- None
189
- }
190
- } )
191
- . collect :: < Vec < _ > > ( ) ;
192
-
193
- let mut trait_with_ref_mut_self_method = false ;
194
-
195
- // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
196
- if predicates
197
- . iter ( )
198
- . filter_map ( |predicate| {
199
- if let ClauseKind :: Trait ( trait_predicate) = predicate. kind ( ) . skip_binder ( )
200
- && trait_predicate. trait_ref . self_ty ( ) == param_ty. to_ty ( cx. tcx )
201
- {
202
- Some ( trait_predicate. trait_ref . def_id )
203
- } else {
204
- None
205
- }
206
- } )
207
- . inspect ( |trait_def_id| {
208
- trait_with_ref_mut_self_method |= has_ref_mut_self_method ( cx, * trait_def_id) ;
209
- } )
210
- . all ( |trait_def_id| {
211
- Some ( trait_def_id) == destruct_trait_def_id
212
- || Some ( trait_def_id) == sized_trait_def_id
213
- || Some ( trait_def_id) == meta_sized_trait_def_id
214
- || cx. tcx . is_diagnostic_item ( sym:: Any , trait_def_id)
215
- } )
216
- {
217
- return 0 ;
218
- }
219
-
220
- // See:
221
- // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
222
- // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
223
- if projection_predicates
224
- . iter ( )
225
- . any ( |projection_predicate| is_mixed_projection_predicate ( cx, fn_id, projection_predicate) )
226
- {
167
+ let Some ( mut check_referent_ty) =
168
+ build_check_predicates_with_new_ty_closure ( cx, fn_id, callee_args, arg_index, param_ty, true , msrv)
169
+ else {
227
170
return 0 ;
228
- }
171
+ } ;
229
172
230
- // `args_with_referent_ty` can be constructed outside of `check_referent` because the same
231
- // elements are modified each time `check_referent` is called.
232
- let mut args_with_referent_ty = callee_args. to_vec ( ) ;
173
+ let drop_trait_def_id = cx. tcx . lang_items ( ) . drop_trait ( ) ;
233
174
234
- let mut check_reference_and_referent = |reference : & Expr < ' tcx > , referent : & Expr < ' tcx > | {
235
- if let ExprKind :: Field ( base, _) = & referent. kind
236
- && let base_ty = cx. typeck_results ( ) . expr_ty ( base)
237
- && drop_trait_def_id. is_some_and ( |id| implements_trait ( cx, base_ty, id, & [ ] ) )
238
- {
239
- return false ;
175
+ let mut count = 0 ;
176
+ while let ExprKind :: AddrOf ( _, _, referent) = expr. kind {
177
+ if let ExprKind :: Field ( base, _) = & referent. kind {
178
+ let base_ty = cx. typeck_results ( ) . expr_ty ( base) ;
179
+ if drop_trait_def_id. is_some_and ( |id| implements_trait ( cx, base_ty, id, & [ ] ) ) {
180
+ break ;
181
+ }
240
182
}
241
183
242
184
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
243
185
244
186
if !( is_copy ( cx, referent_ty)
245
- || referent_ty. is_ref ( ) && referent_used_exactly_once ( cx, possible_borrowers, reference )
187
+ || referent_ty. is_ref ( ) && referent_used_exactly_once ( cx, possible_borrowers, expr )
246
188
|| matches ! ( referent. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) ) )
247
189
{
248
- return false ;
249
- }
250
-
251
- // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
252
- if trait_with_ref_mut_self_method && !matches ! ( referent_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) ) {
253
- return false ;
254
- }
255
-
256
- if !replace_types (
257
- cx,
258
- param_ty,
259
- referent_ty,
260
- fn_sig,
261
- arg_index,
262
- & projection_predicates,
263
- & mut args_with_referent_ty,
264
- ) {
265
- return false ;
190
+ break ;
266
191
}
267
192
268
- predicates. iter ( ) . all ( |predicate| {
269
- if let ClauseKind :: Trait ( trait_predicate) = predicate. kind ( ) . skip_binder ( )
270
- && cx
271
- . tcx
272
- . is_diagnostic_item ( sym:: IntoIterator , trait_predicate. trait_ref . def_id )
273
- && let ty:: Param ( param_ty) = trait_predicate. self_ty ( ) . kind ( )
274
- && let GenericArgKind :: Type ( ty) = args_with_referent_ty[ param_ty. index as usize ] . kind ( )
275
- && ty. is_array ( )
276
- && !msrv. meets ( cx, msrvs:: ARRAY_INTO_ITERATOR )
277
- {
278
- return false ;
279
- }
280
-
281
- let predicate = EarlyBinder :: bind ( predicate) . instantiate ( cx. tcx , & args_with_referent_ty[ ..] ) ;
282
- let obligation = Obligation :: new ( cx. tcx , ObligationCause :: dummy ( ) , cx. param_env , predicate) ;
283
- let infcx = cx. tcx . infer_ctxt ( ) . build ( cx. typing_mode ( ) ) ;
284
- infcx. predicate_must_hold_modulo_regions ( & obligation)
285
- } )
286
- } ;
287
-
288
- let mut count = 0 ;
289
- while let ExprKind :: AddrOf ( _, _, referent) = expr. kind {
290
- if !check_reference_and_referent ( expr, referent) {
193
+ if !check_referent_ty ( referent_ty) {
291
194
break ;
292
195
}
293
196
expr = referent;
@@ -296,56 +199,6 @@ fn needless_borrow_count<'tcx>(
296
199
count
297
200
}
298
201
299
- fn has_ref_mut_self_method ( cx : & LateContext < ' _ > , trait_def_id : DefId ) -> bool {
300
- cx. tcx
301
- . associated_items ( trait_def_id)
302
- . in_definition_order ( )
303
- . any ( |assoc_item| {
304
- if assoc_item. is_method ( ) {
305
- let self_ty = cx
306
- . tcx
307
- . fn_sig ( assoc_item. def_id )
308
- . instantiate_identity ( )
309
- . skip_binder ( )
310
- . inputs ( ) [ 0 ] ;
311
- matches ! ( self_ty. kind( ) , ty:: Ref ( _, _, Mutability :: Mut ) )
312
- } else {
313
- false
314
- }
315
- } )
316
- }
317
-
318
- fn is_mixed_projection_predicate < ' tcx > (
319
- cx : & LateContext < ' tcx > ,
320
- callee_def_id : DefId ,
321
- projection_predicate : & ProjectionPredicate < ' tcx > ,
322
- ) -> bool {
323
- let generics = cx. tcx . generics_of ( callee_def_id) ;
324
- // The predicate requires the projected type to equal a type parameter from the parent context.
325
- if let Some ( term_ty) = projection_predicate. term . as_type ( )
326
- && let ty:: Param ( term_param_ty) = term_ty. kind ( )
327
- && ( term_param_ty. index as usize ) < generics. parent_count
328
- {
329
- // The inner-most self type is a type parameter from the current function.
330
- let mut projection_term = projection_predicate. projection_term ;
331
- loop {
332
- match * projection_term. self_ty ( ) . kind ( ) {
333
- ty:: Alias ( ty:: Projection , inner_projection_ty) => {
334
- projection_term = inner_projection_ty. into ( ) ;
335
- } ,
336
- ty:: Param ( param_ty) => {
337
- return ( param_ty. index as usize ) >= generics. parent_count ;
338
- } ,
339
- _ => {
340
- return false ;
341
- } ,
342
- }
343
- }
344
- } else {
345
- false
346
- }
347
- }
348
-
349
202
fn referent_used_exactly_once < ' tcx > (
350
203
cx : & LateContext < ' tcx > ,
351
204
possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
@@ -376,60 +229,3 @@ fn referent_used_exactly_once<'tcx>(
376
229
false
377
230
}
378
231
}
379
-
380
- // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
381
- // projected type that is a type parameter. Returns `false` if replacing the types would have an
382
- // effect on the function signature beyond substituting `new_ty` for `param_ty`.
383
- // See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
384
- fn replace_types < ' tcx > (
385
- cx : & LateContext < ' tcx > ,
386
- param_ty : ParamTy ,
387
- new_ty : Ty < ' tcx > ,
388
- fn_sig : FnSig < ' tcx > ,
389
- arg_index : usize ,
390
- projection_predicates : & [ ProjectionPredicate < ' tcx > ] ,
391
- args : & mut [ GenericArg < ' tcx > ] ,
392
- ) -> bool {
393
- let mut replaced = DenseBitSet :: new_empty ( args. len ( ) ) ;
394
-
395
- let mut deque = VecDeque :: with_capacity ( args. len ( ) ) ;
396
- deque. push_back ( ( param_ty, new_ty) ) ;
397
-
398
- while let Some ( ( param_ty, new_ty) ) = deque. pop_front ( ) {
399
- // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
400
- if !fn_sig
401
- . inputs_and_output
402
- . iter ( )
403
- . enumerate ( )
404
- . all ( |( i, ty) | ( replaced. is_empty ( ) && i == arg_index) || !ty. contains ( param_ty. to_ty ( cx. tcx ) ) )
405
- {
406
- return false ;
407
- }
408
-
409
- args[ param_ty. index as usize ] = GenericArg :: from ( new_ty) ;
410
-
411
- // The `replaced.insert(...)` check provides some protection against infinite loops.
412
- if replaced. insert ( param_ty. index ) {
413
- for projection_predicate in projection_predicates {
414
- if projection_predicate. projection_term . self_ty ( ) == param_ty. to_ty ( cx. tcx )
415
- && let Some ( term_ty) = projection_predicate. term . as_type ( )
416
- && let ty:: Param ( term_param_ty) = term_ty. kind ( )
417
- {
418
- let projection = projection_predicate
419
- . projection_term
420
- . with_replaced_self_ty ( cx. tcx , new_ty)
421
- . expect_ty ( cx. tcx )
422
- . to_ty ( cx. tcx ) ;
423
-
424
- if let Ok ( projected_ty) = cx. tcx . try_normalize_erasing_regions ( cx. typing_env ( ) , projection)
425
- && args[ term_param_ty. index as usize ] != GenericArg :: from ( projected_ty)
426
- {
427
- deque. push_back ( ( * term_param_ty, projected_ty) ) ;
428
- }
429
- }
430
- }
431
- }
432
- }
433
-
434
- true
435
- }
0 commit comments