about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-12-08 22:22:49 +0000
committerbors <bors@rust-lang.org>2020-12-08 22:22:49 +0000
commitb02b0c737abd9c04ebd43fc2c94444d932d044fe (patch)
tree938fa9d21f2e19c15cd417fc0899e3e22d03215e
parentf9fccbedbff3eee143949abd45df6f3284d8a028 (diff)
parent3187cad8ec0e9c32272341028526d6f1e208a704 (diff)
downloadrust-b02b0c737abd9c04ebd43fc2c94444d932d044fe.tar.gz
rust-b02b0c737abd9c04ebd43fc2c94444d932d044fe.zip
Auto merge of #6367 - justjosias:6348-print-stderr, r=ebroto
Add lint print_stderr

Resolves #6348
Almost identical to print_stdout, this lint applies to the `eprintln!` and `eprint!` macros rather than `println!` and `print!`.

changelog: Add new lint [`print_stderr`]. [`println_empty_string`] and [`print_with_newline`] now apply to `eprint!()` and `eprintln!()` respectively.
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/write.rs107
-rw-r--r--tests/ui/eprint_with_newline.rs49
-rw-r--r--tests/ui/eprint_with_newline.stderr121
-rw-r--r--tests/ui/print_stderr.rs8
-rw-r--r--tests/ui/print_stderr.stderr16
-rw-r--r--tests/ui/println_empty_string.fixed7
-rw-r--r--tests/ui/println_empty_string.rs7
-rw-r--r--tests/ui/println_empty_string.stderr14
10 files changed, 294 insertions, 38 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index adb4a5c8261..82f2ad7ec4e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2006,6 +2006,7 @@ Released 2018-09-13
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ac5a45ccfd2..a92ae9ed8d9 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -936,6 +936,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &wildcard_imports::WILDCARD_IMPORTS,
         &write::PRINTLN_EMPTY_STRING,
         &write::PRINT_LITERAL,
+        &write::PRINT_STDERR,
         &write::PRINT_STDOUT,
         &write::PRINT_WITH_NEWLINE,
         &write::USE_DEBUG,
@@ -1250,6 +1251,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::RC_BUFFER),
         LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
+        LintId::of(&write::PRINT_STDERR),
         LintId::of(&write::PRINT_STDOUT),
         LintId::of(&write::USE_DEBUG),
     ]);
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index ff414f748ef..337f7a229b9 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -76,6 +76,24 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint! {
+    /// **What it does:** Checks for printing on *stderr*. The purpose of this lint
+    /// is to catch debugging remnants.
+    ///
+    /// **Why is this bad?** People often print on *stderr* while debugging an
+    /// application and might forget to remove those prints afterward.
+    ///
+    /// **Known problems:** Only catches `eprint!` and `eprintln!` calls.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// eprintln!("Hello world!");
+    /// ```
+    pub PRINT_STDERR,
+    restriction,
+    "printing on stderr"
+}
+
+declare_clippy_lint! {
     /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
     /// lint is to catch debugging remnants.
     ///
@@ -201,6 +219,7 @@ impl_lint_pass!(Write => [
     PRINT_WITH_NEWLINE,
     PRINTLN_EMPTY_STRING,
     PRINT_STDOUT,
+    PRINT_STDERR,
     USE_DEBUG,
     PRINT_LITERAL,
     WRITE_WITH_NEWLINE,
@@ -243,47 +262,22 @@ impl EarlyLintPass for Write {
                 .map_or(false, |crate_name| crate_name == "build_script_build")
         }
 
-        if mac.path == sym!(println) {
-            if !is_build_script(cx) {
-                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
-            }
-            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-                if fmt_str.symbol == Symbol::intern("") {
-                    span_lint_and_sugg(
-                        cx,
-                        PRINTLN_EMPTY_STRING,
-                        mac.span(),
-                        "using `println!(\"\")`",
-                        "replace it with",
-                        "println!()".to_string(),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-        } else if mac.path == sym!(print) {
+        if mac.path == sym!(print) {
             if !is_build_script(cx) {
                 span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
             }
-            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-                if check_newlines(&fmt_str) {
-                    span_lint_and_then(
-                        cx,
-                        PRINT_WITH_NEWLINE,
-                        mac.span(),
-                        "using `print!()` with a format string that ends in a single newline",
-                        |err| {
-                            err.multipart_suggestion(
-                                "use `println!` instead",
-                                vec![
-                                    (mac.path.span, String::from("println")),
-                                    (newline_span(&fmt_str), String::new()),
-                                ],
-                                Applicability::MachineApplicable,
-                            );
-                        },
-                    );
-                }
+            self.lint_print_with_newline(cx, mac);
+        } else if mac.path == sym!(println) {
+            if !is_build_script(cx) {
+                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
             }
+            self.lint_println_empty_string(cx, mac);
+        } else if mac.path == sym!(eprint) {
+            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
+            self.lint_print_with_newline(cx, mac);
+        } else if mac.path == sym!(eprintln) {
+            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
+            self.lint_println_empty_string(cx, mac);
         } else if mac.path == sym!(write) {
             if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) {
                 if check_newlines(&fmt_str) {
@@ -487,6 +481,45 @@ impl Write {
             }
         }
     }
+
+    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
+        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
+            if fmt_str.symbol == Symbol::intern("") {
+                let name = mac.path.segments[0].ident.name;
+                span_lint_and_sugg(
+                    cx,
+                    PRINTLN_EMPTY_STRING,
+                    mac.span(),
+                    &format!("using `{}!(\"\")`", name),
+                    "replace it with",
+                    format!("{}!()", name),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+
+    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
+        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
+            if check_newlines(&fmt_str) {
+                let name = mac.path.segments[0].ident.name;
+                let suggested = format!("{}ln", name);
+                span_lint_and_then(
+                    cx,
+                    PRINT_WITH_NEWLINE,
+                    mac.span(),
+                    &format!("using `{}!()` with a format string that ends in a single newline", name),
+                    |err| {
+                        err.multipart_suggestion(
+                            &format!("use `{}!` instead", suggested),
+                            vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
+                            Applicability::MachineApplicable,
+                        );
+                    },
+                );
+            }
+        }
+    }
 }
 
 /// Checks if the format string contains a single newline that terminates it.
diff --git a/tests/ui/eprint_with_newline.rs b/tests/ui/eprint_with_newline.rs
new file mode 100644
index 00000000000..8df32649ad9
--- /dev/null
+++ b/tests/ui/eprint_with_newline.rs
@@ -0,0 +1,49 @@
+#![allow(clippy::print_literal)]
+#![warn(clippy::print_with_newline)]
+
+fn main() {
+    eprint!("Hello\n");
+    eprint!("Hello {}\n", "world");
+    eprint!("Hello {} {}\n", "world", "#2");
+    eprint!("{}\n", 1265);
+    eprint!("\n");
+
+    // these are all fine
+    eprint!("");
+    eprint!("Hello");
+    eprintln!("Hello");
+    eprintln!("Hello\n");
+    eprintln!("Hello {}\n", "world");
+    eprint!("Issue\n{}", 1265);
+    eprint!("{}", 1265);
+    eprint!("\n{}", 1275);
+    eprint!("\n\n");
+    eprint!("like eof\n\n");
+    eprint!("Hello {} {}\n\n", "world", "#2");
+    eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+    eprintln!("\nbla\n\n"); // #3126
+
+    // Escaping
+    eprint!("\\n"); // #3514
+    eprint!("\\\n"); // should fail
+    eprint!("\\\\n");
+
+    // Raw strings
+    eprint!(r"\n"); // #3778
+
+    // Literal newlines should also fail
+    eprint!(
+        "
+"
+    );
+    eprint!(
+        r"
+"
+    );
+
+    // Don't warn on CRLF (#4208)
+    eprint!("\r\n");
+    eprint!("foo\r\n");
+    eprint!("\\r\n"); //~ ERROR
+    eprint!("foo\rbar\n") // ~ ERROR
+}
diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr
new file mode 100644
index 00000000000..31811d1d92a
--- /dev/null
+++ b/tests/ui/eprint_with_newline.stderr
@@ -0,0 +1,121 @@
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:5:5
+   |
+LL |     eprint!("Hello/n");
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::print-with-newline` implied by `-D warnings`
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello");
+   |     ^^^^^^^^       --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:6:5
+   |
+LL |     eprint!("Hello {}/n", "world");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello {}", "world");
+   |     ^^^^^^^^          --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:7:5
+   |
+LL |     eprint!("Hello {} {}/n", "world", "#2");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello {} {}", "world", "#2");
+   |     ^^^^^^^^             --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:8:5
+   |
+LL |     eprint!("{}/n", 1265);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("{}", 1265);
+   |     ^^^^^^^^    --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:9:5
+   |
+LL |     eprint!("/n");
+   |     ^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!();
+   |     ^^^^^^^^ --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:28:5
+   |
+LL |     eprint!("//n"); // should fail
+   |     ^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("/"); // should fail
+   |     ^^^^^^^^    --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:35:5
+   |
+LL | /     eprint!(
+LL | |         "
+LL | | "
+LL | |     );
+   | |_____^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!(
+LL |         ""
+   |
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:39:5
+   |
+LL | /     eprint!(
+LL | |         r"
+LL | | "
+LL | |     );
+   | |_____^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!(
+LL |         r""
+   |
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:47:5
+   |
+LL |     eprint!("/r/n"); //~ ERROR
+   |     ^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("/r"); //~ ERROR
+   |     ^^^^^^^^     --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:48:5
+   |
+LL |     eprint!("foo/rbar/n") // ~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("foo/rbar") // ~ ERROR
+   |     ^^^^^^^^          --
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs
new file mode 100644
index 00000000000..fa07e74a7be
--- /dev/null
+++ b/tests/ui/print_stderr.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::print_stderr)]
+
+fn main() {
+    eprintln!("Hello");
+    println!("This should not do anything");
+    eprint!("World");
+    print!("Nor should this");
+}
diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr
new file mode 100644
index 00000000000..5af735af657
--- /dev/null
+++ b/tests/ui/print_stderr.stderr
@@ -0,0 +1,16 @@
+error: use of `eprintln!`
+  --> $DIR/print_stderr.rs:4:5
+   |
+LL |     eprintln!("Hello");
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::print-stderr` implied by `-D warnings`
+
+error: use of `eprint!`
+  --> $DIR/print_stderr.rs:6:5
+   |
+LL |     eprint!("World");
+   |     ^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/println_empty_string.fixed b/tests/ui/println_empty_string.fixed
index 2b889b62ea9..9760680927a 100644
--- a/tests/ui/println_empty_string.fixed
+++ b/tests/ui/println_empty_string.fixed
@@ -8,4 +8,11 @@ fn main() {
     match "a" {
         _ => println!(),
     }
+
+    eprintln!();
+    eprintln!();
+
+    match "a" {
+        _ => eprintln!(),
+    }
 }
diff --git a/tests/ui/println_empty_string.rs b/tests/ui/println_empty_string.rs
index 890f5f68476..80fdb3e6e21 100644
--- a/tests/ui/println_empty_string.rs
+++ b/tests/ui/println_empty_string.rs
@@ -8,4 +8,11 @@ fn main() {
     match "a" {
         _ => println!(""),
     }
+
+    eprintln!();
+    eprintln!("");
+
+    match "a" {
+        _ => eprintln!(""),
+    }
 }
diff --git a/tests/ui/println_empty_string.stderr b/tests/ui/println_empty_string.stderr
index 23112b88168..17fe4ea7479 100644
--- a/tests/ui/println_empty_string.stderr
+++ b/tests/ui/println_empty_string.stderr
@@ -12,5 +12,17 @@ error: using `println!("")`
 LL |         _ => println!(""),
    |              ^^^^^^^^^^^^ help: replace it with: `println!()`
 
-error: aborting due to 2 previous errors
+error: using `eprintln!("")`
+  --> $DIR/println_empty_string.rs:13:5
+   |
+LL |     eprintln!("");
+   |     ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: using `eprintln!("")`
+  --> $DIR/println_empty_string.rs:16:14
+   |
+LL |         _ => eprintln!(""),
+   |              ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: aborting due to 4 previous errors