Skip to content

Commit eb0c13c

Browse files
committed
Add suggestion for explicit_write lint
1 parent ff7da32 commit eb0c13c

File tree

4 files changed

+56
-26
lines changed

4 files changed

+56
-26
lines changed

clippy_lints/src/explicit_write.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use crate::rustc::hir::*;
1212
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
1313
use crate::rustc::{declare_tool_lint, lint_array};
1414
use if_chain::if_chain;
15-
use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint};
15+
use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint_and_sugg};
1616
use crate::utils::opt_def_id;
17+
use crate::syntax::ast::LitKind;
1718

1819
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
1920
/// replaced with `(e)print!()` / `(e)println!()`
@@ -53,6 +54,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
5354
if unwrap_args.len() > 0;
5455
if let ExprKind::MethodCall(ref write_fun, _, ref write_args) =
5556
unwrap_args[0].node;
57+
// Obtain the string that should be printed
58+
if let ExprKind::Call(_, ref output_args) = write_args[1].node;
59+
if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node;
60+
if let ExprKind::Array(ref string_exprs) = output_string_expr.node;
61+
if let ExprKind::Lit(ref lit) = string_exprs[0].node;
62+
if let LitKind::Str(ref write_output, _) = lit.node;
5663
if write_fun.ident.name == "write_fmt";
5764
// match calls to std::io::stdout() / std::io::stderr ()
5865
if write_args.len() > 0;
@@ -83,29 +90,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
8390
} else {
8491
""
8592
};
93+
94+
// We need to remove the last trailing newline from the string because the
95+
// underlying `fmt::write` function doesn't know wether `println!` or `print!` was
96+
// used.
97+
let mut write_output: String = write_output.to_string();
98+
if write_output.ends_with('\n') {
99+
write_output.truncate(write_output.len() - 1)
100+
}
86101
if let Some(macro_name) = calling_macro {
87-
span_lint(
102+
span_lint_and_sugg(
88103
cx,
89104
EXPLICIT_WRITE,
90105
expr.span,
91106
&format!(
92-
"use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
107+
"use of `{}!({}(), ...).unwrap()`",
93108
macro_name,
94-
dest_name,
95-
prefix,
96-
macro_name.replace("write", "print")
97-
)
109+
dest_name
110+
),
111+
"try this",
112+
format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default())
98113
);
99114
} else {
100-
span_lint(
115+
span_lint_and_sugg(
101116
cx,
102117
EXPLICIT_WRITE,
103118
expr.span,
104-
&format!(
105-
"use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead",
106-
dest_name,
107-
prefix,
108-
)
119+
&format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
120+
"try this",
121+
format!("{}print!(\"{}\")", prefix, write_output.escape_default())
109122
);
110123
}
111124
}

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![feature(slice_patterns)]
1616
#![feature(stmt_expr_attributes)]
1717
#![feature(range_contains)]
18+
#![feature(str_escape)]
1819
#![allow(clippy::missing_docs_in_private_items)]
1920
#![recursion_limit = "256"]
2021
#![feature(macro_at_most_once_rep)]

tests/ui/explicit_write.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ fn main() {
3131
writeln!(std::io::stderr(), "test").unwrap();
3232
std::io::stdout().write_fmt(format_args!("test")).unwrap();
3333
std::io::stderr().write_fmt(format_args!("test")).unwrap();
34+
35+
// including newlines
36+
writeln!(std::io::stdout(), "test\ntest").unwrap();
37+
writeln!(std::io::stderr(), "test\ntest").unwrap();
3438
}
3539
// these should not warn, different destination
3640
{

tests/ui/explicit_write.stderr

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,52 @@
1-
error: use of `write!(stdout(), ...).unwrap()`. Consider using `print!` instead
1+
error: use of `write!(stdout(), ...).unwrap()`
22
--> $DIR/explicit_write.rs:28:9
33
|
44
28 | write!(std::io::stdout(), "test").unwrap();
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
66
|
77
= note: `-D clippy::explicit-write` implied by `-D warnings`
88

9-
error: use of `write!(stderr(), ...).unwrap()`. Consider using `eprint!` instead
9+
error: use of `write!(stderr(), ...).unwrap()`
1010
--> $DIR/explicit_write.rs:29:9
1111
|
1212
29 | write!(std::io::stderr(), "test").unwrap();
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
1414

15-
error: use of `writeln!(stdout(), ...).unwrap()`. Consider using `println!` instead
15+
error: use of `writeln!(stdout(), ...).unwrap()`
1616
--> $DIR/explicit_write.rs:30:9
1717
|
1818
30 | writeln!(std::io::stdout(), "test").unwrap();
19-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
2020

21-
error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
21+
error: use of `writeln!(stderr(), ...).unwrap()`
2222
--> $DIR/explicit_write.rs:31:9
2323
|
2424
31 | writeln!(std::io::stderr(), "test").unwrap();
25-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
2626

27-
error: use of `stdout().write_fmt(...).unwrap()`. Consider using `print!` instead
27+
error: use of `stdout().write_fmt(...).unwrap()`
2828
--> $DIR/explicit_write.rs:32:9
2929
|
3030
32 | std::io::stdout().write_fmt(format_args!("test")).unwrap();
31-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
3232

33-
error: use of `stderr().write_fmt(...).unwrap()`. Consider using `eprint!` instead
33+
error: use of `stderr().write_fmt(...).unwrap()`
3434
--> $DIR/explicit_write.rs:33:9
3535
|
3636
33 | std::io::stderr().write_fmt(format_args!("test")).unwrap();
37-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
3838

39-
error: aborting due to 6 previous errors
39+
error: use of `writeln!(stdout(), ...).unwrap()`
40+
--> $DIR/explicit_write.rs:36:9
41+
|
42+
36 | writeln!(std::io::stdout(), "test/ntest").unwrap();
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
44+
45+
error: use of `writeln!(stderr(), ...).unwrap()`
46+
--> $DIR/explicit_write.rs:37:9
47+
|
48+
37 | writeln!(std::io::stderr(), "test/ntest").unwrap();
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
50+
51+
error: aborting due to 8 previous errors
4052

0 commit comments

Comments
 (0)