]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
Rollup merge of #104901 - krtab:filetype_compare, r=the8472
[rust.git] / src / tools / clippy / clippy_lints / src / read_zero_byte_vec.rs
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 {
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 }