1
+ use crate :: { BorrowckContext , BorrowckLintPass } ;
1
2
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 } ;
3
+ use clippy_utils:: mir:: { expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap } ;
3
4
use clippy_utils:: msrvs:: { self , Msrv } ;
4
5
use clippy_utils:: source:: { snippet_with_applicability, snippet_with_context} ;
5
6
use clippy_utils:: sugg:: has_enclosing_paren;
@@ -8,22 +9,23 @@ use clippy_utils::{
8
9
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
9
10
} ;
10
11
12
+ use rustc_ast as ast;
11
13
use rustc_ast:: util:: parser:: { PREC_POSTFIX , PREC_PREFIX } ;
14
+ use rustc_borrowck:: consumers:: BodyWithBorrowckFacts ;
12
15
use rustc_data_structures:: fx:: FxIndexMap ;
13
16
use rustc_data_structures:: graph:: iterate:: { CycleDetector , TriColorDepthFirstSearch } ;
14
17
use rustc_errors:: Applicability ;
18
+ use rustc_hir:: def_id:: { DefId , LocalDefId } ;
15
19
use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
16
20
use rustc_hir:: {
17
- self as hir,
18
- def_id:: { DefId , LocalDefId } ,
19
- BindingAnnotation , Body , BodyId , BorrowKind , Closure , Expr , ExprKind , FnRetTy , GenericArg , HirId , ImplItem ,
21
+ self as hir, BindingAnnotation , Body , BorrowKind , Closure , Expr , ExprKind , FnRetTy , GenericArg , HirId , ImplItem ,
20
22
ImplItemKind , Item , ItemKind , Local , MatchSource , Mutability , Node , Pat , PatKind , Path , QPath , TraitItem ,
21
23
TraitItemKind , TyKind , UnOp ,
22
24
} ;
23
25
use rustc_index:: bit_set:: BitSet ;
24
26
use rustc_infer:: infer:: TyCtxtInferExt ;
25
- use rustc_lint:: { LateContext , LateLintPass } ;
26
- use rustc_middle:: mir:: { Rvalue , StatementKind } ;
27
+ use rustc_lint:: { late_lint_methods , LateContext , LateLintPass , LintPass } ;
28
+ use rustc_middle:: mir:: { PlaceElem , Rvalue , StatementKind } ;
27
29
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
28
30
use rustc_middle:: ty:: {
29
31
self , Binder , BoundVariableKind , Clause , EarlyBinder , FnSig , GenericArgKind , List , ParamEnv , ParamTy ,
@@ -34,6 +36,7 @@ use rustc_span::{symbol::sym, Span, Symbol};
34
36
use rustc_trait_selection:: infer:: InferCtxtExt as _;
35
37
use rustc_trait_selection:: traits:: { query:: evaluate_obligation:: InferCtxtExt as _, Obligation , ObligationCause } ;
36
38
use std:: collections:: VecDeque ;
39
+ use std:: rc:: Rc ;
37
40
38
41
declare_clippy_lint ! {
39
42
/// ### What it does
@@ -148,15 +151,15 @@ declare_clippy_lint! {
148
151
"dereferencing when the compiler would automatically dereference"
149
152
}
150
153
151
- impl_lint_pass ! ( Dereferencing <' _> => [
154
+ impl_lint_pass ! ( Dereferencing <' _, ' _ > => [
152
155
EXPLICIT_DEREF_METHODS ,
153
156
NEEDLESS_BORROW ,
154
157
REF_BINDING_TO_REFERENCE ,
155
158
EXPLICIT_AUTO_DEREF ,
156
159
] ) ;
157
160
158
161
#[ derive( Default ) ]
159
- pub struct Dereferencing < ' tcx > {
162
+ pub struct Dereferencing < ' b , ' tcx > {
160
163
state : Option < ( State , StateData ) > ,
161
164
162
165
// While parsing a `deref` method call in ufcs form, the path to the function is itself an
@@ -167,7 +170,7 @@ pub struct Dereferencing<'tcx> {
167
170
/// The body the first local was found in. Used to emit lints when the traversal of the body has
168
171
/// been finished. Note we can't lint at the end of every body as they can be nested within each
169
172
/// other.
170
- current_body : Option < BodyId > ,
173
+ current_body : Option < & ' b Rc < BodyWithBorrowckFacts < ' tcx > > > ,
171
174
172
175
/// The list of locals currently being checked by the lint.
173
176
/// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
@@ -177,16 +180,15 @@ pub struct Dereferencing<'tcx> {
177
180
/// e.g. `m!(x) | Foo::Bar(ref x)`
178
181
ref_locals : FxIndexMap < HirId , Option < RefPat > > ,
179
182
180
- /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
181
- /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
182
- /// be moved.
183
- possible_borrowers : Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
183
+ /// Used by `needless_borrow_impl_arg_position` to determine when a borrowed expression can
184
+ /// instead be moved.
185
+ possible_borrowers : Option < PossibleBorrowerMap < ' b , ' tcx > > ,
184
186
185
187
// `IntoIterator` for arrays requires Rust 1.53.
186
188
msrv : Msrv ,
187
189
}
188
190
189
- impl < ' tcx > Dereferencing < ' tcx > {
191
+ impl < ' b , ' tcx > Dereferencing < ' b , ' tcx > {
190
192
#[ must_use]
191
193
pub fn new ( msrv : Msrv ) -> Self {
192
194
Self {
@@ -256,7 +258,7 @@ struct RefPat {
256
258
hir_id : HirId ,
257
259
}
258
260
259
- impl < ' tcx > LateLintPass < ' tcx > for Dereferencing < ' tcx > {
261
+ impl < ' b , ' tcx > LateLintPass < ' tcx > for Dereferencing < ' b , ' tcx > {
260
262
#[ expect( clippy:: too_many_lines) ]
261
263
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
262
264
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
@@ -288,7 +290,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
288
290
match ( self . state . take ( ) , kind) {
289
291
( None , kind) => {
290
292
let expr_ty = typeck. expr_ty ( expr) ;
291
- let ( position, adjustments) = walk_parents ( cx, & mut self . possible_borrowers , expr, & self . msrv ) ;
293
+ let ( position, adjustments) = walk_parents (
294
+ cx,
295
+ self . current_body . as_ref ( ) . unwrap ( ) ,
296
+ & mut self . possible_borrowers ,
297
+ expr,
298
+ & self . msrv ,
299
+ ) ;
292
300
match kind {
293
301
RefOp :: Deref => {
294
302
let sub_ty = typeck. expr_ty ( sub_expr) ;
@@ -548,7 +556,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
548
556
then {
549
557
let mut app = Applicability :: MachineApplicable ;
550
558
let snip = snippet_with_context( cx, name. span, pat. span. ctxt( ) , ".." , & mut app) . 0 ;
551
- self . current_body = self . current_body. or( cx. enclosing_body) ;
552
559
self . ref_locals. insert(
553
560
id,
554
561
Some ( RefPat {
@@ -564,40 +571,66 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
564
571
}
565
572
}
566
573
567
- fn check_body_post ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx Body < ' _ > ) {
568
- if self . possible_borrowers . last ( ) . map_or ( false , |& ( local_def_id, _) | {
569
- local_def_id == cx. tcx . hir ( ) . body_owner_def_id ( body. id ( ) )
570
- } ) {
571
- self . possible_borrowers . pop ( ) ;
572
- }
574
+ fn check_body_post ( & mut self , cx : & LateContext < ' tcx > , _body : & ' tcx Body < ' _ > ) {
575
+ self . possible_borrowers = None ;
573
576
574
- if Some ( body. id ( ) ) == self . current_body {
575
- for pat in self . ref_locals . drain ( ..) . filter_map ( |( _, x) | x) {
576
- let replacements = pat. replacements ;
577
- let app = pat. app ;
578
- let lint = if pat. always_deref {
579
- NEEDLESS_BORROW
580
- } else {
581
- REF_BINDING_TO_REFERENCE
582
- } ;
583
- span_lint_hir_and_then (
584
- cx,
585
- lint,
586
- pat. hir_id ,
587
- pat. spans ,
588
- "this pattern creates a reference to a reference" ,
589
- |diag| {
590
- diag. multipart_suggestion ( "try this" , replacements, app) ;
591
- } ,
592
- ) ;
593
- }
594
- self . current_body = None ;
577
+ for pat in self . ref_locals . drain ( ..) . filter_map ( |( _, x) | x) {
578
+ let replacements = pat. replacements ;
579
+ let app = pat. app ;
580
+ let lint = if pat. always_deref {
581
+ NEEDLESS_BORROW
582
+ } else {
583
+ REF_BINDING_TO_REFERENCE
584
+ } ;
585
+ span_lint_hir_and_then (
586
+ cx,
587
+ lint,
588
+ pat. hir_id ,
589
+ pat. spans ,
590
+ "this pattern creates a reference to a reference" ,
591
+ |diag| {
592
+ diag. multipart_suggestion ( "try this" , replacements, app) ;
593
+ } ,
594
+ ) ;
595
595
}
596
596
}
597
597
598
598
extract_msrv_attr ! ( LateContext ) ;
599
599
}
600
600
601
+ #[ allow( rustc:: lint_pass_impl_without_macro) ]
602
+ impl < ' b , ' tcx > LintPass for & mut Dereferencing < ' b , ' tcx > {
603
+ fn name ( & self ) -> & ' static str {
604
+ panic ! ( )
605
+ }
606
+ }
607
+
608
+ macro_rules! impl_late_lint_pass {
609
+ ( [ ] , [ $( $( #[ $attr: meta] ) * fn $f: ident( $( $param: ident: $arg: ty) ,* ) ; ) * ] ) => {
610
+ impl <' b, ' tcx> LateLintPass <' tcx> for & mut Dereferencing <' b, ' tcx> {
611
+ $( fn $f( & mut self , context: & LateContext <' tcx>, $( $param: $arg) ,* ) {
612
+ ( * self ) . $f( context, $( $param) ,* ) ;
613
+ } ) *
614
+ }
615
+ } ;
616
+ }
617
+
618
+ late_lint_methods ! ( impl_late_lint_pass, [ ] ) ;
619
+
620
+ impl < ' b , ' tcx > BorrowckLintPass < ' b , ' tcx > for Dereferencing < ' b , ' tcx > {
621
+ fn check_body_with_facts (
622
+ & mut self ,
623
+ cx : & BorrowckContext < ' tcx > ,
624
+ body_with_facts : & ' b Rc < BodyWithBorrowckFacts < ' tcx > > ,
625
+ ) {
626
+ self . current_body = Some ( body_with_facts) ;
627
+
628
+ let this = cx. visit_hir_body ( self ) ;
629
+
630
+ this. current_body = None ;
631
+ }
632
+ }
633
+
601
634
fn try_parse_ref_op < ' tcx > (
602
635
tcx : TyCtxt < ' tcx > ,
603
636
typeck : & ' tcx TypeckResults < ' _ > ,
@@ -701,9 +734,10 @@ impl Position {
701
734
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
702
735
/// locations as those follow different rules.
703
736
#[ expect( clippy:: too_many_lines) ]
704
- fn walk_parents < ' tcx > (
737
+ fn walk_parents < ' b , ' tcx > (
705
738
cx : & LateContext < ' tcx > ,
706
- possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
739
+ body_with_facts : & ' b Rc < BodyWithBorrowckFacts < ' tcx > > ,
740
+ possible_borrowers : & mut Option < PossibleBorrowerMap < ' b , ' tcx > > ,
707
741
e : & ' tcx Expr < ' _ > ,
708
742
msrv : & Msrv ,
709
743
) -> ( Position , & ' tcx [ Adjustment < ' tcx > ] ) {
@@ -841,6 +875,7 @@ fn walk_parents<'tcx>(
841
875
{
842
876
needless_borrow_impl_arg_position (
843
877
cx,
878
+ body_with_facts,
844
879
possible_borrowers,
845
880
parent,
846
881
i,
@@ -912,6 +947,7 @@ fn walk_parents<'tcx>(
912
947
{
913
948
needless_borrow_impl_arg_position (
914
949
cx,
950
+ body_with_facts,
915
951
possible_borrowers,
916
952
parent,
917
953
i + 1 ,
@@ -1108,9 +1144,10 @@ fn call_is_qualified(expr: &Expr<'_>) -> bool {
1108
1144
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
1109
1145
// be moved, but it cannot be.
1110
1146
#[ expect( clippy:: too_many_arguments, clippy:: too_many_lines) ]
1111
- fn needless_borrow_impl_arg_position < ' tcx > (
1147
+ fn needless_borrow_impl_arg_position < ' b , ' tcx > (
1112
1148
cx : & LateContext < ' tcx > ,
1113
- possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
1149
+ body_with_facts : & ' b Rc < BodyWithBorrowckFacts < ' tcx > > ,
1150
+ possible_borrowers : & mut Option < PossibleBorrowerMap < ' b , ' tcx > > ,
1114
1151
parent : & Expr < ' tcx > ,
1115
1152
arg_index : usize ,
1116
1153
param_ty : ParamTy ,
@@ -1188,7 +1225,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
1188
1225
1189
1226
if !is_copy ( cx, referent_ty)
1190
1227
&& ( referent_ty. has_significant_drop ( cx. tcx , cx. param_env )
1191
- || !referent_used_exactly_once ( cx, possible_borrowers, reference) )
1228
+ || !referent_used_exactly_once ( cx, body_with_facts , possible_borrowers, reference) )
1192
1229
{
1193
1230
return false ;
1194
1231
}
@@ -1289,28 +1326,25 @@ fn is_mixed_projection_predicate<'tcx>(
1289
1326
}
1290
1327
}
1291
1328
1292
- fn referent_used_exactly_once < ' tcx > (
1329
+ fn referent_used_exactly_once < ' b , ' tcx > (
1293
1330
cx : & LateContext < ' tcx > ,
1294
- possible_borrowers : & mut Vec < ( LocalDefId , PossibleBorrowerMap < ' tcx , ' tcx > ) > ,
1331
+ body_with_facts : & ' b Rc < BodyWithBorrowckFacts < ' tcx > > ,
1332
+ possible_borrowers : & mut Option < PossibleBorrowerMap < ' b , ' tcx > > ,
1295
1333
reference : & Expr < ' tcx > ,
1296
1334
) -> bool {
1297
- let mir = enclosing_mir ( cx . tcx , reference . hir_id ) ;
1298
- if let Some ( local) = expr_local ( cx . tcx , reference)
1335
+ let mir = & body_with_facts . body ;
1336
+ if let Some ( local) = expr_local ( mir , reference)
1299
1337
&& let [ location] = * local_assignments ( mir, local) . as_slice ( )
1300
1338
&& let Some ( statement) = mir. basic_blocks [ location. block ] . statements . get ( location. statement_index )
1301
1339
&& let StatementKind :: Assign ( box ( _, Rvalue :: Ref ( _, _, place) ) ) = statement. kind
1302
- && !place. has_deref ( )
1340
+ && !place. projection . contains ( & PlaceElem :: Deref )
1303
1341
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
1304
1342
&& TriColorDepthFirstSearch :: new ( & mir. basic_blocks ) . run_from ( location. block , & mut CycleDetector ) . is_none ( )
1305
1343
{
1306
- let body_owner_local_def_id = cx. tcx . hir ( ) . enclosing_body_owner ( reference. hir_id ) ;
1307
- if possible_borrowers
1308
- . last ( )
1309
- . map_or ( true , |& ( local_def_id, _) | local_def_id != body_owner_local_def_id)
1310
- {
1311
- possible_borrowers. push ( ( body_owner_local_def_id, PossibleBorrowerMap :: new ( cx, body_owner_local_def_id) ) ) ;
1344
+ if possible_borrowers. is_none ( ) {
1345
+ * possible_borrowers = Some ( PossibleBorrowerMap :: new ( cx. tcx , body_with_facts) ) ;
1312
1346
}
1313
- let possible_borrower = & mut possible_borrowers. last_mut ( ) . unwrap ( ) . 1 ;
1347
+ let possible_borrower = possible_borrowers. as_mut ( ) . unwrap ( ) ;
1314
1348
// If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The
1315
1349
// reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible
1316
1350
// borrower of itself. See the comment in that method for an explanation as to why.
@@ -1625,7 +1659,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
1625
1659
}
1626
1660
}
1627
1661
1628
- impl < ' tcx > Dereferencing < ' tcx > {
1662
+ impl < ' b , ' tcx > Dereferencing < ' b , ' tcx > {
1629
1663
fn check_local_usage ( & mut self , cx : & LateContext < ' tcx > , e : & Expr < ' tcx > , local : HirId ) {
1630
1664
if let Some ( outer_pat) = self . ref_locals . get_mut ( & local) {
1631
1665
if let Some ( pat) = outer_pat {
0 commit comments