]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/explicit_write.rs
Merge pull request #3189 from eddyb/rextern
[rust.git] / clippy_lints / src / explicit_write.rs
1 use crate::rustc::hir::*;
2 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
3 use crate::rustc::{declare_tool_lint, lint_array};
4 use if_chain::if_chain;
5 use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint};
6 use crate::utils::opt_def_id;
7
8 /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
9 /// replaced with `(e)print!()` / `(e)println!()`
10 ///
11 /// **Why is this bad?** Using `(e)println! is clearer and more concise
12 ///
13 /// **Known problems:** None.
14 ///
15 /// **Example:**
16 /// ```rust
17 /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
18 /// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
19 /// ```
20 declare_clippy_lint! {
21     pub EXPLICIT_WRITE,
22     complexity,
23     "using the `write!()` family of functions instead of the `print!()` family \
24      of functions, when using the latter would work"
25 }
26
27 #[derive(Copy, Clone, Debug)]
28 pub struct Pass;
29
30 impl LintPass for Pass {
31     fn get_lints(&self) -> LintArray {
32         lint_array!(EXPLICIT_WRITE)
33     }
34 }
35
36 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
37     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
38         if_chain! {
39             // match call to unwrap
40             if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.node;
41             if unwrap_fun.ident.name == "unwrap";
42             // match call to write_fmt
43             if unwrap_args.len() > 0;
44             if let ExprKind::MethodCall(ref write_fun, _, ref write_args) =
45                 unwrap_args[0].node;
46             if write_fun.ident.name == "write_fmt";
47             // match calls to std::io::stdout() / std::io::stderr ()
48             if write_args.len() > 0;
49             if let ExprKind::Call(ref dest_fun, _) = write_args[0].node;
50             if let ExprKind::Path(ref qpath) = dest_fun.node;
51             if let Some(dest_fun_id) =
52                 opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id));
53             if let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
54                 Some("stdout")
55             } else if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stderr"]) {
56                 Some("stderr")
57             } else {
58                 None
59             };
60             then {
61                 let write_span = unwrap_args[0].span;
62                 let calling_macro =
63                     // ordering is important here, since `writeln!` uses `write!` internally
64                     if is_expn_of(write_span, "writeln").is_some() {
65                         Some("writeln")
66                     } else if is_expn_of(write_span, "write").is_some() {
67                         Some("write")
68                     } else {
69                         None
70                     };
71                 let prefix = if dest_name == "stderr" {
72                     "e"
73                 } else {
74                     ""
75                 };
76                 if let Some(macro_name) = calling_macro {
77                     span_lint(
78                         cx,
79                         EXPLICIT_WRITE,
80                         expr.span,
81                         &format!(
82                             "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
83                             macro_name,
84                             dest_name,
85                             prefix,
86                             macro_name.replace("write", "print")
87                         )
88                     );
89                 } else {
90                     span_lint(
91                         cx,
92                         EXPLICIT_WRITE,
93                         expr.span,
94                         &format!(
95                             "use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead",
96                             dest_name,
97                             prefix,
98                         )
99                     );
100                 }
101             }
102         }
103     }
104 }