1
1
use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then} ;
2
+ use clippy_utils:: mir:: { enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap } ;
2
3
use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
3
4
use clippy_utils:: sugg:: has_enclosing_paren;
4
5
use clippy_utils:: ty:: { expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res} ;
@@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap;
11
12
use rustc_errors:: Applicability ;
12
13
use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
13
14
use rustc_hir:: {
14
- self as hir, def_id:: DefId , BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy ,
15
- GenericArg , HirId , ImplItem , ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind ,
16
- 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 ,
17
20
} ;
18
21
use rustc_index:: bit_set:: BitSet ;
19
22
use rustc_infer:: infer:: TyCtxtInferExt ;
20
23
use rustc_lint:: { LateContext , LateLintPass } ;
24
+ use rustc_middle:: mir:: { Rvalue , StatementKind } ;
21
25
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
22
26
use rustc_middle:: ty:: {
23
27
self , Binder , BoundVariableKind , EarlyBinder , FnSig , GenericArgKind , List , ParamTy , PredicateKind ,
@@ -141,15 +145,15 @@ declare_clippy_lint! {
141
145
"dereferencing when the compiler would automatically dereference"
142
146
}
143
147
144
- impl_lint_pass ! ( Dereferencing => [
148
+ impl_lint_pass ! ( Dereferencing < ' _> => [
145
149
EXPLICIT_DEREF_METHODS ,
146
150
NEEDLESS_BORROW ,
147
151
REF_BINDING_TO_REFERENCE ,
148
152
EXPLICIT_AUTO_DEREF ,
149
153
] ) ;
150
154
151
155
#[ derive( Default ) ]
152
- pub struct Dereferencing {
156
+ pub struct Dereferencing < ' tcx > {
153
157
state : Option < ( State , StateData ) > ,
154
158
155
159
// While parsing a `deref` method call in ufcs form, the path to the function is itself an
@@ -170,11 +174,16 @@ pub struct Dereferencing {
170
174
/// e.g. `m!(x) | Foo::Bar(ref x)`
171
175
ref_locals : FxIndexMap < HirId , Option < RefPat > > ,
172
176
177
+ /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
178
+ /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
179
+ /// be moved.
180
+ possible_borrowers : Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
181
+
173
182
// `IntoIterator` for arrays requires Rust 1.53.
174
183
msrv : Option < RustcVersion > ,
175
184
}
176
185
177
- impl Dereferencing {
186
+ impl < ' tcx > Dereferencing < ' tcx > {
178
187
#[ must_use]
179
188
pub fn new ( msrv : Option < RustcVersion > ) -> Self {
180
189
Self {
@@ -244,7 +253,7 @@ struct RefPat {
244
253
hir_id : HirId ,
245
254
}
246
255
247
- impl < ' tcx > LateLintPass < ' tcx > for Dereferencing {
256
+ impl < ' tcx > LateLintPass < ' tcx > for Dereferencing < ' tcx > {
248
257
#[ expect( clippy:: too_many_lines) ]
249
258
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
250
259
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
@@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
278
287
match ( self . state . take ( ) , kind) {
279
288
( None , kind) => {
280
289
let expr_ty = typeck. expr_ty ( expr) ;
281
- let ( position, adjustments) = walk_parents ( cx, expr, self . msrv ) ;
290
+ let ( position, adjustments) = walk_parents ( cx, & mut self . possible_borrowers , expr, self . msrv ) ;
282
291
match kind {
283
292
RefOp :: Deref => {
284
293
if let Position :: FieldAccess {
@@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
550
559
}
551
560
552
561
fn check_body_post ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx Body < ' _ > ) {
562
+ if self . possible_borrowers . last ( ) . map_or ( false , |& ( local_def_id, _) | {
563
+ local_def_id == cx. tcx . hir ( ) . body_owner_def_id ( body. id ( ) )
564
+ } ) {
565
+ self . possible_borrowers . pop ( ) ;
566
+ }
567
+
553
568
if Some ( body. id ( ) ) == self . current_body {
554
569
for pat in self . ref_locals . drain ( ..) . filter_map ( |( _, x) | x) {
555
570
let replacements = pat. replacements ;
@@ -682,6 +697,7 @@ impl Position {
682
697
#[ expect( clippy:: too_many_lines) ]
683
698
fn walk_parents < ' tcx > (
684
699
cx : & LateContext < ' tcx > ,
700
+ possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
685
701
e : & ' tcx Expr < ' _ > ,
686
702
msrv : Option < RustcVersion > ,
687
703
) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
@@ -796,7 +812,16 @@ fn walk_parents<'tcx>(
796
812
Some ( hir_ty) => binding_ty_auto_deref_stability ( cx, hir_ty, precedence, ty. bound_vars ( ) ) ,
797
813
None => {
798
814
if let ty:: Param ( param_ty) = ty. skip_binder ( ) . kind ( ) {
799
- needless_borrow_impl_arg_position ( cx, parent, i, * param_ty, e, precedence, msrv)
815
+ needless_borrow_impl_arg_position (
816
+ cx,
817
+ possible_borrowers,
818
+ parent,
819
+ i,
820
+ * param_ty,
821
+ e,
822
+ precedence,
823
+ msrv,
824
+ )
800
825
} else {
801
826
ty_auto_deref_stability ( cx, cx. tcx . erase_late_bound_regions ( ty) , precedence)
802
827
. position_for_arg ( )
@@ -844,7 +869,16 @@ fn walk_parents<'tcx>(
844
869
args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
845
870
let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i + 1 ] ;
846
871
if let ty:: Param ( param_ty) = ty. kind ( ) {
847
- needless_borrow_impl_arg_position ( cx, parent, i + 1 , * param_ty, e, precedence, msrv)
872
+ needless_borrow_impl_arg_position (
873
+ cx,
874
+ possible_borrowers,
875
+ parent,
876
+ i + 1 ,
877
+ * param_ty,
878
+ e,
879
+ precedence,
880
+ msrv,
881
+ )
848
882
} else {
849
883
ty_auto_deref_stability (
850
884
cx,
@@ -1018,8 +1052,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
1018
1052
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
1019
1053
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
1020
1054
// be moved, but it cannot be.
1055
+ #[ expect( clippy:: too_many_arguments) ]
1021
1056
fn needless_borrow_impl_arg_position < ' tcx > (
1022
1057
cx : & LateContext < ' tcx > ,
1058
+ possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
1023
1059
parent : & Expr < ' tcx > ,
1024
1060
arg_index : usize ,
1025
1061
param_ty : ParamTy ,
@@ -1082,10 +1118,13 @@ fn needless_borrow_impl_arg_position<'tcx>(
1082
1118
// elements are modified each time `check_referent` is called.
1083
1119
let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
1084
1120
1085
- let mut check_referent = |referent| {
1121
+ let mut check_reference_and_referent = |reference , referent| {
1086
1122
let referent_ty = cx. typeck_results ( ) . expr_ty ( referent) ;
1087
1123
1088
- if !is_copy ( cx, referent_ty) {
1124
+ if !is_copy ( cx, referent_ty)
1125
+ && ( referent_ty. has_significant_drop ( cx. tcx , cx. param_env )
1126
+ || !referent_used_exactly_once ( cx, possible_borrowers, reference) )
1127
+ {
1089
1128
return false ;
1090
1129
}
1091
1130
@@ -1127,7 +1166,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
1127
1166
1128
1167
let mut needless_borrow = false ;
1129
1168
while let ExprKind :: AddrOf ( _, _, referent) = expr. kind {
1130
- if !check_referent ( referent) {
1169
+ if !check_reference_and_referent ( expr , referent) {
1131
1170
break ;
1132
1171
}
1133
1172
expr = referent;
@@ -1155,6 +1194,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
1155
1194
} )
1156
1195
}
1157
1196
1197
+ fn referent_used_exactly_once < ' a , ' tcx > (
1198
+ cx : & ' a LateContext < ' tcx > ,
1199
+ possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
1200
+ reference : & Expr < ' tcx > ,
1201
+ ) -> bool {
1202
+ let mir = enclosing_mir ( cx. tcx , reference. hir_id ) ;
1203
+ if let Some ( local) = expr_local ( cx. tcx , reference)
1204
+ && let [ location] = * local_assignments ( mir, local) . as_slice ( )
1205
+ && let StatementKind :: Assign ( box ( _, Rvalue :: Ref ( _, _, place) ) ) =
1206
+ mir. basic_blocks [ location. block ] . statements [ location. statement_index ] . kind
1207
+ && !place. has_deref ( )
1208
+ {
1209
+ let body_owner_local_def_id = cx. tcx . hir ( ) . enclosing_body_owner ( reference. hir_id ) ;
1210
+ if possible_borrowers
1211
+ . last ( )
1212
+ . map_or ( true , |& ( local_def_id, _) | local_def_id != body_owner_local_def_id)
1213
+ {
1214
+ possible_borrowers. push ( ( body_owner_local_def_id, PossibleBorrowerMap :: new ( cx, mir) ) ) ;
1215
+ }
1216
+ let possible_borrower = & mut possible_borrowers. last_mut ( ) . unwrap ( ) . 1 ;
1217
+ // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
1218
+ // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
1219
+ // itself. See the comment in that method for an explanation as to why.
1220
+ possible_borrower. bounded_borrowers ( & [ local] , & [ local, place. local ] , place. local , location)
1221
+ && used_exactly_once ( mir, place. local ) . unwrap_or ( false )
1222
+ } else {
1223
+ false
1224
+ }
1225
+ }
1226
+
1158
1227
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
1159
1228
// projected type that is a type parameter. Returns `false` if replacing the types would have an
1160
1229
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
@@ -1439,8 +1508,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
1439
1508
}
1440
1509
}
1441
1510
1442
- impl Dereferencing {
1443
- fn check_local_usage < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1511
+ impl < ' tcx > Dereferencing < ' tcx > {
1512
+ fn check_local_usage ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1444
1513
if let Some ( outer_pat) = self . ref_locals . get_mut ( & local) {
1445
1514
if let Some ( pat) = outer_pat {
1446
1515
// Check for auto-deref
0 commit comments