2 diagnostics::{span_lint, span_lint_and_sugg},
3 higher::{get_vec_init_kind, VecInitKind},
5 visitors::expr_visitor_no_bodies,
7 use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
8 use rustc_errors::Applicability;
10 use rustc_lint::{LateContext, LateLintPass};
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 declare_clippy_lint! {
15 /// This lint catches reads into a zero-length `Vec`.
16 /// Especially in the case of a call to `with_capacity`, this lint warns that read
17 /// gets the number of bytes from the `Vec`'s length, not its capacity.
19 /// ### Why is this bad?
20 /// Reading zero bytes is almost certainly not the intended behavior.
22 /// ### Known problems
23 /// In theory, a very unusual read implementation could assign some semantic meaning
24 /// to zero-byte reads. But it seems exceptionally unlikely that code intending to do
25 /// a zero-byte read would allocate a `Vec` for it.
30 /// fn foo<F: io::Read>(mut f: F) {
31 /// let mut data = Vec::with_capacity(100);
32 /// f.read(&mut data).unwrap();
38 /// fn foo<F: io::Read>(mut f: F) {
39 /// let mut data = Vec::with_capacity(100);
40 /// data.resize(100, 0);
41 /// f.read(&mut data).unwrap();
44 #[clippy::version = "1.63.0"]
45 pub READ_ZERO_BYTE_VEC,
47 "checks for reads into a zero-length `Vec`"
49 declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
51 impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
52 fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
53 for (idx, stmt) in block.stmts.iter().enumerate() {
54 if !stmt.span.from_expansion()
55 // matches `let v = Vec::new();`
56 && let StmtKind::Local(local) = stmt.kind
57 && let Local { pat, init: Some(init), .. } = local
58 && let PatKind::Binding(_, _, ident, _) = pat.kind
59 && let Some(vec_init_kind) = get_vec_init_kind(cx, init)
61 // finds use of `_.read(&mut v)`
62 let mut read_found = false;
63 let mut visitor = expr_visitor_no_bodies(|expr| {
64 if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind
65 && let PathSegment { ident: read_or_read_exact, .. } = *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
78 if idx == block.stmts.len() - 1 {
79 // case { .. stmt; expr }
80 if let Some(e) = block.expr {
81 visitor.visit_expr(e);
82 next_stmt_span = e.span;
87 // case { .. stmt; stmt; .. }
88 let next_stmt = &block.stmts[idx + 1];
89 visitor.visit_stmt(next_stmt);
90 next_stmt_span = next_stmt.span;
94 if read_found && !next_stmt_span.from_expansion() {
95 let applicability = Applicability::MaybeIncorrect;
97 VecInitKind::WithConstCapacity(len) => {
102 "reading zero byte data to `Vec`",
104 format!("{}.resize({}, 0); {}",
107 snippet(cx, next_stmt_span, "..")
112 VecInitKind::WithExprCapacity(hir_id) => {
113 let e = cx.tcx.hir().expect_expr(hir_id);
118 "reading zero byte data to `Vec`",
120 format!("{}.resize({}, 0); {}",
122 snippet(cx, e.span, ".."),
123 snippet(cx, next_stmt_span, "..")
133 "reading zero byte data to `Vec`",