1
1
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_sugg} ;
2
+ use clippy_utils:: get_enclosing_block;
2
3
use clippy_utils:: higher:: { get_vec_init_kind, VecInitKind } ;
3
4
use 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 } ;
7
7
use rustc_errors:: Applicability ;
8
8
use rustc_hir as hir;
9
+ use rustc_hir:: def:: Res ;
10
+ use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
9
11
use rustc_lint:: { LateContext , LateLintPass } ;
10
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11
13
@@ -49,57 +51,40 @@ declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
49
51
50
52
impl < ' tcx > LateLintPass < ' tcx > for ReadZeroByteVec {
51
53
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 . iter ( ) {
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
58
64
&& let Some ( vec_init_kind) = get_vec_init_kind ( cx, init)
59
65
{
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 ,
76
70
} ;
77
71
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 {
85
73
return ;
86
74
} ;
75
+ visitor. visit_block ( enclosing_block) ;
87
76
88
- if read_found && !next_stmt_span . from_expansion ( ) {
77
+ if let Some ( expr ) = visitor . read_zero_expr {
89
78
let applicability = Applicability :: MaybeIncorrect ;
90
79
match vec_init_kind {
91
80
VecInitKind :: WithConstCapacity ( len) => {
92
81
span_lint_and_sugg (
93
82
cx,
94
83
READ_ZERO_BYTE_VEC ,
95
- next_stmt_span ,
84
+ expr . span ,
96
85
"reading zero byte data to `Vec`" ,
97
86
"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, ".." ) ) ,
103
88
applicability,
104
89
) ;
105
90
} ,
@@ -108,29 +93,68 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
108
93
span_lint_and_sugg (
109
94
cx,
110
95
READ_ZERO_BYTE_VEC ,
111
- next_stmt_span ,
96
+ expr . span ,
112
97
"reading zero byte data to `Vec`" ,
113
98
"try" ,
114
99
format ! (
115
100
"{}.resize({}, 0); {}" ,
116
101
ident. as_str( ) ,
117
102
snippet( cx, e. span, ".." ) ,
118
- snippet( cx, next_stmt_span , ".." )
103
+ snippet( cx, expr . span , ".." )
119
104
) ,
120
105
applicability,
121
106
) ;
122
107
} ,
123
108
_ => {
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`" ) ;
130
110
} ,
131
111
}
132
112
}
133
113
}
134
114
}
135
115
}
136
116
}
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_some ( ) {
157
+ walk_expr ( self , e) ;
158
+ }
159
+ }
160
+ }
0 commit comments