11use clippy_config:: Conf ;
22use clippy_utils:: diagnostics:: span_lint_and_then;
33use 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 ;
55use 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} ;
77use clippy_utils:: { DefinedTy , ExprUseNode , expr_use_ctxt, peel_n_hir_expr_refs} ;
88use rustc_errors:: Applicability ;
99use rustc_hir:: def:: { DefKind , Res } ;
1010use 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 } ;
1412use rustc_lint:: { LateContext , LateLintPass } ;
1513use 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 } ;
1915use 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 ;
2416
2517declare_clippy_lint ! {
2618 /// ### What it does
@@ -161,7 +153,7 @@ fn path_has_args(p: &QPath<'_>) -> bool {
161153/// - `Copy` itself, or
162154/// - the only use of a mutable reference, or
163155/// - not a variable (created by a function call)
164- #[ expect( clippy:: too_many_arguments, clippy :: too_many_lines ) ]
156+ #[ expect( clippy:: too_many_arguments) ]
165157fn needless_borrow_count < ' tcx > (
166158 cx : & LateContext < ' tcx > ,
167159 possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
@@ -172,122 +164,33 @@ fn needless_borrow_count<'tcx>(
172164 mut expr : & Expr < ' tcx > ,
173165 msrv : Msrv ,
174166) -> 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 {
227170 return 0 ;
228- }
171+ } ;
229172
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 ( ) ;
233174
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+ }
240182 }
241183
242184 let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
243185
244186 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 )
246188 || matches ! ( referent. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) ) )
247189 {
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 ;
266191 }
267192
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) {
291194 break ;
292195 }
293196 expr = referent;
@@ -296,56 +199,6 @@ fn needless_borrow_count<'tcx>(
296199 count
297200}
298201
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-
349202fn referent_used_exactly_once < ' tcx > (
350203 cx : & LateContext < ' tcx > ,
351204 possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
@@ -376,60 +229,3 @@ fn referent_used_exactly_once<'tcx>(
376229 false
377230 }
378231}
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