]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | use rustc::lint::*; |
2 | use rustc::hir; | |
abe05a73 | 3 | use utils::{is_try, match_qpath, match_trait_method, paths, span_lint}; |
ea8adc8c XL |
4 | |
5 | /// **What it does:** Checks for unused written/read amount. | |
6 | /// | |
7 | /// **Why is this bad?** `io::Write::write` and `io::Read::read` are not | |
8 | /// guaranteed to | |
9 | /// process the entire buffer. They return how many bytes were processed, which | |
10 | /// might be smaller | |
11 | /// than a given buffer's length. If you don't need to deal with | |
12 | /// partial-write/read, use | |
13 | /// `write_all`/`read_exact` instead. | |
14 | /// | |
15 | /// **Known problems:** Detects only common patterns. | |
16 | /// | |
17 | /// **Example:** | |
18 | /// ```rust,ignore | |
19 | /// use std::io; | |
20 | /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { | |
21 | /// // must be `w.write_all(b"foo")?;` | |
22 | /// w.write(b"foo")?; | |
23 | /// Ok(()) | |
24 | /// } | |
25 | /// ``` | |
26 | declare_lint! { | |
27 | pub UNUSED_IO_AMOUNT, | |
28 | Deny, | |
29 | "unused written/read amount" | |
30 | } | |
31 | ||
32 | pub struct UnusedIoAmount; | |
33 | ||
34 | impl LintPass for UnusedIoAmount { | |
35 | fn get_lints(&self) -> LintArray { | |
36 | lint_array!(UNUSED_IO_AMOUNT) | |
37 | } | |
38 | } | |
39 | ||
40 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount { | |
41 | fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { | |
42 | let expr = match s.node { | |
abe05a73 | 43 | hir::StmtSemi(ref expr, _) | hir::StmtExpr(ref expr, _) => &**expr, |
ea8adc8c XL |
44 | _ => return, |
45 | }; | |
46 | ||
47 | match expr.node { | |
48 | hir::ExprMatch(ref res, _, _) if is_try(expr).is_some() => { | |
49 | if let hir::ExprCall(ref func, ref args) = res.node { | |
50 | if let hir::ExprPath(ref path) = func.node { | |
51 | if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 { | |
52 | check_method_call(cx, &args[0], expr); | |
53 | } | |
54 | } | |
55 | } else { | |
56 | check_method_call(cx, res, expr); | |
57 | } | |
58 | }, | |
59 | ||
abe05a73 XL |
60 | hir::ExprMethodCall(ref path, _, ref args) => match &*path.name.as_str() { |
61 | "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { | |
62 | check_method_call(cx, &args[0], expr); | |
63 | }, | |
64 | _ => (), | |
ea8adc8c XL |
65 | }, |
66 | ||
67 | _ => (), | |
68 | } | |
69 | } | |
70 | } | |
71 | ||
72 | fn check_method_call(cx: &LateContext, call: &hir::Expr, expr: &hir::Expr) { | |
73 | if let hir::ExprMethodCall(ref path, _, _) = call.node { | |
74 | let symbol = &*path.name.as_str(); | |
75 | if match_trait_method(cx, call, &paths::IO_READ) && symbol == "read" { | |
76 | span_lint( | |
77 | cx, | |
78 | UNUSED_IO_AMOUNT, | |
79 | expr.span, | |
80 | "handle read amount returned or use `Read::read_exact` instead", | |
81 | ); | |
82 | } else if match_trait_method(cx, call, &paths::IO_WRITE) && symbol == "write" { | |
83 | span_lint( | |
84 | cx, | |
85 | UNUSED_IO_AMOUNT, | |
86 | expr.span, | |
87 | "handle written amount returned or use `Write::write_all` instead", | |
88 | ); | |
89 | } | |
90 | } | |
91 | } |