Skip to content

Commit f01e45f

Browse files
authored
Merge pull request #2131 from devonhollowood/suggest-print
Suggest print
2 parents 343e438 + 2842038 commit f01e45f

File tree

4 files changed

+188
-0
lines changed

4 files changed

+188
-0
lines changed

clippy_lints/src/explicit_write.rs

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

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub mod serde_api;
146146
pub mod shadow;
147147
pub mod should_assert_eq;
148148
pub mod strings;
149+
pub mod explicit_write;
149150
pub mod swap;
150151
pub mod temporary_assignment;
151152
pub mod transmute;
@@ -327,6 +328,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
327328
reg.register_late_lint_pass(box unused_io_amount::UnusedIoAmount);
328329
reg.register_late_lint_pass(box large_enum_variant::LargeEnumVariant::new(conf.enum_variant_size_threshold));
329330
reg.register_late_lint_pass(box should_assert_eq::ShouldAssertEq);
331+
reg.register_late_lint_pass(box explicit_write::Pass);
330332
reg.register_late_lint_pass(box needless_pass_by_value::NeedlessPassByValue);
331333
reg.register_early_lint_pass(box literal_digit_grouping::LiteralDigitGrouping);
332334
reg.register_late_lint_pass(box use_self::UseSelf);
@@ -542,6 +544,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
542544
serde_api::SERDE_API_MISUSE,
543545
should_assert_eq::SHOULD_ASSERT_EQ,
544546
strings::STRING_LIT_AS_BYTES,
547+
explicit_write::EXPLICIT_WRITE,
545548
swap::ALMOST_SWAPPED,
546549
swap::MANUAL_SWAP,
547550
temporary_assignment::TEMPORARY_ASSIGNMENT,

tests/ui/explicit_write.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![warn(explicit_write)]
2+
3+
4+
fn stdout() -> String {
5+
String::new()
6+
}
7+
8+
fn stderr() -> String {
9+
String::new()
10+
}
11+
12+
fn main() {
13+
// these should warn
14+
{
15+
use std::io::Write;
16+
write!(std::io::stdout(), "test").unwrap();
17+
write!(std::io::stderr(), "test").unwrap();
18+
writeln!(std::io::stdout(), "test").unwrap();
19+
writeln!(std::io::stderr(), "test").unwrap();
20+
std::io::stdout().write_fmt(format_args!("test")).unwrap();
21+
std::io::stderr().write_fmt(format_args!("test")).unwrap();
22+
}
23+
// these should not warn, different destination
24+
{
25+
use std::fmt::Write;
26+
let mut s = String::new();
27+
write!(s, "test").unwrap();
28+
write!(s, "test").unwrap();
29+
writeln!(s, "test").unwrap();
30+
writeln!(s, "test").unwrap();
31+
s.write_fmt(format_args!("test")).unwrap();
32+
s.write_fmt(format_args!("test")).unwrap();
33+
write!(stdout(), "test").unwrap();
34+
write!(stderr(), "test").unwrap();
35+
writeln!(stdout(), "test").unwrap();
36+
writeln!(stderr(), "test").unwrap();
37+
stdout().write_fmt(format_args!("test")).unwrap();
38+
stderr().write_fmt(format_args!("test")).unwrap();
39+
}
40+
// these should not warn, no unwrap
41+
{
42+
use std::io::Write;
43+
std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
44+
std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
45+
}
46+
}

tests/ui/explicit_write.stderr

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: use of `write!(stdout(), ...).unwrap()`. Consider using `print!` instead
2+
--> $DIR/explicit_write.rs:16:9
3+
|
4+
16 | write!(std::io::stdout(), "test").unwrap();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D explicit-write` implied by `-D warnings`
8+
9+
error: use of `write!(stderr(), ...).unwrap()`. Consider using `eprint!` instead
10+
--> $DIR/explicit_write.rs:17:9
11+
|
12+
17 | write!(std::io::stderr(), "test").unwrap();
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: use of `writeln!(stdout(), ...).unwrap()`. Consider using `println!` instead
16+
--> $DIR/explicit_write.rs:18:9
17+
|
18+
18 | writeln!(std::io::stdout(), "test").unwrap();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
22+
--> $DIR/explicit_write.rs:19:9
23+
|
24+
19 | writeln!(std::io::stderr(), "test").unwrap();
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: use of `stdout().write_fmt(...).unwrap()`. Consider using `print!` instead
28+
--> $DIR/explicit_write.rs:20:9
29+
|
30+
20 | std::io::stdout().write_fmt(format_args!("test")).unwrap();
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: use of `stderr().write_fmt(...).unwrap()`. Consider using `eprint!` instead
34+
--> $DIR/explicit_write.rs:21:9
35+
|
36+
21 | std::io::stderr().write_fmt(format_args!("test")).unwrap();
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+

0 commit comments

Comments
 (0)