@@ -61,11 +61,24 @@ struct VecAllocation<'tcx> {
61
61
62
62
/// Reference to the expression used as argument on `with_capacity` call. This is used
63
63
/// to only match slow zero-filling idioms of the same length than vector initialization.
64
- ///
65
- /// Initially set to `None` if initialized with `Vec::new()`, but will always be `Some(_)` by
66
- /// the time the visitor has looked through the enclosing block and found a slow
67
- /// initialization, so it is safe to unwrap later at lint time.
68
- size_expr : Option < & ' tcx Expr < ' tcx > > ,
64
+ size_expr : InitializedSize < ' tcx > ,
65
+ }
66
+
67
+ /// Initializer for the creation of the vector.
68
+ ///
69
+ /// When `Vec::with_capacity(size)` is found, the `size` expression will be in
70
+ /// `InitializedSize::Initialized`.
71
+ ///
72
+ /// Otherwise, for `Vec::new()` calls, there is no allocation initializer yet, so
73
+ /// `InitializedSize::Uninitialized` is used.
74
+ /// Later, when a call to `.resize(size, 0)` or similar is found, it's set
75
+ /// to `InitializedSize::Initialized(size)`.
76
+ ///
77
+ /// Since it will be set to `InitializedSize::Initialized(size)` when a slow initialization is
78
+ /// found, it is always safe to "unwrap" it at lint time.
79
+ enum InitializedSize < ' tcx > {
80
+ Initialized ( & ' tcx Expr < ' tcx > ) ,
81
+ Uninitialized ,
69
82
}
70
83
71
84
/// Type of slow initialization
@@ -120,20 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
120
133
}
121
134
122
135
impl SlowVectorInit {
123
- /// Given an expression, returns:
124
- /// - `Some(Some(size))` if it is a function call to `Vec::with_capacity(size)`
125
- /// - `Some(None)` if it is a call to `Vec::new()`
126
- /// - `None` if it is neither
127
- #[ allow(
128
- clippy:: option_option,
129
- reason = "outer option is immediately removed at call site and the inner option is kept around, \
130
- so extracting into a dedicated enum seems unnecessarily complicated"
131
- ) ]
132
- fn as_vec_initializer < ' tcx > ( cx : & LateContext < ' _ > , expr : & ' tcx Expr < ' tcx > ) -> Option < Option < & ' tcx Expr < ' tcx > > > {
136
+ fn as_vec_initializer < ' tcx > ( cx : & LateContext < ' _ > , expr : & ' tcx Expr < ' tcx > ) -> Option < InitializedSize < ' tcx > > {
133
137
if let Some ( len_expr) = Self :: is_vec_with_capacity ( cx, expr) {
134
- Some ( Some ( len_expr) )
135
- } else if Self :: is_vec_new ( cx, expr ) {
136
- Some ( None )
138
+ Some ( InitializedSize :: Initialized ( len_expr) )
139
+ } else if matches ! ( expr . kind , ExprKind :: Call ( func , _ ) if is_expr_path_def_path ( cx, func , & paths :: VEC_NEW ) ) {
140
+ Some ( InitializedSize :: Uninitialized )
137
141
} else {
138
142
None
139
143
}
@@ -155,14 +159,6 @@ impl SlowVectorInit {
155
159
}
156
160
}
157
161
158
- /// Checks if the given expression is `Vec::new()`
159
- fn is_vec_new ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
160
- matches ! (
161
- expr. kind,
162
- ExprKind :: Call ( func, _) if is_expr_path_def_path( cx, func, & paths:: VEC_NEW )
163
- )
164
- }
165
-
166
162
/// Search initialization for the given vector
167
163
fn search_initialization < ' tcx > ( cx : & LateContext < ' tcx > , vec_alloc : VecAllocation < ' tcx > , parent_node : HirId ) {
168
164
let enclosing_body = get_enclosing_block ( cx, parent_node) ;
@@ -200,9 +196,10 @@ impl SlowVectorInit {
200
196
fn emit_lint ( cx : & LateContext < ' _ > , slow_fill : & Expr < ' _ > , vec_alloc : & VecAllocation < ' _ > , msg : & str ) {
201
197
let len_expr = Sugg :: hir (
202
198
cx,
203
- vec_alloc
204
- . size_expr
205
- . expect ( "length expression must be set by this point" ) ,
199
+ match vec_alloc. size_expr {
200
+ InitializedSize :: Initialized ( expr) => expr,
201
+ InitializedSize :: Uninitialized => unreachable ! ( "size expression must be set by this point" ) ,
202
+ } ,
206
203
"len" ,
207
204
) ;
208
205
@@ -257,12 +254,12 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
257
254
// Check that is filled with 0
258
255
&& is_integer_literal ( fill_arg, 0 )
259
256
{
260
- let is_matching_resize = if let Some ( size_expr) = self . vec_alloc . size_expr {
257
+ let is_matching_resize = if let InitializedSize :: Initialized ( size_expr) = self . vec_alloc . size_expr {
261
258
// If we have a size expression, check that it is equal to what's passed to `resize`
262
259
SpanlessEq :: new ( self . cx ) . eq_expr ( len_arg, size_expr)
263
260
|| matches ! ( len_arg. kind, ExprKind :: MethodCall ( path, ..) if path. ident. as_str( ) == "capacity" )
264
261
} else {
265
- self . vec_alloc . size_expr = Some ( len_arg) ;
262
+ self . vec_alloc . size_expr = InitializedSize :: Initialized ( len_arg) ;
266
263
true
267
264
} ;
268
265
@@ -280,13 +277,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
280
277
// Check that take is applied to `repeat(0)`
281
278
if self . is_repeat_zero( recv) ;
282
279
then {
283
- if let Some ( size_expr) = self . vec_alloc. size_expr {
280
+ if let InitializedSize :: Initialized ( size_expr) = self . vec_alloc. size_expr {
284
281
// Check that len expression is equals to `with_capacity` expression
285
282
return SpanlessEq :: new( self . cx) . eq_expr( len_arg, size_expr)
286
283
|| matches!( len_arg. kind, ExprKind :: MethodCall ( path, ..) if path. ident. as_str( ) == "capacity" )
287
284
}
288
285
289
- self . vec_alloc. size_expr = Some ( len_arg) ;
286
+ self . vec_alloc. size_expr = InitializedSize :: Initialized ( len_arg) ;
290
287
return true ;
291
288
}
292
289
}
0 commit comments