1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: source:: snippet_opt;
3
- use clippy_utils:: visitors:: for_each_local_use_after_expr;
4
- use clippy_utils:: { fn_has_unsatisfiable_preds, get_parent_expr, is_from_proc_macro} ;
3
+ use clippy_utils:: { fn_has_unsatisfiable_preds, is_from_proc_macro} ;
5
4
use rustc_data_structures:: fx:: FxHashSet ;
6
5
use rustc_data_structures:: graph:: dominators:: Dominators ;
7
6
use rustc_data_structures:: graph:: iterate:: DepthFirstSearch ;
@@ -11,16 +10,14 @@ use rustc_hir::def::Res;
11
10
use rustc_hir:: def_id:: LocalDefId ;
12
11
use rustc_hir:: {
13
12
Closure , Expr , ExprKind , HirId , ImplItem , ImplItemKind , Item , ItemKind , Mutability , Node , Path , PathSegment , QPath ,
14
- StmtKind , TraitFn , TraitItem , TraitItemKind , UnOp ,
13
+ StmtKind , TraitFn , TraitItem , TraitItemKind ,
15
14
} ;
16
15
use rustc_lint:: { LateContext , LateLintPass } ;
17
16
use rustc_middle:: lint:: in_external_macro;
18
- use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
17
+ use rustc_middle:: mir:: visit:: { MutatingUseContext , PlaceContext , Visitor } ;
19
18
use rustc_middle:: mir:: { self , BasicBlock , Body , Local , Location , Place , Statement , Terminator } ;
20
19
use rustc_session:: { declare_lint_pass, declare_tool_lint, Session } ;
21
20
use rustc_span:: Span ;
22
- use std:: collections:: BTreeSet ;
23
- use std:: ops:: ControlFlow ;
24
21
25
22
declare_clippy_lint ! {
26
23
/// ### What it does
@@ -75,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
75
72
None ,
76
73
Path {
77
74
segments : [ PathSegment { args : None , .. } ] ,
78
- res : Res :: Local ( local_id ) ,
75
+ res : Res :: Local ( _ ) ,
79
76
..
80
77
} ,
81
78
) ) ,
@@ -85,7 +82,6 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
85
82
right,
86
83
_,
87
84
) ,
88
- hir_id : expr_id,
89
85
..
90
86
} ,
91
87
) = stmt. kind
@@ -118,65 +114,14 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
118
114
119
115
assert ! ( start_location. dominates( location, dominators) ) ;
120
116
121
- if dominate_all_usage ( mir, dominators, local, start_location) {
122
- // copy from `vec_init_then_push.rs`
123
- let mut needs_mut = false ;
124
- let _ = for_each_local_use_after_expr ( cx, * local_id, * expr_id, |e| {
125
- let Some ( parent) = get_parent_expr ( cx, e) else {
126
- return ControlFlow :: Continue ( ( ) ) ;
127
- } ;
128
- let adjusted_ty = cx. typeck_results ( ) . expr_ty_adjusted ( e) ;
129
- let adjusted_mut = adjusted_ty. ref_mutability ( ) . unwrap_or ( Mutability :: Not ) ;
130
- needs_mut |= adjusted_mut == Mutability :: Mut ;
131
- match parent. kind {
132
- ExprKind :: AddrOf ( _, Mutability :: Mut , _) => {
133
- needs_mut = true ;
134
- return ControlFlow :: Break ( true ) ;
135
- } ,
136
- ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Index ( ..) if !needs_mut => {
137
- let mut last_place = parent;
138
- while let Some ( parent) = get_parent_expr ( cx, last_place) {
139
- if matches ! ( parent. kind, ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Field ( ..) )
140
- || matches ! ( parent. kind, ExprKind :: Index ( e, _, _) if e. hir_id == last_place. hir_id)
141
- {
142
- last_place = parent;
143
- } else {
144
- break ;
145
- }
146
- }
147
- needs_mut |= cx. typeck_results ( ) . expr_ty_adjusted ( last_place) . ref_mutability ( )
148
- == Some ( Mutability :: Mut )
149
- || get_parent_expr ( cx, last_place)
150
- . map_or ( false , |e| matches ! ( e. kind, ExprKind :: AddrOf ( _, Mutability :: Mut , _) ) ) ;
151
- } ,
152
- ExprKind :: MethodCall ( _, recv, ..)
153
- if recv. hir_id == e. hir_id
154
- && adjusted_mut == Mutability :: Mut
155
- && !adjusted_ty. peel_refs ( ) . is_slice ( ) =>
156
- {
157
- // No need to set `needs_mut` to true. The receiver will be either explicitly borrowed, or it
158
- // will be implicitly borrowed via an adjustment. Both of these cases
159
- // are already handled by this point.
160
- return ControlFlow :: Break ( true ) ;
161
- } ,
162
- ExprKind :: Assign ( lhs, ..) if e. hir_id == lhs. hir_id => {
163
- needs_mut = true ;
164
- return ControlFlow :: Break ( false ) ;
165
- } ,
166
- _ => ( ) ,
167
- }
168
- ControlFlow :: Continue ( ( ) )
169
- } ) ;
170
-
171
- let mut_str = if needs_mut { "mut " } else { "" } ;
172
-
117
+ if let Some ( mutability) = dominate_all_usage ( mir, dominators, local, location) {
173
118
span_lint_and_sugg (
174
119
cx,
175
120
EXPLICIT_REINITIALIZATION ,
176
121
stmt. span ,
177
122
"create a fresh variable is more explicit" ,
178
123
"create a fresh variable instead of reinitialization" ,
179
- format ! ( "let {mut_str }{snip}" ) ,
124
+ format ! ( "let {}{snip}" , mutability . prefix_str ( ) ) ,
180
125
Applicability :: MachineApplicable ,
181
126
) ;
182
127
}
@@ -386,42 +331,71 @@ fn search_mir_by_span(
386
331
accurate_visitor. result
387
332
}
388
333
334
+ // None: doesn't dominate all usage
335
+ // Some(Mut): dominate all usage, and a mut usage found
336
+ // Some(Not); dominate all uagee, and no muge usage found
389
337
fn dominate_all_usage (
390
338
mir : & mir:: Body < ' _ > ,
391
339
dominators : & Dominators < BasicBlock > ,
392
340
local : Local ,
393
341
start_location : Location ,
394
- ) -> bool {
342
+ ) -> Option < Mutability > {
395
343
let mut dfs = DepthFirstSearch :: new ( & mir. basic_blocks ) ;
396
344
for successor in mir. basic_blocks . successors ( start_location. block ) {
397
345
dfs. push_start_node ( successor) ;
398
346
}
399
347
let reachable_bb: FxHashSet < BasicBlock > = dfs. collect ( ) ;
400
- find_usage ( mir, local)
348
+
349
+ let mut res = Mutability :: Not ;
350
+
351
+ for ( location, mutability) in find_usage ( mir, local)
401
352
. into_iter ( )
402
- . filter ( |location| reachable_bb. contains ( & location. block ) )
403
- . filter ( |location| !mir. basic_blocks [ location. block ] . is_cleanup )
404
- . all ( |location| start_location. dominates ( location, dominators) )
353
+ . filter ( |( location, _) | !mir. basic_blocks [ location. block ] . is_cleanup )
354
+ {
355
+ if reachable_bb. contains ( & location. block ) {
356
+ if !start_location. dominates ( location, dominators) {
357
+ return None ;
358
+ }
359
+ if location != start_location && mutability == Mutability :: Mut {
360
+ res = Mutability :: Mut ;
361
+ }
362
+ } else if location. block == start_location. block
363
+ && location. statement_index > start_location. statement_index
364
+ && mutability == Mutability :: Mut
365
+ {
366
+ res = Mutability :: Mut ;
367
+ }
368
+ }
369
+ Some ( res)
405
370
}
406
371
407
372
// copy from https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_borrowck/diagnostics/find_all_local_uses.rs.html#1-29
408
- fn find_usage ( body : & Body < ' _ > , local : Local ) -> BTreeSet < Location > {
373
+ fn find_usage ( body : & Body < ' _ > , local : Local ) -> FxHashSet < ( Location , Mutability ) > {
409
374
struct AllLocalUsesVisitor {
410
375
for_local : Local ,
411
- uses : BTreeSet < Location > ,
376
+ uses : FxHashSet < ( Location , Mutability ) > ,
412
377
}
413
378
414
379
impl < ' tcx > Visitor < ' tcx > for AllLocalUsesVisitor {
415
- fn visit_local ( & mut self , local : Local , _context : PlaceContext , location : Location ) {
380
+ fn visit_local ( & mut self , local : Local , context : PlaceContext , location : Location ) {
416
381
if local == self . for_local {
417
- self . uses . insert ( location) ;
382
+ match context {
383
+ PlaceContext :: NonMutatingUse ( _)
384
+ | PlaceContext :: NonUse ( _)
385
+ | PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) => {
386
+ self . uses . insert ( ( location, Mutability :: Not ) ) ;
387
+ } ,
388
+ PlaceContext :: MutatingUse ( _) => {
389
+ self . uses . insert ( ( location, Mutability :: Mut ) ) ;
390
+ } ,
391
+ }
418
392
}
419
393
}
420
394
}
421
395
422
396
let mut visitor = AllLocalUsesVisitor {
423
397
for_local : local,
424
- uses : BTreeSet :: default ( ) ,
398
+ uses : FxHashSet :: default ( ) ,
425
399
} ;
426
400
visitor. visit_body ( body) ;
427
401
visitor. uses
0 commit comments