]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint; |
2 | use clippy_utils::{is_try, match_trait_method, paths}; | |
f20569fa XL |
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! { | |
94222f64 XL |
8 | /// ### What it does |
9 | /// Checks for unused written/read amount. | |
f20569fa | 10 | /// |
94222f64 XL |
11 | /// ### Why is this bad? |
12 | /// `io::Write::write(_vectored)` and | |
f20569fa XL |
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 | /// | |
94222f64 XL |
20 | /// ### Known problems |
21 | /// Detects only common patterns. | |
f20569fa | 22 | /// |
94222f64 | 23 | /// ### Example |
f20569fa XL |
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 { | |
cdc7bbd5 | 42 | hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, |
f20569fa XL |
43 | _ => return, |
44 | }; | |
45 | ||
46 | match expr.kind { | |
cdc7bbd5 XL |
47 | hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { |
48 | if let hir::ExprKind::Call(func, args) = res.kind { | |
f20569fa XL |
49 | if matches!( |
50 | func.kind, | |
17df50a5 | 51 | hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _)) |
f20569fa | 52 | ) { |
cdc7bbd5 | 53 | check_map_error(cx, &args[0], expr); |
f20569fa XL |
54 | } |
55 | } else { | |
cdc7bbd5 | 56 | check_map_error(cx, res, expr); |
f20569fa XL |
57 | } |
58 | }, | |
cdc7bbd5 | 59 | hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { |
f20569fa | 60 | "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { |
cdc7bbd5 | 61 | check_map_error(cx, &args[0], expr); |
f20569fa XL |
62 | }, |
63 | _ => (), | |
64 | }, | |
f20569fa XL |
65 | _ => (), |
66 | } | |
67 | } | |
68 | } | |
69 | ||
cdc7bbd5 XL |
70 | fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { |
71 | let mut call = call; | |
17df50a5 | 72 | while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { |
cdc7bbd5 XL |
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 | ||
f20569fa | 82 | fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { |
cdc7bbd5 | 83 | if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { |
f20569fa XL |
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 | } |