11use clippy_utils:: diagnostics:: span_lint_and_then;
2+ use clippy_utils:: get_vec_init_kind;
23use 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 } ;
86use rustc_lint:: { LateContext , LateLintPass } ;
97use rustc_middle:: ty;
108use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
119use rustc_span:: { sym, Span } ;
1210
11+ // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std
1312declare_clippy_lint ! {
1413 /// ### 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()`.
1717 ///
1818 /// ### 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
2020 /// 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`.
2222 ///
2323 /// ### Example
2424 /// ```rust,ignore
2525 /// let mut vec: Vec<u8> = Vec::with_capacity(1000);
2626 /// unsafe { vec.set_len(1000); }
2727 /// reader.read(&mut vec); // undefined behavior!
2828 /// ```
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+ /// ```
3948 pub UNINIT_VEC ,
4049 correctness,
4150 "Vec with uninitialized data"
@@ -59,24 +68,24 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec {
5968
6069fn handle_uninit_vec_pair (
6170 cx : & LateContext < ' tcx > ,
62- maybe_with_capacity_or_reserve : & ' tcx Stmt < ' tcx > ,
71+ maybe_init_or_reserve : & ' tcx Stmt < ' tcx > ,
6372 maybe_set_len : & ' tcx Expr < ' tcx > ,
6473) {
6574 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 ) ;
6776 if let Some ( ( set_len_self, call_span) ) = extract_set_len_self( cx, maybe_set_len) ;
6877 if vec. eq_expr( cx, set_len_self) ;
6978 if let ty:: Ref ( _, vec_ty, _) = cx. typeck_results( ) . expr_ty_adjusted( set_len_self) . kind( ) ;
7079 if let ty:: Adt ( _, substs) = vec_ty. kind( ) ;
7180 // Check T of Vec<T>
7281 if !is_uninit_value_valid_for_ty( cx, substs. type_at( 0 ) ) ;
7382 then {
74- // FIXME: false positive #7698
83+ // FIXME: #7698, false positive of the internal lints
7584 #[ allow( clippy:: collapsible_span_lint_calls) ]
7685 span_lint_and_then(
7786 cx,
7887 UNINIT_VEC ,
79- vec![ call_span, maybe_with_capacity_or_reserve . span] ,
88+ vec![ call_span, maybe_init_or_reserve . span] ,
8089 "calling `set_len()` immediately after reserving a buffer creates uninitialized values" ,
8190 |diag| {
8291 diag. help( "initialize the buffer or wrap the content in `MaybeUninit`" ) ;
@@ -101,56 +110,36 @@ impl<'tcx> LocalOrExpr<'tcx> {
101110 }
102111}
103112
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 > > {
106116 match stmt. kind {
107117 StmtKind :: Local ( local) => {
108- // let mut x = Vec::with_capacity()
109118 if_chain ! {
110119 if let Some ( init_expr) = local. init;
111120 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 ( ) ;
113122 then {
114123 Some ( LocalOrExpr :: Local ( hir_id) )
115124 } else {
116125 None
117126 }
118127 }
119128 } ,
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 ,
138135 } ,
139136 StmtKind :: Item ( _) => None ,
140137 }
141138}
142139
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"
154143}
155144
156145/// Returns self if the expression is `Vec::set_len()`
@@ -169,14 +158,13 @@ fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&
169158 }
170159 } ) ;
171160 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+ }
180168 } ,
181169 _ => None ,
182170 }
0 commit comments