1
1
use 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 } ;
5
3
use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
6
4
use clippy_utils:: sugg:: has_enclosing_paren;
7
5
use 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;
14
12
use rustc_errors:: Applicability ;
15
13
use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
16
14
use 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 ,
20
20
} ;
21
21
use rustc_index:: bit_set:: BitSet ;
22
22
use 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;
24
25
use rustc_middle:: mir:: { Rvalue , StatementKind } ;
25
26
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
26
27
use rustc_middle:: ty:: {
27
28
self , Binder , BoundVariableKind , EarlyBinder , FnSig , GenericArgKind , List , ParamTy , PredicateKind ,
28
29
ProjectionPredicate , Ty , TyCtxt , TypeVisitable , TypeckResults ,
29
30
} ;
30
31
use rustc_semver:: RustcVersion ;
31
- use rustc_session:: { declare_tool_lint, impl_lint_pass } ;
32
+ use rustc_session:: declare_tool_lint;
32
33
use rustc_span:: { symbol:: sym, Span , Symbol , DUMMY_SP } ;
33
34
use rustc_trait_selection:: infer:: InferCtxtExt as _;
34
35
use rustc_trait_selection:: traits:: { query:: evaluate_obligation:: InferCtxtExt as _, Obligation , ObligationCause } ;
@@ -145,15 +146,27 @@ declare_clippy_lint! {
145
146
"dereferencing when the compiler would automatically dereference"
146
147
}
147
148
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
+ }
154
167
155
168
#[ derive( Default ) ]
156
- pub struct Dereferencing {
169
+ pub struct Dereferencing < ' tcx > {
157
170
state : Option < ( State , StateData ) > ,
158
171
159
172
// While parsing a `deref` method call in ufcs form, the path to the function is itself an
@@ -174,11 +187,15 @@ pub struct Dereferencing {
174
187
/// e.g. `m!(x) | Foo::Bar(ref x)`
175
188
ref_locals : FxIndexMap < HirId , Option < RefPat > > ,
176
189
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
+
177
194
// `IntoIterator` for arrays requires Rust 1.53.
178
195
msrv : Option < RustcVersion > ,
179
196
}
180
197
181
- impl Dereferencing {
198
+ impl < ' tcx > Dereferencing < ' tcx > {
182
199
#[ must_use]
183
200
pub fn new ( msrv : Option < RustcVersion > ) -> Self {
184
201
Self {
@@ -248,7 +265,7 @@ struct RefPat {
248
265
hir_id : HirId ,
249
266
}
250
267
251
- impl < ' tcx > LateLintPass < ' tcx > for Dereferencing {
268
+ impl < ' tcx > LateLintPass < ' tcx > for Dereferencing < ' tcx > {
252
269
#[ expect( clippy:: too_many_lines) ]
253
270
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
254
271
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
@@ -282,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
282
299
match ( self . state . take ( ) , kind) {
283
300
( None , kind) => {
284
301
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 ) ;
286
303
match kind {
287
304
RefOp :: Deref => {
288
305
if let Position :: FieldAccess {
@@ -686,6 +703,7 @@ impl Position {
686
703
#[ expect( clippy:: too_many_lines) ]
687
704
fn walk_parents < ' tcx > (
688
705
cx : & LateContext < ' tcx > ,
706
+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
689
707
e : & ' tcx Expr < ' _ > ,
690
708
msrv : Option < RustcVersion > ,
691
709
) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
@@ -800,7 +818,16 @@ fn walk_parents<'tcx>(
800
818
Some ( hir_ty) => binding_ty_auto_deref_stability ( cx, hir_ty, precedence, ty. bound_vars ( ) ) ,
801
819
None => {
802
820
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
+ )
804
831
} else {
805
832
ty_auto_deref_stability ( cx, cx. tcx . erase_late_bound_regions ( ty) , precedence)
806
833
. position_for_arg ( )
@@ -848,7 +875,16 @@ fn walk_parents<'tcx>(
848
875
args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
849
876
let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i + 1 ] ;
850
877
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
+ )
852
888
} else {
853
889
ty_auto_deref_stability (
854
890
cx,
@@ -1022,8 +1058,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
1022
1058
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
1023
1059
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
1024
1060
// be moved, but it cannot be.
1061
+ #[ expect( clippy:: too_many_arguments) ]
1025
1062
fn needless_borrow_impl_arg_position < ' tcx > (
1026
1063
cx : & LateContext < ' tcx > ,
1064
+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
1027
1065
parent : & Expr < ' tcx > ,
1028
1066
arg_index : usize ,
1029
1067
param_ty : ParamTy ,
@@ -1082,8 +1120,6 @@ fn needless_borrow_impl_arg_position<'tcx>(
1082
1120
return Position :: Other ( precedence) ;
1083
1121
}
1084
1122
1085
- let mut possible_borrower = None ;
1086
-
1087
1123
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
1088
1124
// elements are modified each time `check_referent` is called.
1089
1125
let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
@@ -1093,7 +1129,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
1093
1129
1094
1130
if !is_copy ( cx, referent_ty)
1095
1131
&& ( 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) )
1097
1133
{
1098
1134
return false ;
1099
1135
}
@@ -1164,9 +1200,9 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
1164
1200
} )
1165
1201
}
1166
1202
1167
- fn referent_dropped_without_further_use < ' a , ' tcx > (
1203
+ fn referent_used_exactly_once < ' a , ' tcx > (
1168
1204
cx : & ' a LateContext < ' tcx > ,
1169
- possible_borrower : & mut Option < PossibleBorrowerMap < ' a , ' tcx > > ,
1205
+ possible_borrower : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
1170
1206
reference : & Expr < ' tcx > ,
1171
1207
) -> bool {
1172
1208
let mir = enclosing_mir ( cx. tcx , reference. hir_id ) ;
@@ -1176,12 +1212,15 @@ fn referent_dropped_without_further_use<'a, 'tcx>(
1176
1212
mir. basic_blocks [ location. block ] . statements [ location. statement_index ] . kind
1177
1213
&& !place. has_deref ( )
1178
1214
{
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) ) ;
1180
1219
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
1181
1220
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
1182
1221
// itself. See the comment in that method for an explanation as to why.
1183
1222
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 )
1185
1224
} else {
1186
1225
false
1187
1226
}
@@ -1471,8 +1510,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
1471
1510
}
1472
1511
}
1473
1512
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 ) {
1476
1515
if let Some ( outer_pat) = self . ref_locals . get_mut ( & local) {
1477
1516
if let Some ( pat) = outer_pat {
1478
1517
// Check for auto-deref
0 commit comments