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 , subst:: Subst , 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 {
@@ -245,7 +262,7 @@ struct RefPat {
245
262
hir_id : HirId ,
246
263
}
247
264
248
- impl < ' tcx > LateLintPass < ' tcx > for Dereferencing {
265
+ impl < ' tcx > LateLintPass < ' tcx > for Dereferencing < ' tcx > {
249
266
#[ expect( clippy:: too_many_lines) ]
250
267
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
251
268
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
@@ -279,7 +296,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
279
296
match ( self . state . take ( ) , kind) {
280
297
( None , kind) => {
281
298
let expr_ty = typeck. expr_ty ( expr) ;
282
- let ( position, adjustments) = walk_parents ( cx, expr, self . msrv ) ;
299
+ let ( position, adjustments) = walk_parents ( cx, & mut self . possible_borrowers , expr, self . msrv ) ;
283
300
284
301
match kind {
285
302
RefOp :: Deref => {
@@ -678,6 +695,7 @@ impl Position {
678
695
#[ expect( clippy:: too_many_lines) ]
679
696
fn walk_parents < ' tcx > (
680
697
cx : & LateContext < ' tcx > ,
698
+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
681
699
e : & ' tcx Expr < ' _ > ,
682
700
msrv : Option < RustcVersion > ,
683
701
) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
@@ -792,7 +810,16 @@ fn walk_parents<'tcx>(
792
810
Some ( hir_ty) => binding_ty_auto_deref_stability ( cx, hir_ty, precedence, ty. bound_vars ( ) ) ,
793
811
None => {
794
812
if let ty:: Param ( param_ty) = ty. skip_binder ( ) . kind ( ) {
795
- needless_borrow_impl_arg_position ( cx, parent, i, * param_ty, e, precedence, msrv)
813
+ needless_borrow_impl_arg_position (
814
+ cx,
815
+ possible_borrowers,
816
+ parent,
817
+ i,
818
+ * param_ty,
819
+ e,
820
+ precedence,
821
+ msrv,
822
+ )
796
823
} else {
797
824
ty_auto_deref_stability ( cx, cx. tcx . erase_late_bound_regions ( ty) , precedence)
798
825
. position_for_arg ( )
@@ -840,7 +867,16 @@ fn walk_parents<'tcx>(
840
867
args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map ( |i| {
841
868
let ty = cx. tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ i + 1 ] ;
842
869
if let ty:: Param ( param_ty) = ty. kind ( ) {
843
- needless_borrow_impl_arg_position ( cx, parent, i + 1 , * param_ty, e, precedence, msrv)
870
+ needless_borrow_impl_arg_position (
871
+ cx,
872
+ possible_borrowers,
873
+ parent,
874
+ i + 1 ,
875
+ * param_ty,
876
+ e,
877
+ precedence,
878
+ msrv,
879
+ )
844
880
} else {
845
881
ty_auto_deref_stability (
846
882
cx,
@@ -1004,8 +1040,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
1004
1040
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
1005
1041
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
1006
1042
// be moved, but it cannot be.
1043
+ #[ expect( clippy:: too_many_arguments) ]
1007
1044
fn needless_borrow_impl_arg_position < ' tcx > (
1008
1045
cx : & LateContext < ' tcx > ,
1046
+ possible_borrowers : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
1009
1047
parent : & Expr < ' tcx > ,
1010
1048
arg_index : usize ,
1011
1049
param_ty : ParamTy ,
@@ -1064,8 +1102,6 @@ fn needless_borrow_impl_arg_position<'tcx>(
1064
1102
return Position :: Other ( precedence) ;
1065
1103
}
1066
1104
1067
- let mut possible_borrower = None ;
1068
-
1069
1105
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
1070
1106
// elements are modified each time `check_referent` is called.
1071
1107
let mut substs_with_referent_ty = substs_with_expr_ty. to_vec ( ) ;
@@ -1075,7 +1111,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
1075
1111
1076
1112
if !is_copy ( cx, referent_ty)
1077
1113
&& ( referent_ty. has_significant_drop ( cx. tcx , cx. param_env )
1078
- || !referent_dropped_without_further_use ( cx, & mut possible_borrower , reference) )
1114
+ || !referent_used_exactly_once ( cx, possible_borrowers , reference) )
1079
1115
{
1080
1116
return false ;
1081
1117
}
@@ -1146,9 +1182,9 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
1146
1182
} )
1147
1183
}
1148
1184
1149
- fn referent_dropped_without_further_use < ' a , ' tcx > (
1185
+ fn referent_used_exactly_once < ' a , ' tcx > (
1150
1186
cx : & ' a LateContext < ' tcx > ,
1151
- possible_borrower : & mut Option < PossibleBorrowerMap < ' a , ' tcx > > ,
1187
+ possible_borrower : & mut FxIndexMap < LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > > ,
1152
1188
reference : & Expr < ' tcx > ,
1153
1189
) -> bool {
1154
1190
let mir = enclosing_mir ( cx. tcx , reference. hir_id ) ;
@@ -1158,12 +1194,15 @@ fn referent_dropped_without_further_use<'a, 'tcx>(
1158
1194
mir. basic_blocks [ location. block ] . statements [ location. statement_index ] . kind
1159
1195
&& !place. has_deref ( )
1160
1196
{
1161
- let possible_borrower = possible_borrower. get_or_insert_with ( || PossibleBorrowerMap :: new ( cx, mir) ) ;
1197
+ let body_owner_local_def_id = cx. tcx . hir ( ) . enclosing_body_owner ( reference. hir_id ) ;
1198
+ let possible_borrower = possible_borrower
1199
+ . entry ( body_owner_local_def_id)
1200
+ . or_insert_with ( || PossibleBorrowerMap :: new ( cx, mir) ) ;
1162
1201
// If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
1163
1202
// that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
1164
1203
// itself. See the comment in that method for an explanation as to why.
1165
1204
possible_borrower. bounded_borrowers ( & [ local] , & [ local, place. local ] , place. local , location)
1166
- && dropped_without_further_use ( mir, place. local , location ) . unwrap_or ( false )
1205
+ && used_exactly_once ( mir, place. local ) . unwrap_or ( false )
1167
1206
} else {
1168
1207
false
1169
1208
}
@@ -1453,8 +1492,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
1453
1492
}
1454
1493
}
1455
1494
1456
- impl Dereferencing {
1457
- fn check_local_usage < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1495
+ impl < ' tcx > Dereferencing < ' tcx > {
1496
+ fn check_local_usage ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1458
1497
if let Some ( outer_pat) = self . ref_locals . get_mut ( & local) {
1459
1498
if let Some ( pat) = outer_pat {
1460
1499
// Check for auto-deref
0 commit comments