]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/explicit_write.rs
Rollup merge of #92399 - Veeupup:fix_vec_typo, r=Dylan-DPC
[rust.git] / src / tools / clippy / clippy_lints / src / explicit_write.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::macros::FormatArgsExpn;
3 use clippy_utils::source::snippet_with_applicability;
4 use clippy_utils::{is_expn_of, match_function_call, paths};
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::{Expr, ExprKind};
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::sym;
11
12 declare_clippy_lint! {
13     /// ### What it does
14     /// Checks for usage of `write!()` / `writeln()!` which can be
15     /// replaced with `(e)print!()` / `(e)println!()`
16     ///
17     /// ### Why is this bad?
18     /// Using `(e)println! is clearer and more concise
19     ///
20     /// ### Example
21     /// ```rust
22     /// # use std::io::Write;
23     /// # let bar = "furchtbar";
24     /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
25     /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
26     /// ```
27     #[clippy::version = "pre 1.29.0"]
28     pub EXPLICIT_WRITE,
29     complexity,
30     "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
31 }
32
33 declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
34
35 impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
36     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
37         if_chain! {
38             // match call to unwrap
39             if let ExprKind::MethodCall(unwrap_fun, [write_call], _) = expr.kind;
40             if unwrap_fun.ident.name == sym::unwrap;
41             // match call to write_fmt
42             if let ExprKind::MethodCall(write_fun, [write_recv, write_arg], _) = write_call.kind;
43             if write_fun.ident.name == sym!(write_fmt);
44             // match calls to std::io::stdout() / std::io::stderr ()
45             if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
46                 Some("stdout")
47             } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
48                 Some("stderr")
49             } else {
50                 None
51             };
52             if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
53             then {
54                 let calling_macro =
55                     // ordering is important here, since `writeln!` uses `write!` internally
56                     if is_expn_of(write_call.span, "writeln").is_some() {
57                         Some("writeln")
58                     } else if is_expn_of(write_call.span, "write").is_some() {
59                         Some("write")
60                     } else {
61                         None
62                     };
63                 let prefix = if dest_name == "stderr" {
64                     "e"
65                 } else {
66                     ""
67                 };
68
69                 // We need to remove the last trailing newline from the string because the
70                 // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
71                 // used.
72                 let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
73                     (
74                         format!("{}!({}(), ...)", macro_name, dest_name),
75                         macro_name.replace("write", "print"),
76                     )
77                 } else {
78                     (
79                         format!("{}().write_fmt(...)", dest_name),
80                         "print".into(),
81                     )
82                 };
83                 let mut applicability = Applicability::MachineApplicable;
84                 let inputs_snippet = snippet_with_applicability(
85                     cx,
86                     format_args.inputs_span(),
87                     "..",
88                     &mut applicability,
89                 );
90                 span_lint_and_sugg(
91                     cx,
92                     EXPLICIT_WRITE,
93                     expr.span,
94                     &format!("use of `{}.unwrap()`", used),
95                     "try this",
96                     format!("{}{}!({})", prefix, sugg_mac, inputs_snippet),
97                     applicability,
98                 )
99             }
100         }
101     }
102 }