1 use clippy_utils::{ 2 diagnostics::{span_lint, span_lint_and_sugg}, 3 higher::{get_vec_init_kind, VecInitKind}, 4 source::snippet, 5 visitors::for_each_expr, 6 }; 7 use core::ops::ControlFlow; 8 use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; 9 use rustc_errors::Applicability; 10 use rustc_hir as hir; 11 use rustc_lint::{LateContext, LateLintPass}; 12 use rustc_session::{declare_lint_pass, declare_tool_lint}; 13 14 declare_clippy_lint! { 15 /// ### What it does 16 /// This lint catches reads into a zero-length `Vec`. 17 /// Especially in the case of a call to `with_capacity`, this lint warns that read 18 /// gets the number of bytes from the `Vec`'s length, not its capacity. 19 /// 20 /// ### Why is this bad? 21 /// Reading zero bytes is almost certainly not the intended behavior. 22 /// 23 /// ### Known problems 24 /// In theory, a very unusual read implementation could assign some semantic meaning 25 /// to zero-byte reads. But it seems exceptionally unlikely that code intending to do 26 /// a zero-byte read would allocate a `Vec` for it. 27 /// 28 /// ### Example 29 /// ```rust 30 /// use std::io; 31 /// fn foo<F: io::Read>(mut f: F) { 32 /// let mut data = Vec::with_capacity(100); 33 /// f.read(&mut data).unwrap(); 34 /// } 35 /// ``` 36 /// Use instead: 37 /// ```rust 38 /// use std::io; 39 /// fn foo<F: io::Read>(mut f: F) { 40 /// let mut data = Vec::with_capacity(100); 41 /// data.resize(100, 0); 42 /// f.read(&mut data).unwrap(); 43 /// } 44 /// ``` 45 #[clippy::version = "1.63.0"] 46 pub READ_ZERO_BYTE_VEC, 47 correctness, 48 "checks for reads into a zero-length `Vec`" 49 } 50 declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]); 51 52 impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>)53 fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) { 54 for (idx, stmt) in block.stmts.iter().enumerate() { 55 if !stmt.span.from_expansion() 56 // matches `let v = Vec::new();` 57 && let StmtKind::Local(local) = stmt.kind 58 && let Local { pat, init: Some(init), .. } = local 59 && let PatKind::Binding(_, _, ident, _) = pat.kind 60 && let Some(vec_init_kind) = get_vec_init_kind(cx, init) 61 { 62 let visitor = |expr: &Expr<'_>| { 63 if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind 64 && let PathSegment { ident: read_or_read_exact, .. } = *path 65 && matches!(read_or_read_exact.as_str(), "read" | "read_exact") 66 && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind 67 && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind 68 && let [inner_seg] = inner_path.segments 69 && ident.name == inner_seg.ident.name 70 { 71 ControlFlow::Break(()) 72 } else { 73 ControlFlow::Continue(()) 74 } 75 }; 76 77 let (read_found, next_stmt_span) = 78 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 { 85 return 86 }; 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({len}, 0); {}", 99 ident.as_str(), 100 snippet(cx, next_stmt_span, "..") 101 ), 102 applicability, 103 ); 104 } 105 VecInitKind::WithExprCapacity(hir_id) => { 106 let e = cx.tcx.hir().expect_expr(hir_id); 107 span_lint_and_sugg( 108 cx, 109 READ_ZERO_BYTE_VEC, 110 next_stmt_span, 111 "reading zero byte data to `Vec`", 112 "try", 113 format!("{}.resize({}, 0); {}", 114 ident.as_str(), 115 snippet(cx, e.span, ".."), 116 snippet(cx, next_stmt_span, "..") 117 ), 118 applicability, 119 ); 120 } 121 _ => { 122 span_lint( 123 cx, 124 READ_ZERO_BYTE_VEC, 125 next_stmt_span, 126 "reading zero byte data to `Vec`", 127 ); 128 129 } 130 } 131 } 132 } 133 } 134 } 135 } 136