@@ -75,6 +75,24 @@ declare_clippy_lint! {
7575 "printing on stdout"
7676}
7777
78+ declare_clippy_lint ! {
79+ /// **What it does:** Checks for printing on *stderr*. The purpose of this lint
80+ /// is to catch debugging remnants.
81+ ///
82+ /// **Why is this bad?** People often print on *stderr* while debugging an
83+ /// application and might forget to remove those prints afterward.
84+ ///
85+ /// **Known problems:** Only catches `eprint!` and `eprintln!` calls.
86+ ///
87+ /// **Example:**
88+ /// ```rust
89+ /// eprintln!("Hello world!");
90+ /// ```
91+ pub PRINT_STDERR ,
92+ restriction,
93+ "printing on stderr"
94+ }
95+
7896declare_clippy_lint ! {
7997 /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
8098 /// lint is to catch debugging remnants.
@@ -201,6 +219,7 @@ impl_lint_pass!(Write => [
201219 PRINT_WITH_NEWLINE ,
202220 PRINTLN_EMPTY_STRING ,
203221 PRINT_STDOUT ,
222+ PRINT_STDERR ,
204223 USE_DEBUG ,
205224 PRINT_LITERAL ,
206225 WRITE_WITH_NEWLINE ,
@@ -243,47 +262,22 @@ impl EarlyLintPass for Write {
243262 . map_or ( false , |crate_name| crate_name == "build_script_build" )
244263 }
245264
246- if mac. path == sym ! ( println) {
247- if !is_build_script ( cx) {
248- span_lint ( cx, PRINT_STDOUT , mac. span ( ) , "use of `println!`" ) ;
249- }
250- if let ( Some ( fmt_str) , _) = self . check_tts ( cx, mac. args . inner_tokens ( ) , false ) {
251- if fmt_str. symbol == Symbol :: intern ( "" ) {
252- span_lint_and_sugg (
253- cx,
254- PRINTLN_EMPTY_STRING ,
255- mac. span ( ) ,
256- "using `println!(\" \" )`" ,
257- "replace it with" ,
258- "println!()" . to_string ( ) ,
259- Applicability :: MachineApplicable ,
260- ) ;
261- }
262- }
263- } else if mac. path == sym ! ( print) {
265+ if mac. path == sym ! ( print) {
264266 if !is_build_script ( cx) {
265267 span_lint ( cx, PRINT_STDOUT , mac. span ( ) , "use of `print!`" ) ;
266268 }
267- if let ( Some ( fmt_str) , _) = self . check_tts ( cx, mac. args . inner_tokens ( ) , false ) {
268- if check_newlines ( & fmt_str) {
269- span_lint_and_then (
270- cx,
271- PRINT_WITH_NEWLINE ,
272- mac. span ( ) ,
273- "using `print!()` with a format string that ends in a single newline" ,
274- |err| {
275- err. multipart_suggestion (
276- "use `println!` instead" ,
277- vec ! [
278- ( mac. path. span, String :: from( "println" ) ) ,
279- ( newline_span( & fmt_str) , String :: new( ) ) ,
280- ] ,
281- Applicability :: MachineApplicable ,
282- ) ;
283- } ,
284- ) ;
285- }
269+ self . lint_print_with_newline ( cx, mac) ;
270+ } else if mac. path == sym ! ( println) {
271+ if !is_build_script ( cx) {
272+ span_lint ( cx, PRINT_STDOUT , mac. span ( ) , "use of `println!`" ) ;
286273 }
274+ self . lint_println_empty_string ( cx, mac) ;
275+ } else if mac. path == sym ! ( eprint) {
276+ span_lint ( cx, PRINT_STDERR , mac. span ( ) , "use of `eprint!`" ) ;
277+ self . lint_print_with_newline ( cx, mac) ;
278+ } else if mac. path == sym ! ( eprintln) {
279+ span_lint ( cx, PRINT_STDERR , mac. span ( ) , "use of `eprintln!`" ) ;
280+ self . lint_println_empty_string ( cx, mac) ;
287281 } else if mac. path == sym ! ( write) {
288282 if let ( Some ( fmt_str) , _) = self . check_tts ( cx, mac. args . inner_tokens ( ) , true ) {
289283 if check_newlines ( & fmt_str) {
@@ -487,6 +481,45 @@ impl Write {
487481 }
488482 }
489483 }
484+
485+ fn lint_println_empty_string ( & self , cx : & EarlyContext < ' _ > , mac : & MacCall ) {
486+ if let ( Some ( fmt_str) , _) = self . check_tts ( cx, mac. args . inner_tokens ( ) , false ) {
487+ if fmt_str. symbol == Symbol :: intern ( "" ) {
488+ let name = mac. path . segments [ 0 ] . ident . name ;
489+ span_lint_and_sugg (
490+ cx,
491+ PRINTLN_EMPTY_STRING ,
492+ mac. span ( ) ,
493+ & format ! ( "using `{}!(\" \" )`" , name) ,
494+ "replace it with" ,
495+ format ! ( "{}!()" , name) ,
496+ Applicability :: MachineApplicable ,
497+ ) ;
498+ }
499+ }
500+ }
501+
502+ fn lint_print_with_newline ( & self , cx : & EarlyContext < ' _ > , mac : & MacCall ) {
503+ if let ( Some ( fmt_str) , _) = self . check_tts ( cx, mac. args . inner_tokens ( ) , false ) {
504+ if check_newlines ( & fmt_str) {
505+ let name = mac. path . segments [ 0 ] . ident . name ;
506+ let suggested = format ! ( "{}ln" , name) ;
507+ span_lint_and_then (
508+ cx,
509+ PRINT_WITH_NEWLINE ,
510+ mac. span ( ) ,
511+ & format ! ( "using `{}!()` with a format string that ends in a single newline" , name) ,
512+ |err| {
513+ err. multipart_suggestion (
514+ & format ! ( "use `{}!` instead" , suggested) ,
515+ vec ! [ ( mac. path. span, suggested) , ( newline_span( & fmt_str) , String :: new( ) ) ] ,
516+ Applicability :: MachineApplicable ,
517+ ) ;
518+ } ,
519+ ) ;
520+ }
521+ }
522+ }
490523}
491524
492525/// Checks if the format string contains a single newline that terminates it.
0 commit comments