11use clippy_utils:: diagnostics:: { span_lint, span_lint_and_sugg} ;
2+ use clippy_utils:: get_enclosing_block;
23use clippy_utils:: higher:: { get_vec_init_kind, VecInitKind } ;
34use clippy_utils:: source:: snippet;
4- use clippy_utils:: visitors:: for_each_expr;
5- use core:: ops:: ControlFlow ;
6- use hir:: { Expr , ExprKind , Local , PatKind , PathSegment , QPath , StmtKind } ;
5+
6+ use hir:: { Expr , ExprKind , HirId , Local , PatKind , PathSegment , QPath , StmtKind } ;
77use rustc_errors:: Applicability ;
88use rustc_hir as hir;
9+ use rustc_hir:: def:: Res ;
10+ use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
911use rustc_lint:: { LateContext , LateLintPass } ;
1012use rustc_session:: declare_lint_pass;
1113
@@ -49,57 +51,40 @@ declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
4951
5052impl < ' tcx > LateLintPass < ' tcx > for ReadZeroByteVec {
5153 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & hir:: Block < ' tcx > ) {
52- for ( idx, stmt) in block. stmts . iter ( ) . enumerate ( ) {
53- if !stmt. span . from_expansion ( )
54- // matches `let v = Vec::new();`
55- && let StmtKind :: Local ( local) = stmt. kind
56- && let Local { pat, init : Some ( init) , .. } = local
57- && let PatKind :: Binding ( _, _, ident, _) = pat. kind
54+ for stmt in block. stmts {
55+ if stmt. span . from_expansion ( ) {
56+ return ;
57+ }
58+
59+ if let StmtKind :: Local ( local) = stmt. kind
60+ && let Local {
61+ pat, init : Some ( init) , ..
62+ } = local
63+ && let PatKind :: Binding ( _, id, ident, _) = pat. kind
5864 && let Some ( vec_init_kind) = get_vec_init_kind ( cx, init)
5965 {
60- let visitor = |expr : & Expr < ' _ > | {
61- if let ExprKind :: MethodCall ( path, _, [ arg] , _) = expr. kind
62- && let PathSegment {
63- ident : read_or_read_exact,
64- ..
65- } = * path
66- && matches ! ( read_or_read_exact. as_str( ) , "read" | "read_exact" )
67- && let ExprKind :: AddrOf ( _, hir:: Mutability :: Mut , inner) = arg. kind
68- && let ExprKind :: Path ( QPath :: Resolved ( None , inner_path) ) = inner. kind
69- && let [ inner_seg] = inner_path. segments
70- && ident. name == inner_seg. ident . name
71- {
72- ControlFlow :: Break ( ( ) )
73- } else {
74- ControlFlow :: Continue ( ( ) )
75- }
66+ let mut visitor = ReadVecVisitor {
67+ local_id : id,
68+ read_zero_expr : None ,
69+ has_resize : false ,
7670 } ;
7771
78- let ( read_found, next_stmt_span) = if let Some ( next_stmt) = block. stmts . get ( idx + 1 ) {
79- // case { .. stmt; stmt; .. }
80- ( for_each_expr ( next_stmt, visitor) . is_some ( ) , next_stmt. span )
81- } else if let Some ( e) = block. expr {
82- // case { .. stmt; expr }
83- ( for_each_expr ( e, visitor) . is_some ( ) , e. span )
84- } else {
72+ let Some ( enclosing_block) = get_enclosing_block ( cx, id) else {
8573 return ;
8674 } ;
75+ visitor. visit_block ( enclosing_block) ;
8776
88- if read_found && !next_stmt_span . from_expansion ( ) {
77+ if let Some ( expr ) = visitor . read_zero_expr {
8978 let applicability = Applicability :: MaybeIncorrect ;
9079 match vec_init_kind {
9180 VecInitKind :: WithConstCapacity ( len) => {
9281 span_lint_and_sugg (
9382 cx,
9483 READ_ZERO_BYTE_VEC ,
95- next_stmt_span ,
84+ expr . span ,
9685 "reading zero byte data to `Vec`" ,
9786 "try" ,
98- format ! (
99- "{}.resize({len}, 0); {}" ,
100- ident. as_str( ) ,
101- snippet( cx, next_stmt_span, ".." )
102- ) ,
87+ format ! ( "{}.resize({len}, 0); {}" , ident. as_str( ) , snippet( cx, expr. span, ".." ) ) ,
10388 applicability,
10489 ) ;
10590 } ,
@@ -108,29 +93,68 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
10893 span_lint_and_sugg (
10994 cx,
11095 READ_ZERO_BYTE_VEC ,
111- next_stmt_span ,
96+ expr . span ,
11297 "reading zero byte data to `Vec`" ,
11398 "try" ,
11499 format ! (
115100 "{}.resize({}, 0); {}" ,
116101 ident. as_str( ) ,
117102 snippet( cx, e. span, ".." ) ,
118- snippet( cx, next_stmt_span , ".." )
103+ snippet( cx, expr . span , ".." )
119104 ) ,
120105 applicability,
121106 ) ;
122107 } ,
123108 _ => {
124- span_lint (
125- cx,
126- READ_ZERO_BYTE_VEC ,
127- next_stmt_span,
128- "reading zero byte data to `Vec`" ,
129- ) ;
109+ span_lint ( cx, READ_ZERO_BYTE_VEC , expr. span , "reading zero byte data to `Vec`" ) ;
130110 } ,
131111 }
132112 }
133113 }
134114 }
135115 }
136116}
117+
118+ struct ReadVecVisitor < ' tcx > {
119+ local_id : HirId ,
120+ read_zero_expr : Option < & ' tcx Expr < ' tcx > > ,
121+ has_resize : bool ,
122+ }
123+
124+ impl < ' tcx > Visitor < ' tcx > for ReadVecVisitor < ' tcx > {
125+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' tcx > ) {
126+ if let ExprKind :: MethodCall ( path, receiver, args, _) = e. kind {
127+ let PathSegment { ident, .. } = * path;
128+
129+ match ident. as_str ( ) {
130+ "read" | "read_exact" => {
131+ let [ arg] = args else { return } ;
132+ if let ExprKind :: AddrOf ( _, hir:: Mutability :: Mut , inner) = arg. kind
133+ && let ExprKind :: Path ( QPath :: Resolved ( None , inner_path) ) = inner. kind
134+ && let [ inner_seg] = inner_path. segments
135+ && let Res :: Local ( res_id) = inner_seg. res
136+ && self . local_id == res_id
137+ {
138+ self . read_zero_expr = Some ( e) ;
139+ return ;
140+ }
141+ } ,
142+ "resize" => {
143+ // If the Vec is resized, then it's a valid read
144+ if let ExprKind :: Path ( QPath :: Resolved ( _, inner_path) ) = receiver. kind
145+ && let Res :: Local ( res_id) = inner_path. res
146+ && self . local_id == res_id
147+ {
148+ self . has_resize = true ;
149+ return ;
150+ }
151+ } ,
152+ _ => { } ,
153+ }
154+ }
155+
156+ if !self . has_resize && self . read_zero_expr . is_none ( ) {
157+ walk_expr ( self , e) ;
158+ }
159+ }
160+ }
0 commit comments