1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
+ use clippy_utils:: get_vec_init_kind;
2
3
use clippy_utils:: ty:: is_type_diagnostic_item;
3
- use clippy_utils:: {
4
- match_def_path, path_to_local_id, paths, peel_hir_expr_while, ty:: is_uninit_value_valid_for_ty, SpanlessEq ,
5
- } ;
6
- use rustc_hir:: def:: Res ;
7
- use rustc_hir:: { Block , Expr , ExprKind , HirId , PatKind , Stmt , StmtKind } ;
4
+ use clippy_utils:: { path_to_local_id, peel_hir_expr_while, ty:: is_uninit_value_valid_for_ty, SpanlessEq } ;
5
+ use rustc_hir:: { Block , Expr , ExprKind , HirId , PatKind , PathSegment , Stmt , StmtKind } ;
8
6
use rustc_lint:: { LateContext , LateLintPass } ;
9
7
use rustc_middle:: ty;
10
8
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11
9
use rustc_span:: { sym, Span } ;
12
10
11
+ // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std
13
12
declare_clippy_lint ! {
14
13
/// ### What it does
15
- /// Checks for the creation of uninitialized `Vec<T>` by calling `set_len()`
16
- /// immediately after `with_capacity()` or `reserve()`.
14
+ /// Checks for `set_len()` call that creates `Vec` with uninitialized elements.
15
+ /// This is commonly caused by calling `set_len()` right after after calling
16
+ /// `with_capacity()` or `reserve()`.
17
17
///
18
18
/// ### Why is this bad?
19
- /// It creates `Vec<T>` that contains uninitialized data, which leads to an
19
+ /// It creates a `Vec` with uninitialized data, which leads to an
20
20
/// undefined behavior with most safe operations.
21
- /// Notably, using uninitialized `Vec<u8>` with generic `Read` is unsound .
21
+ /// Notably, uninitialized `Vec<u8>` must not be used with generic `Read`.
22
22
///
23
23
/// ### Example
24
24
/// ```rust,ignore
25
25
/// let mut vec: Vec<u8> = Vec::with_capacity(1000);
26
26
/// unsafe { vec.set_len(1000); }
27
27
/// reader.read(&mut vec); // undefined behavior!
28
28
/// ```
29
- /// Use an initialized buffer:
30
- /// ```rust,ignore
31
- /// let mut vec: Vec<u8> = vec![0; 1000];
32
- /// reader.read(&mut vec);
33
- /// ```
34
- /// Or, wrap the content in `MaybeUninit`:
35
- /// ```rust,ignore
36
- /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
37
- /// unsafe { vec.set_len(1000); }
38
- /// ```
29
+ ///
30
+ /// ### How to fix?
31
+ /// 1. Use an initialized buffer:
32
+ /// ```rust,ignore
33
+ /// let mut vec: Vec<u8> = vec![0; 1000];
34
+ /// reader.read(&mut vec);
35
+ /// ```
36
+ /// 2. Wrap the content in `MaybeUninit`:
37
+ /// ```rust,ignore
38
+ /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
39
+ /// vec.set_len(1000); // `MaybeUninit` can be uninitialized
40
+ /// ```
41
+ /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
42
+ /// ```rust,ignore
43
+ /// let mut vec: Vec<u8> = Vec::with_capacity(1000);
44
+ /// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit<u8>]`
45
+ /// // perform initialization with `remaining`
46
+ /// vec.set_len(...); // Safe to call `set_len()` on initialized part
47
+ /// ```
39
48
pub UNINIT_VEC ,
40
49
correctness,
41
50
"Vec with uninitialized data"
@@ -59,24 +68,24 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec {
59
68
60
69
fn handle_uninit_vec_pair (
61
70
cx : & LateContext < ' tcx > ,
62
- maybe_with_capacity_or_reserve : & ' tcx Stmt < ' tcx > ,
71
+ maybe_init_or_reserve : & ' tcx Stmt < ' tcx > ,
63
72
maybe_set_len : & ' tcx Expr < ' tcx > ,
64
73
) {
65
74
if_chain ! {
66
- if let Some ( vec) = extract_with_capacity_or_reserve_target ( cx, maybe_with_capacity_or_reserve ) ;
75
+ if let Some ( vec) = extract_init_or_reserve_target ( cx, maybe_init_or_reserve ) ;
67
76
if let Some ( ( set_len_self, call_span) ) = extract_set_len_self( cx, maybe_set_len) ;
68
77
if vec. eq_expr( cx, set_len_self) ;
69
78
if let ty:: Ref ( _, vec_ty, _) = cx. typeck_results( ) . expr_ty_adjusted( set_len_self) . kind( ) ;
70
79
if let ty:: Adt ( _, substs) = vec_ty. kind( ) ;
71
80
// Check T of Vec<T>
72
81
if !is_uninit_value_valid_for_ty( cx, substs. type_at( 0 ) ) ;
73
82
then {
74
- // FIXME: false positive #7698
83
+ // FIXME: #7698, false positive of the internal lints
75
84
#[ allow( clippy:: collapsible_span_lint_calls) ]
76
85
span_lint_and_then(
77
86
cx,
78
87
UNINIT_VEC ,
79
- vec![ call_span, maybe_with_capacity_or_reserve . span] ,
88
+ vec![ call_span, maybe_init_or_reserve . span] ,
80
89
"calling `set_len()` immediately after reserving a buffer creates uninitialized values" ,
81
90
|diag| {
82
91
diag. help( "initialize the buffer or wrap the content in `MaybeUninit`" ) ;
@@ -101,56 +110,36 @@ impl<'tcx> LocalOrExpr<'tcx> {
101
110
}
102
111
}
103
112
104
- /// Returns the target vec of `Vec::with_capacity()` or `Vec::reserve()`
105
- fn extract_with_capacity_or_reserve_target ( cx : & LateContext < ' _ > , stmt : & ' tcx Stmt < ' _ > ) -> Option < LocalOrExpr < ' tcx > > {
113
+ /// Finds the target location where the result of `Vec` initialization is stored
114
+ /// or `self` expression for `Vec::reserve()`.
115
+ fn extract_init_or_reserve_target < ' tcx > ( cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' tcx > ) -> Option < LocalOrExpr < ' tcx > > {
106
116
match stmt. kind {
107
117
StmtKind :: Local ( local) => {
108
- // let mut x = Vec::with_capacity()
109
118
if_chain ! {
110
119
if let Some ( init_expr) = local. init;
111
120
if let PatKind :: Binding ( _, hir_id, _, None ) = local. pat. kind;
112
- if is_with_capacity ( cx, init_expr) ;
121
+ if get_vec_init_kind ( cx, init_expr) . is_some ( ) ;
113
122
then {
114
123
Some ( LocalOrExpr :: Local ( hir_id) )
115
124
} else {
116
125
None
117
126
}
118
127
}
119
128
} ,
120
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => {
121
- match expr. kind {
122
- ExprKind :: Assign ( lhs, rhs, _span) if is_with_capacity ( cx, rhs) => {
123
- // self.vec = Vec::with_capacity()
124
- Some ( LocalOrExpr :: Expr ( lhs) )
125
- } ,
126
- ExprKind :: MethodCall ( path, _, [ self_expr, _] , _) => {
127
- // self.vec.reserve()
128
- if is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( self_expr) . peel_refs ( ) , sym:: vec_type)
129
- && path. ident . name . as_str ( ) == "reserve"
130
- {
131
- Some ( LocalOrExpr :: Expr ( self_expr) )
132
- } else {
133
- None
134
- }
135
- } ,
136
- _ => None ,
137
- }
129
+ StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => match expr. kind {
130
+ ExprKind :: Assign ( lhs, rhs, _span) if get_vec_init_kind ( cx, rhs) . is_some ( ) => Some ( LocalOrExpr :: Expr ( lhs) ) ,
131
+ ExprKind :: MethodCall ( path, _, [ self_expr, _] , _) if is_reserve ( cx, path, self_expr) => {
132
+ Some ( LocalOrExpr :: Expr ( self_expr) )
133
+ } ,
134
+ _ => None ,
138
135
} ,
139
136
StmtKind :: Item ( _) => None ,
140
137
}
141
138
}
142
139
143
- fn is_with_capacity ( cx : & LateContext < ' _ > , expr : & ' tcx Expr < ' _ > ) -> bool {
144
- if_chain ! {
145
- if let ExprKind :: Call ( path_expr, _) = & expr. kind;
146
- if let ExprKind :: Path ( qpath) = & path_expr. kind;
147
- if let Res :: Def ( _, def_id) = cx. qpath_res( qpath, path_expr. hir_id) ;
148
- then {
149
- match_def_path( cx, def_id, & paths:: VEC_WITH_CAPACITY )
150
- } else {
151
- false
152
- }
153
- }
140
+ fn is_reserve ( cx : & LateContext < ' _ > , path : & PathSegment < ' _ > , self_expr : & Expr < ' _ > ) -> bool {
141
+ is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( self_expr) . peel_refs ( ) , sym:: Vec )
142
+ && path. ident . name . as_str ( ) == "reserve"
154
143
}
155
144
156
145
/// Returns self if the expression is `Vec::set_len()`
@@ -169,14 +158,13 @@ fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&
169
158
}
170
159
} ) ;
171
160
match expr. kind {
172
- ExprKind :: MethodCall ( _, _, [ vec_expr, _] , _) => {
173
- cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) . and_then ( |id| {
174
- if match_def_path ( cx, id, & paths:: VEC_SET_LEN ) {
175
- Some ( ( vec_expr, expr. span ) )
176
- } else {
177
- None
178
- }
179
- } )
161
+ ExprKind :: MethodCall ( path, _, [ self_expr, _] , _) => {
162
+ let self_type = cx. typeck_results ( ) . expr_ty ( self_expr) . peel_refs ( ) ;
163
+ if is_type_diagnostic_item ( cx, self_type, sym:: Vec ) && path. ident . name . as_str ( ) == "set_len" {
164
+ Some ( ( self_expr, expr. span ) )
165
+ } else {
166
+ None
167
+ }
180
168
} ,
181
169
_ => None ,
182
170
}
0 commit comments