]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
Rollup merge of #101624 - notriddle:notriddle/search, r=GuillaumeGomez
[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::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;
10 use rustc_lint::{LateContext, LateLintPass};
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
12
13 declare_clippy_lint! {
14     /// ### What it does
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.
18     ///
19     /// ### Why is this bad?
20     /// Reading zero bytes is almost certainly not the intended behavior.
21     ///
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.
26     ///
27     /// ### Example
28     /// ```rust
29     /// use std::io;
30     /// fn foo<F: io::Read>(mut f: F) {
31     ///     let mut data = Vec::with_capacity(100);
32     ///     f.read(&mut data).unwrap();
33     /// }
34     /// ```
35     /// Use instead:
36     /// ```rust
37     /// use std::io;
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();
42     /// }
43     /// ```
44     #[clippy::version = "1.63.0"]
45     pub READ_ZERO_BYTE_VEC,
46     correctness,
47     "checks for reads into a zero-length `Vec`"
48 }
49 declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
50
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)
60             {
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
71                     {
72                         read_found = true;
73                     }
74                     !read_found
75                 });
76
77                 let next_stmt_span;
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;
83                     } else {
84                         return;
85                     }
86                 } else {
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;
91                 }
92                 drop(visitor);
93
94                 if read_found && !next_stmt_span.from_expansion() {
95                     let applicability = Applicability::MaybeIncorrect;
96                     match vec_init_kind {
97                         VecInitKind::WithConstCapacity(len) => {
98                             span_lint_and_sugg(
99                                 cx,
100                                 READ_ZERO_BYTE_VEC,
101                                 next_stmt_span,
102                                 "reading zero byte data to `Vec`",
103                                 "try",
104                                 format!("{}.resize({}, 0); {}",
105                                     ident.as_str(),
106                                     len,
107                                     snippet(cx, next_stmt_span, "..")
108                                 ),
109                                 applicability,
110                             );
111                         }
112                         VecInitKind::WithExprCapacity(hir_id) => {
113                             let e = cx.tcx.hir().expect_expr(hir_id);
114                             span_lint_and_sugg(
115                                 cx,
116                                 READ_ZERO_BYTE_VEC,
117                                 next_stmt_span,
118                                 "reading zero byte data to `Vec`",
119                                 "try",
120                                 format!("{}.resize({}, 0); {}",
121                                     ident.as_str(),
122                                     snippet(cx, e.span, ".."),
123                                     snippet(cx, next_stmt_span, "..")
124                                 ),
125                                 applicability,
126                             );
127                         }
128                         _ => {
129                             span_lint(
130                                 cx,
131                                 READ_ZERO_BYTE_VEC,
132                                 next_stmt_span,
133                                 "reading zero byte data to `Vec`",
134                             );
135
136                         }
137                     }
138                 }
139             }
140         }
141     }
142 }