Skip to content

Commit 23e1ff5

Browse files
committed
ok
1 parent c36c6d8 commit 23e1ff5

File tree

3 files changed

+249
-4
lines changed

3 files changed

+249
-4
lines changed

clippy_lints/src/read_zero_byte_vec.rs

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
use rustc_hir::*;
1+
use clippy_utils::{
2+
diagnostics::{span_lint, span_lint_and_sugg},
3+
higher::{get_vec_init_kind, VecInitKind},
4+
source::snippet,
5+
visitors::expr_visitor_no_bodies,
6+
};
7+
use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
8+
use rustc_errors::Applicability;
9+
use rustc_hir as hir;
210
use rustc_lint::{LateContext, LateLintPass};
311
use rustc_session::{declare_lint_pass, declare_tool_lint};
412

@@ -34,4 +42,95 @@ declare_clippy_lint! {
3442
}
3543
declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
3644

37-
impl LateLintPass<'_> for ReadZeroByteVec {}
45+
impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
46+
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
47+
for (idx, stmt) in block.stmts.iter().enumerate() {
48+
if !stmt.span.from_expansion()
49+
// matches `let v = Vec::new();`
50+
&& let StmtKind::Local(local) = stmt.kind
51+
&& let Local { pat, init: Some(init), .. } = local
52+
&& let PatKind::Binding(_, _, ident, _) = pat.kind
53+
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
54+
{
55+
// finds use of `_.read(&mut v)`
56+
let mut read_found = false;
57+
let mut visitor = expr_visitor_no_bodies(|expr| {
58+
if let ExprKind::MethodCall(path, [_self, arg], _) = expr.kind
59+
&& let PathSegment { ident: read_or_read_exact, .. } = *path
60+
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
61+
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
62+
&& let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
63+
&& let [inner_seg] = inner_path.segments
64+
&& ident.name == inner_seg.ident.name
65+
{
66+
read_found = true;
67+
}
68+
!read_found
69+
});
70+
71+
let next_stmt_span;
72+
if idx == block.stmts.len() - 1 {
73+
// case { .. stmt; expr }
74+
if let Some(e) = block.expr {
75+
visitor.visit_expr(e);
76+
next_stmt_span = e.span;
77+
} else {
78+
return;
79+
}
80+
} else {
81+
// case { .. stmt; stmt; .. }
82+
let next_stmt = &block.stmts[idx + 1];
83+
visitor.visit_stmt(next_stmt);
84+
next_stmt_span = next_stmt.span;
85+
}
86+
drop(visitor);
87+
88+
if read_found && !next_stmt_span.from_expansion() {
89+
let applicability = Applicability::MaybeIncorrect;
90+
match vec_init_kind {
91+
VecInitKind::WithConstCapacity(len) => {
92+
span_lint_and_sugg(
93+
cx,
94+
READ_ZERO_BYTE_VEC,
95+
next_stmt_span,
96+
"reading zero byte data to `Vec`",
97+
"try",
98+
format!("{}.resize({}, 0); {}",
99+
ident.as_str(),
100+
len,
101+
snippet(cx, next_stmt_span, "..")
102+
),
103+
applicability,
104+
);
105+
}
106+
VecInitKind::WithExprCapacity(hir_id) => {
107+
let e = cx.tcx.hir().expect_expr(hir_id);
108+
span_lint_and_sugg(
109+
cx,
110+
READ_ZERO_BYTE_VEC,
111+
next_stmt_span,
112+
"reading zero byte data to `Vec`",
113+
"try",
114+
format!("{}.resize({}, 0); {}",
115+
ident.as_str(),
116+
snippet(cx, e.span, ".."),
117+
snippet(cx, next_stmt_span, "..")
118+
),
119+
applicability,
120+
);
121+
}
122+
_ => {
123+
span_lint(
124+
cx,
125+
READ_ZERO_BYTE_VEC,
126+
next_stmt_span,
127+
"reading zero byte data to `Vec`",
128+
);
129+
130+
}
131+
}
132+
}
133+
}
134+
}
135+
}
136+
}

tests/ui/read_zero_byte_vec.rs

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,87 @@
11
#![warn(clippy::read_zero_byte_vec)]
2+
#![allow(clippy::unused_io_amount)]
3+
use std::fs::File;
4+
use std::io;
5+
use std::io::prelude::*;
26

3-
fn main() {
4-
// test code goes here
7+
extern crate futures;
8+
use futures::io::{AsyncRead, AsyncReadExt};
9+
use tokio::io::{AsyncRead as TokioAsyncRead, AsyncReadExt as _, AsyncWrite as TokioAsyncWrite, AsyncWriteExt as _};
10+
11+
fn test() -> io::Result<()> {
12+
let cap = 1000;
13+
let mut f = File::open("foo.txt").unwrap();
14+
15+
// should lint
16+
let mut data = Vec::with_capacity(20);
17+
f.read_exact(&mut data).unwrap();
18+
19+
// should lint
20+
let mut data2 = Vec::with_capacity(cap);
21+
f.read_exact(&mut data2)?;
22+
23+
// should lint
24+
let mut data3 = Vec::new();
25+
f.read_exact(&mut data3)?;
26+
27+
// should lint
28+
let mut data4 = vec![];
29+
let _ = f.read(&mut data4)?;
30+
31+
// should lint
32+
let _ = {
33+
let mut data5 = Vec::new();
34+
f.read(&mut data5)
35+
};
36+
37+
// should lint
38+
let _ = {
39+
let mut data6: Vec<u8> = Default::default();
40+
f.read(&mut data6)
41+
};
42+
43+
// should not lint
44+
let mut buf = [0u8; 100];
45+
f.read(&mut buf)?;
46+
47+
// should not lint
48+
let mut empty = vec![];
49+
let mut data7 = vec![];
50+
f.read(&mut empty);
51+
52+
// should not lint
53+
f.read(&mut data7);
54+
55+
// should not lint
56+
let mut data8 = Vec::new();
57+
data8.resize(100, 0);
58+
f.read_exact(&mut data8)?;
59+
60+
// should not lint
61+
let mut data9 = vec![1, 2, 3];
62+
f.read_exact(&mut data9)?;
63+
64+
Ok(())
65+
}
66+
67+
async fn test_futures<R: AsyncRead + Unpin>(r: &mut R) {
68+
// should lint
69+
let mut data = Vec::new();
70+
r.read(&mut data).await.unwrap();
71+
72+
// should lint
73+
let mut data2 = Vec::new();
74+
r.read_exact(&mut data2).await.unwrap();
575
}
76+
77+
async fn test_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) {
78+
// should lint
79+
let mut data = Vec::new();
80+
r.read(&mut data).await.unwrap();
81+
82+
// should lint
83+
let mut data2 = Vec::new();
84+
r.read_exact(&mut data2).await.unwrap();
85+
}
86+
87+
fn main() {}

tests/ui/read_zero_byte_vec.stderr

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: reading zero byte data to `Vec`
2+
--> $DIR/read_zero_byte_vec.rs:17:5
3+
|
4+
LL | f.read_exact(&mut data).unwrap();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();`
6+
|
7+
= note: `-D clippy::read-zero-byte-vec` implied by `-D warnings`
8+
9+
error: reading zero byte data to `Vec`
10+
--> $DIR/read_zero_byte_vec.rs:21:5
11+
|
12+
LL | f.read_exact(&mut data2)?;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;`
14+
15+
error: reading zero byte data to `Vec`
16+
--> $DIR/read_zero_byte_vec.rs:25:5
17+
|
18+
LL | f.read_exact(&mut data3)?;
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: reading zero byte data to `Vec`
22+
--> $DIR/read_zero_byte_vec.rs:29:5
23+
|
24+
LL | let _ = f.read(&mut data4)?;
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: reading zero byte data to `Vec`
28+
--> $DIR/read_zero_byte_vec.rs:34:9
29+
|
30+
LL | f.read(&mut data5)
31+
| ^^^^^^^^^^^^^^^^^^
32+
33+
error: reading zero byte data to `Vec`
34+
--> $DIR/read_zero_byte_vec.rs:40:9
35+
|
36+
LL | f.read(&mut data6)
37+
| ^^^^^^^^^^^^^^^^^^
38+
39+
error: reading zero byte data to `Vec`
40+
--> $DIR/read_zero_byte_vec.rs:70:5
41+
|
42+
LL | r.read(&mut data).await.unwrap();
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
error: reading zero byte data to `Vec`
46+
--> $DIR/read_zero_byte_vec.rs:74:5
47+
|
48+
LL | r.read_exact(&mut data2).await.unwrap();
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
error: reading zero byte data to `Vec`
52+
--> $DIR/read_zero_byte_vec.rs:80:5
53+
|
54+
LL | r.read(&mut data).await.unwrap();
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
57+
error: reading zero byte data to `Vec`
58+
--> $DIR/read_zero_byte_vec.rs:84:5
59+
|
60+
LL | r.read_exact(&mut data2).await.unwrap();
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
63+
error: aborting due to 10 previous errors
64+

0 commit comments

Comments
 (0)