11use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
2- use clippy_utils:: mir:: {
3- dropped_without_further_use, enclosing_mir, expr_local, local_assignments, PossibleBorrowerMap ,
4- } ;
2+ use clippy_utils:: mir:: { enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap } ;
53use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
64use clippy_utils:: sugg:: has_enclosing_paren;
75use clippy_utils:: ty:: { expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res} ;
@@ -14,21 +12,24 @@ use rustc_data_structures::fx::FxIndexMap;
1412use rustc_errors:: Applicability ;
1513use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
1614use rustc_hir:: {
17- self as hir, def_id:: DefId , BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy ,
18- GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind ,
19- Path , QPath , TraitItem , TraitItemKind , TyKind , UnOp ,
15+ self as hir,
16+ def_id:: { DefId , LocalDefId } ,
17+ BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy , GenericArg , HirId , ImplItem ,
18+ ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath , TraitItem ,
19+ TraitItemKind , TyKind , UnOp ,
2020} ;
2121use rustc_index:: bit_set:: BitSet ;
2222use rustc_infer:: infer:: TyCtxtInferExt ;
23- use rustc_lint:: { LateContext , LateLintPass } ;
23+ use rustc_lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
24+ use rustc_lint_defs:: lint_array;
2425use rustc_middle:: mir:: { Rvalue , StatementKind } ;
2526use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
2627use rustc_middle:: ty:: {
2728 self , Binder , BoundVariableKind , EarlyBinder , FnSig , GenericArgKind , List , ParamTy , PredicateKind ,
2829 ProjectionPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults ,
2930} ;
3031use rustc_semver:: RustcVersion ;
31- use rustc_session:: { declare_tool_lint, impl_lint_pass } ;
32+ use rustc_session:: declare_tool_lint;
3233use rustc_span:: { symbol:: sym, Span , Symbol , DUMMY_SP } ;
3334use rustc_trait_selection:: infer:: InferCtxtExt as _;
3435use rustc_trait_selection:: traits:: { query:: evaluate_obligation:: InferCtxtExt as _, Obligation , ObligationCause } ;
@@ -145,15 +146,27 @@ declare_clippy_lint! {
145146 "dereferencing when the compiler would automatically dereference"
146147}
147148
148- impl_lint_pass ! ( Dereferencing => [
149- EXPLICIT_DEREF_METHODS ,
150- NEEDLESS_BORROW ,
151- REF_BINDING_TO_REFERENCE ,
152- EXPLICIT_AUTO_DEREF ,
153- ] ) ;
149+ #[ expect( rustc:: internal) ]
150+ impl < ' tcx > LintPass for Dereferencing < ' tcx > {
151+ fn name ( & self ) -> & ' static str {
152+ stringify ! ( $ty)
153+ }
154+ }
155+
156+ impl < ' tcx > Dereferencing < ' tcx > {
157+ #[ expect( dead_code) ]
158+ pub fn get_lints ( ) -> LintArray {
159+ lint_array ! (
160+ EXPLICIT_DEREF_METHODS ,
161+ NEEDLESS_BORROW ,
162+ REF_BINDING_TO_REFERENCE ,
163+ EXPLICIT_AUTO_DEREF ,
164+ )
165+ }
166+ }
154167
155168#[ derive( Default ) ]
156- pub struct Dereferencing {
169+ pub struct Dereferencing < ' tcx > {
157170 state : Option < ( State , StateData ) > ,
158171
159172 // While parsing a `deref` method call in ufcs form, the path to the function is itself an
@@ -174,11 +187,15 @@ pub struct Dereferencing {
174187 /// e.g. `m!(x) | Foo::Bar(ref x)`
175188 ref_locals : FxIndexMap < HirId , Option < RefPat > > ,
176189
190+ /// Map from body owners to `PossibleBorrowerMap`s. Used by `needless_borrow_impl_arg_position`
191+ /// to determine when a borrowed expression can instead be moved.
192+ possible_borrowers : FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
193+
177194 // `IntoIterator` for arrays requires Rust 1.53.
178195 msrv : Option < RustcVersion > ,
179196}
180197
181- impl Dereferencing {
198+ impl < ' tcx > Dereferencing < ' tcx > {
182199 #[ must_use]
183200 pub fn new ( msrv : Option < RustcVersion > ) -> Self {
184201 Self {
@@ -248,7 +265,7 @@ struct RefPat {
248265 hir_id : HirId ,
249266}
250267
251- impl < ' tcx > LateLintPass < ' tcx > for Dereferencing {
268+ impl < ' tcx > LateLintPass < ' tcx > for Dereferencing < ' tcx > {
252269 #[ expect( clippy:: too_many_lines) ]
253270 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
254271 // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
@@ -282,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
282299 match ( self . state . take ( ) , kind) {
283300 ( None , kind) => {
284301 let expr_ty = typeck. expr_ty ( expr) ;
285- let ( position, adjustments) = walk_parents ( cx, expr, self . msrv ) ;
302+ let ( position, adjustments) = walk_parents ( cx, & mut self . possible_borrowers , expr, self . msrv ) ;
286303 match kind {
287304 RefOp :: Deref => {
288305 if let Position :: FieldAccess {
@@ -686,6 +703,7 @@ impl Position {
686703#[ expect( clippy:: too_many_lines) ]
687704fn walk_parents < ' tcx > (
688705 cx : & LateContext < ' tcx > ,
706+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
689707 e : & ' tcx Expr < ' _ > ,
690708 msrv : Option < RustcVersion > ,
691709) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
@@ -800,7 +818,16 @@ fn walk_parents<'tcx>(
800818 Some ( hir_ty) => binding_ty_auto_deref_stability ( cx, hir_ty, precedence, ty. bound_vars ( ) ) ,
801819 None => {
802820 if let ty:: Param ( param_ty) = ty. skip_binder ( ) . kind ( ) {
803- needless_borrow_impl_arg_position ( cx, parent, i, * param_ty, e, precedence, msrv)
821+ needless_borrow_impl_arg_position (
822+ cx,
823+ possible_borrowers,
824+ parent,
825+ i,
826+ * param_ty,
827+ e,
828+ precedence,
829+ msrv,
830+ )
804831 } else {
805832 ty_auto_deref_stability ( cx, cx. tcx . erase_late_bound_regions ( ty) , precedence)
806833 . position_for_arg ( )
@@ -848,7 +875,16 @@ fn walk_parents<'tcx>(
848875 args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
849876 let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i + 1 ] ;
850877 if let ty:: Param ( param_ty) = ty. kind ( ) {
851- needless_borrow_impl_arg_position ( cx, parent, i + 1 , * param_ty, e, precedence, msrv)
878+ needless_borrow_impl_arg_position (
879+ cx,
880+ possible_borrowers,
881+ parent,
882+ i + 1 ,
883+ * param_ty,
884+ e,
885+ precedence,
886+ msrv,
887+ )
852888 } else {
853889 ty_auto_deref_stability (
854890 cx,
@@ -1022,8 +1058,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
10221058// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
10231059// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
10241060// be moved, but it cannot be.
1061+ #[ expect( clippy:: too_many_arguments) ]
10251062fn needless_borrow_impl_arg_position < ' tcx > (
10261063 cx : & LateContext < ' tcx > ,
1064+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
10271065 parent : & Expr < ' tcx > ,
10281066 arg_index : usize ,
10291067 param_ty : ParamTy ,
@@ -1082,8 +1120,6 @@ fn needless_borrow_impl_arg_position<'tcx>(
10821120 return Position :: Other ( precedence) ;
10831121 }
10841122
1085- let mut possible_borrower = None ;
1086-
10871123 // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
10881124 // elements are modified each time `check_referent` is called.
10891125 let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
@@ -1093,7 +1129,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
10931129
10941130 if !is_copy ( cx, referent_ty)
10951131 && ( referent_ty. has_significant_drop ( cx. tcx , cx. param_env )
1096- || !referent_dropped_without_further_use ( cx, & mut possible_borrower , reference) )
1132+ || !referent_used_exactly_once ( cx, possible_borrowers , reference) )
10971133 {
10981134 return false ;
10991135 }
@@ -1164,9 +1200,9 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
11641200 } )
11651201}
11661202
1167- fn referent_dropped_without_further_use < ' a , ' tcx > (
1203+ fn referent_used_exactly_once < ' a , ' tcx > (
11681204 cx : & ' a LateContext < ' tcx > ,
1169- possible_borrower : & mut Option < PossibleBorrowerMap < ' a , ' tcx > > ,
1205+ possible_borrower : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
11701206 reference : & Expr < ' tcx > ,
11711207) -> bool {
11721208 let mir = enclosing_mir ( cx. tcx , reference. hir_id ) ;
@@ -1176,12 +1212,15 @@ fn referent_dropped_without_further_use<'a, 'tcx>(
11761212 mir. basic_blocks [ location. block ] . statements [ location. statement_index ] . kind
11771213 && !place. has_deref ( )
11781214 {
1179- let possible_borrower = possible_borrower. get_or_insert_with ( || PossibleBorrowerMap :: new ( cx, mir) ) ;
1215+ let body_owner_local_def_id = cx. tcx . hir ( ) . enclosing_body_owner ( reference. hir_id ) ;
1216+ let possible_borrower = possible_borrower
1217+ . entry ( body_owner_local_def_id)
1218+ . or_insert_with ( || PossibleBorrowerMap :: new ( cx, mir) ) ;
11801219 // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
11811220 // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
11821221 // itself. See the comment in that method for an explanation as to why.
11831222 possible_borrower. bounded_borrowers ( & [ local] , & [ local, place. local ] , place. local , location)
1184- && dropped_without_further_use ( mir, place. local , location ) . unwrap_or ( false )
1223+ && used_exactly_once ( mir, place. local ) . unwrap_or ( false )
11851224 } else {
11861225 false
11871226 }
@@ -1471,8 +1510,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
14711510 }
14721511}
14731512
1474- impl Dereferencing {
1475- fn check_local_usage < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1513+ impl < ' tcx > Dereferencing < ' tcx > {
1514+ fn check_local_usage ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
14761515 if let Some ( outer_pat) = self . ref_locals . get_mut ( & local) {
14771516 if let Some ( pat) = outer_pat {
14781517 // Check for auto-deref
0 commit comments