]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unused_io_amount.rs
Rollup merge of #91562 - dtolnay:asyncspace, r=Mark-Simulacrum
[rust.git] / src / tools / clippy / clippy_lints / src / unused_io_amount.rs
1 use clippy_utils::diagnostics::span_lint;
2 use clippy_utils::{is_try, match_trait_method, paths};
3 use rustc_hir as hir;
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6
7 declare_clippy_lint! {
8     /// ### What it does
9     /// Checks for unused written/read amount.
10     ///
11     /// ### Why is this bad?
12     /// `io::Write::write(_vectored)` and
13     /// `io::Read::read(_vectored)` are not guaranteed to
14     /// process the entire buffer. They return how many bytes were processed, which
15     /// might be smaller
16     /// than a given buffer's length. If you don't need to deal with
17     /// partial-write/read, use
18     /// `write_all`/`read_exact` instead.
19     ///
20     /// ### Known problems
21     /// Detects only common patterns.
22     ///
23     /// ### Example
24     /// ```rust,ignore
25     /// use std::io;
26     /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
27     ///     // must be `w.write_all(b"foo")?;`
28     ///     w.write(b"foo")?;
29     ///     Ok(())
30     /// }
31     /// ```
32     #[clippy::version = "pre 1.29.0"]
33     pub UNUSED_IO_AMOUNT,
34     correctness,
35     "unused written/read amount"
36 }
37
38 declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
39
40 impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
41     fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
42         let expr = match s.kind {
43             hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
44             _ => return,
45         };
46
47         match expr.kind {
48             hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
49                 if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
50                     if matches!(
51                         func.kind,
52                         hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _))
53                     ) {
54                         check_map_error(cx, arg_0, expr);
55                     }
56                 } else {
57                     check_map_error(cx, res, expr);
58                 }
59             },
60             hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() {
61                 "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
62                     check_map_error(cx, arg_0, expr);
63                 },
64                 _ => (),
65             },
66             _ => (),
67         }
68     }
69 }
70
71 fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
72     let mut call = call;
73     while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind {
74         if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
75             call = &args[0];
76         } else {
77             break;
78         }
79     }
80     check_method_call(cx, call, expr);
81 }
82
83 fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
84     if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
85         let symbol = &*path.ident.as_str();
86         let read_trait = match_trait_method(cx, call, &paths::IO_READ);
87         let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
88
89         match (read_trait, write_trait, symbol) {
90             (true, _, "read") => span_lint(
91                 cx,
92                 UNUSED_IO_AMOUNT,
93                 expr.span,
94                 "read amount is not handled. Use `Read::read_exact` instead",
95             ),
96             (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
97             (_, true, "write") => span_lint(
98                 cx,
99                 UNUSED_IO_AMOUNT,
100                 expr.span,
101                 "written amount is not handled. Use `Write::write_all` instead",
102             ),
103             (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
104             _ => (),
105         }
106     }
107 }