]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/unused_io_amount.rs
Rollup merge of #88860 - nbdd0121:panic, r=m-ou-se
[rust.git] / 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     pub UNUSED_IO_AMOUNT,
33     correctness,
34     "unused written/read amount"
35 }
36
37 declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
38
39 impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
40     fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
41         let expr = match s.kind {
42             hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
43             _ => return,
44         };
45
46         match expr.kind {
47             hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
48                 if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
49                     if matches!(
50                         func.kind,
51                         hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _))
52                     ) {
53                         check_map_error(cx, arg_0, expr);
54                     }
55                 } else {
56                     check_map_error(cx, res, expr);
57                 }
58             },
59             hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() {
60                 "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
61                     check_map_error(cx, arg_0, expr);
62                 },
63                 _ => (),
64             },
65             _ => (),
66         }
67     }
68 }
69
70 fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
71     let mut call = call;
72     while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind {
73         if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
74             call = &args[0];
75         } else {
76             break;
77         }
78     }
79     check_method_call(cx, call, expr);
80 }
81
82 fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
83     if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
84         let symbol = &*path.ident.as_str();
85         let read_trait = match_trait_method(cx, call, &paths::IO_READ);
86         let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
87
88         match (read_trait, write_trait, symbol) {
89             (true, _, "read") => span_lint(
90                 cx,
91                 UNUSED_IO_AMOUNT,
92                 expr.span,
93                 "read amount is not handled. Use `Read::read_exact` instead",
94             ),
95             (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
96             (_, true, "write") => span_lint(
97                 cx,
98                 UNUSED_IO_AMOUNT,
99                 expr.span,
100                 "written amount is not handled. Use `Write::write_all` instead",
101             ),
102             (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
103             _ => (),
104         }
105     }
106 }