1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: source:: snippet_opt;
3
- use clippy_utils:: { fn_has_unsatisfiable_preds, is_from_proc_macro} ;
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} ;
4
5
use rustc_data_structures:: fx:: FxHashSet ;
5
6
use rustc_data_structures:: graph:: dominators:: Dominators ;
6
7
use rustc_data_structures:: graph:: iterate:: DepthFirstSearch ;
7
8
use rustc_data_structures:: graph:: WithSuccessors ;
8
9
use rustc_errors:: Applicability ;
10
+ use rustc_hir:: def:: Res ;
9
11
use rustc_hir:: def_id:: LocalDefId ;
10
12
use rustc_hir:: {
11
- Closure , Expr , ExprKind , HirId , ImplItem , ImplItemKind , Item , ItemKind , Node , Path , PathSegment , QPath , StmtKind ,
12
- TraitFn , TraitItem , TraitItemKind ,
13
+ Closure , Expr , ExprKind , HirId , ImplItem , ImplItemKind , Item , ItemKind , Mutability , Node , Path , PathSegment , QPath ,
14
+ StmtKind , TraitFn , TraitItem , TraitItemKind , UnOp ,
13
15
} ;
14
16
use rustc_lint:: { LateContext , LateLintPass } ;
15
17
use rustc_middle:: lint:: in_external_macro;
@@ -18,6 +20,7 @@ use rustc_middle::mir::{self, BasicBlock, Body, Local, Location, Place, Statemen
18
20
use rustc_session:: { declare_lint_pass, declare_tool_lint, Session } ;
19
21
use rustc_span:: Span ;
20
22
use std:: collections:: BTreeSet ;
23
+ use std:: ops:: ControlFlow ;
21
24
22
25
declare_clippy_lint ! {
23
26
/// ### What it does
@@ -81,6 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
81
84
None ,
82
85
Path {
83
86
segments : [ PathSegment { args : None , .. } ] ,
87
+ res : Res :: Local ( local_id) ,
84
88
..
85
89
} ,
86
90
) ) ,
@@ -90,6 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
90
94
right,
91
95
_,
92
96
) ,
97
+ hir_id : expr_id,
93
98
..
94
99
} ,
95
100
) = stmt. kind
@@ -123,13 +128,64 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitReinitialization {
123
128
assert ! ( start_location. dominates( location, dominators) ) ;
124
129
125
130
if dominate_all_usage ( mir, dominators, local, start_location) {
131
+ // copy from `vec_init_then_push.rs`
132
+ let mut needs_mut = false ;
133
+ let _ = for_each_local_use_after_expr ( cx, * local_id, * expr_id, |e| {
134
+ let Some ( parent) = get_parent_expr ( cx, e) else {
135
+ return ControlFlow :: Continue ( ( ) ) ;
136
+ } ;
137
+ let adjusted_ty = cx. typeck_results ( ) . expr_ty_adjusted ( e) ;
138
+ let adjusted_mut = adjusted_ty. ref_mutability ( ) . unwrap_or ( Mutability :: Not ) ;
139
+ needs_mut |= adjusted_mut == Mutability :: Mut ;
140
+ match parent. kind {
141
+ ExprKind :: AddrOf ( _, Mutability :: Mut , _) => {
142
+ needs_mut = true ;
143
+ return ControlFlow :: Break ( true ) ;
144
+ } ,
145
+ ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Index ( ..) if !needs_mut => {
146
+ let mut last_place = parent;
147
+ while let Some ( parent) = get_parent_expr ( cx, last_place) {
148
+ if matches ! ( parent. kind, ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Field ( ..) )
149
+ || matches ! ( parent. kind, ExprKind :: Index ( e, _, _) if e. hir_id == last_place. hir_id)
150
+ {
151
+ last_place = parent;
152
+ } else {
153
+ break ;
154
+ }
155
+ }
156
+ needs_mut |= cx. typeck_results ( ) . expr_ty_adjusted ( last_place) . ref_mutability ( )
157
+ == Some ( Mutability :: Mut )
158
+ || get_parent_expr ( cx, last_place)
159
+ . map_or ( false , |e| matches ! ( e. kind, ExprKind :: AddrOf ( _, Mutability :: Mut , _) ) ) ;
160
+ } ,
161
+ ExprKind :: MethodCall ( _, recv, ..)
162
+ if recv. hir_id == e. hir_id
163
+ && adjusted_mut == Mutability :: Mut
164
+ && !adjusted_ty. peel_refs ( ) . is_slice ( ) =>
165
+ {
166
+ // No need to set `needs_mut` to true. The receiver will be either explicitly borrowed, or it
167
+ // will be implicitly borrowed via an adjustment. Both of these cases
168
+ // are already handled by this point.
169
+ return ControlFlow :: Break ( true ) ;
170
+ } ,
171
+ ExprKind :: Assign ( lhs, ..) if e. hir_id == lhs. hir_id => {
172
+ needs_mut = true ;
173
+ return ControlFlow :: Break ( false ) ;
174
+ } ,
175
+ _ => ( ) ,
176
+ }
177
+ ControlFlow :: Continue ( ( ) )
178
+ } ) ;
179
+
180
+ let mut_str = if needs_mut { "mut " } else { "" } ;
181
+
126
182
span_lint_and_sugg (
127
183
cx,
128
184
EXPLICIT_REINITIALIZATION ,
129
185
stmt. span ,
130
186
"create a fresh variable is more explicit" ,
131
187
"create a fresh variable instead of reinitialization" ,
132
- format ! ( "let mut {snip}" ) ,
188
+ format ! ( "let {mut_str} {snip}" ) ,
133
189
Applicability :: MachineApplicable ,
134
190
) ;
135
191
}
0 commit comments