Skip to content

Commit 1fe2762

Browse files
committed
use a dedicated enum for vec initializer
1 parent 541d0c8 commit 1fe2762

File tree

1 file changed

+30
-33
lines changed

1 file changed

+30
-33
lines changed

clippy_lints/src/slow_vector_initialization.rs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,24 @@ struct VecAllocation<'tcx> {
6161

6262
/// Reference to the expression used as argument on `with_capacity` call. This is used
6363
/// 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,
6982
}
7083

7184
/// Type of slow initialization
@@ -120,20 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
120133
}
121134

122135
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>> {
133137
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)
137141
} else {
138142
None
139143
}
@@ -155,14 +159,6 @@ impl SlowVectorInit {
155159
}
156160
}
157161

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-
166162
/// Search initialization for the given vector
167163
fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
168164
let enclosing_body = get_enclosing_block(cx, parent_node);
@@ -200,9 +196,10 @@ impl SlowVectorInit {
200196
fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
201197
let len_expr = Sugg::hir(
202198
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+
},
206203
"len",
207204
);
208205

@@ -257,12 +254,12 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
257254
// Check that is filled with 0
258255
&& is_integer_literal(fill_arg, 0)
259256
{
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 {
261258
// If we have a size expression, check that it is equal to what's passed to `resize`
262259
SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
263260
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
264261
} else {
265-
self.vec_alloc.size_expr = Some(len_arg);
262+
self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
266263
true
267264
};
268265

@@ -280,13 +277,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
280277
// Check that take is applied to `repeat(0)`
281278
if self.is_repeat_zero(recv);
282279
then {
283-
if let Some(size_expr) = self.vec_alloc.size_expr {
280+
if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
284281
// Check that len expression is equals to `with_capacity` expression
285282
return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
286283
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
287284
}
288285

289-
self.vec_alloc.size_expr = Some(len_arg);
286+
self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
290287
return true;
291288
}
292289
}

0 commit comments

Comments
 (0)