Skip to content

Commit c984816

Browse files
committed
Improved error message for set_len() on empty Vec
1 parent b2e91e9 commit c984816

File tree

2 files changed

+73
-40
lines changed

2 files changed

+73
-40
lines changed

clippy_lints/src/uninit_vec.rs

+70-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::higher::get_vec_init_kind;
1+
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
2+
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
33
use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty};
44
use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq};
55
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind};
@@ -83,69 +83,108 @@ fn handle_uninit_vec_pair(
8383
if_chain! {
8484
if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve);
8585
if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len);
86-
if vec.eq_expr(cx, set_len_self);
86+
if vec.location.eq_expr(cx, set_len_self);
8787
if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind();
8888
if let ty::Adt(_, substs) = vec_ty.kind();
89-
// Check T of Vec<T>
90-
if !is_uninit_value_valid_for_ty(cx, substs.type_at(0));
9189
// `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()`
9290
if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id);
9391
then {
94-
// FIXME: #7698, false positive of the internal lints
95-
#[allow(clippy::collapsible_span_lint_calls)]
96-
span_lint_and_then(
97-
cx,
98-
UNINIT_VEC,
99-
vec![call_span, maybe_init_or_reserve.span],
100-
"calling `set_len()` immediately after reserving a buffer creates uninitialized values",
101-
|diag| {
102-
diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
103-
},
104-
);
92+
if vec.has_capacity() {
93+
// with_capacity / reserve -> set_len
94+
95+
// Check T of Vec<T>
96+
if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) {
97+
// FIXME: #7698, false positive of the internal lints
98+
#[allow(clippy::collapsible_span_lint_calls)]
99+
span_lint_and_then(
100+
cx,
101+
UNINIT_VEC,
102+
vec![call_span, maybe_init_or_reserve.span],
103+
"calling `set_len()` immediately after reserving a buffer creates uninitialized values",
104+
|diag| {
105+
diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
106+
},
107+
);
108+
}
109+
} else {
110+
// new / default -> set_len
111+
span_lint(
112+
cx,
113+
UNINIT_VEC,
114+
vec![call_span, maybe_init_or_reserve.span],
115+
"calling `set_len()` on empty `Vec` creates out-of-bound values",
116+
)
117+
}
105118
}
106119
}
107120
}
108121

109-
#[derive(Clone, Copy, Debug)]
110-
enum LocalOrExpr<'tcx> {
122+
/// The target `Vec` that is initialized or reserved
123+
#[derive(Clone, Copy)]
124+
struct TargetVec<'tcx> {
125+
location: VecLocation<'tcx>,
126+
/// `None` if `reserve()`
127+
init_kind: Option<VecInitKind>,
128+
}
129+
130+
impl TargetVec<'_> {
131+
pub fn has_capacity(self) -> bool {
132+
!matches!(self.init_kind, Some(VecInitKind::New | VecInitKind::Default))
133+
}
134+
}
135+
136+
#[derive(Clone, Copy)]
137+
enum VecLocation<'tcx> {
111138
Local(HirId),
112139
Expr(&'tcx Expr<'tcx>),
113140
}
114141

115-
impl<'tcx> LocalOrExpr<'tcx> {
116-
fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
142+
impl<'tcx> VecLocation<'tcx> {
143+
pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
117144
match self {
118-
LocalOrExpr::Local(hir_id) => path_to_local_id(expr, hir_id),
119-
LocalOrExpr::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
145+
VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id),
146+
VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
120147
}
121148
}
122149
}
123150

124151
/// Finds the target location where the result of `Vec` initialization is stored
125152
/// or `self` expression for `Vec::reserve()`.
126-
fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<LocalOrExpr<'tcx>> {
153+
fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> {
127154
match stmt.kind {
128155
StmtKind::Local(local) => {
129156
if_chain! {
130157
if let Some(init_expr) = local.init;
131158
if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind;
132-
if get_vec_init_kind(cx, init_expr).is_some();
159+
if let Some(init_kind) = get_vec_init_kind(cx, init_expr);
133160
then {
134-
Some(LocalOrExpr::Local(hir_id))
135-
} else {
136-
None
161+
return Some(TargetVec {
162+
location: VecLocation::Local(hir_id),
163+
init_kind: Some(init_kind),
164+
})
137165
}
138166
}
139167
},
140168
StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
141-
ExprKind::Assign(lhs, rhs, _span) if get_vec_init_kind(cx, rhs).is_some() => Some(LocalOrExpr::Expr(lhs)),
169+
ExprKind::Assign(lhs, rhs, _span) => {
170+
if let Some(init_kind) = get_vec_init_kind(cx, rhs) {
171+
return Some(TargetVec {
172+
location: VecLocation::Expr(lhs),
173+
init_kind: Some(init_kind),
174+
});
175+
}
176+
},
142177
ExprKind::MethodCall(path, _, [self_expr, _], _) if is_reserve(cx, path, self_expr) => {
143-
Some(LocalOrExpr::Expr(self_expr))
178+
return Some(TargetVec {
179+
location: VecLocation::Expr(self_expr),
180+
init_kind: None,
181+
});
144182
},
145-
_ => None,
183+
_ => (),
146184
},
147-
StmtKind::Item(_) => None,
185+
StmtKind::Item(_) => (),
148186
}
187+
None
149188
}
150189

151190
fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool {

tests/ui/uninit_vec.stderr

+3-9
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,32 @@ LL | vec.set_len(200);
2121
|
2222
= help: initialize the buffer or wrap the content in `MaybeUninit`
2323

24-
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
24+
error: calling `set_len()` on empty `Vec` creates out-of-bound values
2525
--> $DIR/uninit_vec.rs:24:5
2626
|
2727
LL | let mut vec: Vec<u8> = Vec::new();
2828
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2929
LL | unsafe {
3030
LL | vec.set_len(200);
3131
| ^^^^^^^^^^^^^^^^
32-
|
33-
= help: initialize the buffer or wrap the content in `MaybeUninit`
3432

35-
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
33+
error: calling `set_len()` on empty `Vec` creates out-of-bound values
3634
--> $DIR/uninit_vec.rs:30:5
3735
|
3836
LL | let mut vec: Vec<u8> = Default::default();
3937
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4038
LL | unsafe {
4139
LL | vec.set_len(200);
4240
| ^^^^^^^^^^^^^^^^
43-
|
44-
= help: initialize the buffer or wrap the content in `MaybeUninit`
4541

46-
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
42+
error: calling `set_len()` on empty `Vec` creates out-of-bound values
4743
--> $DIR/uninit_vec.rs:35:5
4844
|
4945
LL | let mut vec: Vec<u8> = Vec::default();
5046
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5147
LL | unsafe {
5248
LL | vec.set_len(200);
5349
| ^^^^^^^^^^^^^^^^
54-
|
55-
= help: initialize the buffer or wrap the content in `MaybeUninit`
5650

5751
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
5852
--> $DIR/uninit_vec.rs:49:5

0 commit comments

Comments
 (0)