about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPhilipp Hansch <dev@phansch.net>2018-11-23 08:18:23 +0100
committerPhilipp Hansch <dev@phansch.net>2018-12-12 07:31:01 +0100
commitc4c9d9fc62eb244c0f219cfbf23f19b9240c2827 (patch)
tree3ad76d4d688d4297a1d3a855ab811ee744ab5632
parente2608fc27231d12a360915c9cb60de5219a22505 (diff)
downloadrust-c4c9d9fc62eb244c0f219cfbf23f19b9240c2827.tar.gz
rust-c4c9d9fc62eb244c0f219cfbf23f19b9240c2827.zip
Add suggestion for explicit_write lint
-rw-r--r--clippy_lints/src/explicit_write.rs40
-rw-r--r--clippy_lints/src/lib.rs1
-rw-r--r--tests/ui/explicit_write.rs4
-rw-r--r--tests/ui/explicit_write.stderr62
4 files changed, 68 insertions, 39 deletions
diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs
index 94bd0ab209c..65fa6aeec0b 100644
--- a/clippy_lints/src/explicit_write.rs
+++ b/clippy_lints/src/explicit_write.rs
@@ -10,8 +10,8 @@
 use crate::rustc::hir::*;
 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
 use crate::rustc::{declare_tool_lint, lint_array};
-use crate::utils::opt_def_id;
-use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint};
+use crate::syntax::ast::LitKind;
+use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg};
 use if_chain::if_chain;
 
 /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
@@ -51,6 +51,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
             if unwrap_args.len() > 0;
             if let ExprKind::MethodCall(ref write_fun, _, ref write_args) =
                 unwrap_args[0].node;
+            // Obtain the string that should be printed
+            if let ExprKind::Call(_, ref output_args) = write_args[1].node;
+            if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node;
+            if let ExprKind::Array(ref string_exprs) = output_string_expr.node;
+            if let ExprKind::Lit(ref lit) = string_exprs[0].node;
+            if let LitKind::Str(ref write_output, _) = lit.node;
             if write_fun.ident.name == "write_fmt";
             // match calls to std::io::stdout() / std::io::stderr ()
             if write_args.len() > 0;
@@ -81,29 +87,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
                 } else {
                     ""
                 };
+
+                // We need to remove the last trailing newline from the string because the
+                // underlying `fmt::write` function doesn't know wether `println!` or `print!` was
+                // used.
+                let mut write_output: String = write_output.to_string();
+                if write_output.ends_with('\n') {
+                    write_output.truncate(write_output.len() - 1)
+                }
                 if let Some(macro_name) = calling_macro {
-                    span_lint(
+                    span_lint_and_sugg(
                         cx,
                         EXPLICIT_WRITE,
                         expr.span,
                         &format!(
-                            "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
+                            "use of `{}!({}(), ...).unwrap()`",
                             macro_name,
-                            dest_name,
-                            prefix,
-                            macro_name.replace("write", "print")
-                        )
+                            dest_name
+                        ),
+                        "try this",
+                        format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default())
                     );
                 } else {
-                    span_lint(
+                    span_lint_and_sugg(
                         cx,
                         EXPLICIT_WRITE,
                         expr.span,
-                        &format!(
-                            "use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead",
-                            dest_name,
-                            prefix,
-                        )
+                        &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
+                        "try this",
+                        format!("{}print!(\"{}\")", prefix, write_output.escape_default())
                     );
                 }
             }
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ee41c632077..a862d774174 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -14,6 +14,7 @@
 #![feature(slice_patterns)]
 #![feature(stmt_expr_attributes)]
 #![feature(range_contains)]
+#![feature(str_escape)]
 #![allow(clippy::missing_docs_in_private_items)]
 #![recursion_limit = "256"]
 #![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs
index 10a4bca9f49..01a63b3a95f 100644
--- a/tests/ui/explicit_write.rs
+++ b/tests/ui/explicit_write.rs
@@ -27,6 +27,10 @@ fn main() {
         writeln!(std::io::stderr(), "test").unwrap();
         std::io::stdout().write_fmt(format_args!("test")).unwrap();
         std::io::stderr().write_fmt(format_args!("test")).unwrap();
+
+        // including newlines
+        writeln!(std::io::stdout(), "test\ntest").unwrap();
+        writeln!(std::io::stderr(), "test\ntest").unwrap();
     }
     // these should not warn, different destination
     {
diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr
index 171bf312a9b..6d318d09e58 100644
--- a/tests/ui/explicit_write.stderr
+++ b/tests/ui/explicit_write.stderr
@@ -1,40 +1,52 @@
-error: use of `write!(stdout(), ...).unwrap()`. Consider using `print!` instead
-  --> $DIR/explicit_write.rs:24:9
+error: use of `write!(stdout(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:28:9
    |
-24 |         write!(std::io::stdout(), "test").unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+28 |         write!(std::io::stdout(), "test").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
    |
    = note: `-D clippy::explicit-write` implied by `-D warnings`
 
-error: use of `write!(stderr(), ...).unwrap()`. Consider using `eprint!` instead
-  --> $DIR/explicit_write.rs:25:9
+error: use of `write!(stderr(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:29:9
    |
-25 |         write!(std::io::stderr(), "test").unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+29 |         write!(std::io::stderr(), "test").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
 
-error: use of `writeln!(stdout(), ...).unwrap()`. Consider using `println!` instead
-  --> $DIR/explicit_write.rs:26:9
+error: use of `writeln!(stdout(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:30:9
    |
-26 |         writeln!(std::io::stdout(), "test").unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+30 |         writeln!(std::io::stdout(), "test").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
 
-error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
-  --> $DIR/explicit_write.rs:27:9
+error: use of `writeln!(stderr(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:31:9
    |
-27 |         writeln!(std::io::stderr(), "test").unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+31 |         writeln!(std::io::stderr(), "test").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
 
-error: use of `stdout().write_fmt(...).unwrap()`. Consider using `print!` instead
-  --> $DIR/explicit_write.rs:28:9
+error: use of `stdout().write_fmt(...).unwrap()`
+  --> $DIR/explicit_write.rs:32:9
    |
-28 |         std::io::stdout().write_fmt(format_args!("test")).unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+32 |         std::io::stdout().write_fmt(format_args!("test")).unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
 
-error: use of `stderr().write_fmt(...).unwrap()`. Consider using `eprint!` instead
-  --> $DIR/explicit_write.rs:29:9
+error: use of `stderr().write_fmt(...).unwrap()`
+  --> $DIR/explicit_write.rs:33:9
+   |
+33 |         std::io::stderr().write_fmt(format_args!("test")).unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
+
+error: use of `writeln!(stdout(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:36:9
+   |
+36 |         writeln!(std::io::stdout(), "test/ntest").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+  --> $DIR/explicit_write.rs:37:9
    |
-29 |         std::io::stderr().write_fmt(format_args!("test")).unwrap();
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+37 |         writeln!(std::io::stderr(), "test/ntest").unwrap();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors