From b81141cfb91cbf39e87e32a27530537f18e85405 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 22 Nov 2020 19:02:57 +0100 Subject: [PATCH 1/5] 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.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/write.rs | 23 +++++++++++++++++++++++ tests/ui/print_stderr.rs | 6 ++++++ tests/ui/print_stderr.stderr | 16 ++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 tests/ui/print_stderr.rs create mode 100644 tests/ui/print_stderr.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e02aaf4e1..1f2c4f310de 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 2b99ed570b1..0e630a352fe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -934,6 +934,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, @@ -1247,6 +1248,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..2248bc1e32e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -75,6 +75,24 @@ "printing on stdout" } +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 @@ pub struct Write { PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, + PRINT_STDERR, USE_DEBUG, PRINT_LITERAL, WRITE_WITH_NEWLINE, @@ -260,6 +279,10 @@ fn is_build_script(cx: &EarlyContext<'_>) -> bool { ); } } + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs new file mode 100644 index 00000000000..e53f46b1c2f --- /dev/null +++ b/tests/ui/print_stderr.rs @@ -0,0 +1,6 @@ +#![warn(clippy::print_stderr)] + +fn main() { + eprintln!("Hello"); + eprint!("World"); +} diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr new file mode 100644 index 00000000000..7252fce72c6 --- /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:5:5 + | +LL | eprint!("World"); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 51cee15be0f67403eedfe88c0c4ca07f306bef42 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 29 Nov 2020 09:55:47 +0100 Subject: [PATCH 2/5] Add negative tests --- tests/ui/print_stderr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs index e53f46b1c2f..fa07e74a7be 100644 --- a/tests/ui/print_stderr.rs +++ b/tests/ui/print_stderr.rs @@ -2,5 +2,7 @@ fn main() { eprintln!("Hello"); + println!("This should not do anything"); eprint!("World"); + print!("Nor should this"); } From b04bfbd09b63d978f1bb7f4c9222cb2d37607309 Mon Sep 17 00:00:00 2001 From: Josias Date: Tue, 1 Dec 2020 11:29:49 +0100 Subject: [PATCH 3/5] Fix print_stderr.stderr test --- tests/ui/print_stderr.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr index 7252fce72c6..5af735af657 100644 --- a/tests/ui/print_stderr.stderr +++ b/tests/ui/print_stderr.stderr @@ -7,7 +7,7 @@ LL | eprintln!("Hello"); = note: `-D clippy::print-stderr` implied by `-D warnings` error: use of `eprint!` - --> $DIR/print_stderr.rs:5:5 + --> $DIR/print_stderr.rs:6:5 | LL | eprint!("World"); | ^^^^^^^^^^^^^^^^ From 7063c36c912990bd67327a41445706a451fe5b48 Mon Sep 17 00:00:00 2001 From: Josias Date: Fri, 4 Dec 2020 15:39:09 +0100 Subject: [PATCH 4/5] Add eprint! to print_with_newline lint --- clippy_lints/src/write.rs | 20 +++++ tests/ui/eprint_with_newline.rs | 49 +++++++++++ tests/ui/eprint_with_newline.stderr | 121 ++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 tests/ui/eprint_with_newline.rs create mode 100644 tests/ui/eprint_with_newline.stderr diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 2248bc1e32e..0fd56f78572 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -283,6 +283,26 @@ fn is_build_script(cx: &EarlyContext<'_>) -> bool { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + 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 `eprint!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `eprintln!` instead", + vec![ + (mac.path.span, String::from("eprintln")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ); + } + } } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); 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 + From 3187cad8ec0e9c32272341028526d6f1e208a704 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 23:17:12 +0100 Subject: [PATCH 5/5] Factor out some code in write.rs Get rid of the too-many-lines error. --- clippy_lints/src/write.rs | 112 ++++++++++++--------------- tests/ui/println_empty_string.fixed | 7 ++ tests/ui/println_empty_string.rs | 7 ++ tests/ui/println_empty_string.stderr | 14 +++- 4 files changed, 78 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0fd56f78572..337f7a229b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -262,71 +262,22 @@ fn is_build_script(cx: &EarlyContext<'_>) -> bool { .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!(eprintln) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); - } else if mac.path == sym!(eprint) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); - 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 `eprint!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `eprintln!` instead", - vec![ - (mac.path.span, String::from("eprintln")), - (newline_span(&fmt_str), String::new()), - ], - 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) { @@ -530,6 +481,45 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) } } } + + 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/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