From c6505aa1604e0b5378305b95e8d89f99d0da0031 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 11 Dec 2018 08:28:25 +0200 Subject: [PATCH] Fix write_with_newline escaping false positive Fixes #3514 --- clippy_lints/src/write.rs | 36 +++++++++++++++++++++++------- tests/ui/write_with_newline.rs | 5 +++++ tests/ui/write_with_newline.stderr | 8 ++++++- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 440ab7433cc..416ba4ca18d 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -206,10 +206,7 @@ impl EarlyLintPass for Pass { } else if mac.node.path == "print" { span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`"); if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 { - if fmtstr.ends_with("\\n") && - // don't warn about strings with several `\n`s (#3126) - fmtstr.matches("\\n").count() == 1 - { + if check_newlines(&fmtstr) { span_lint( cx, PRINT_WITH_NEWLINE, @@ -221,10 +218,7 @@ impl EarlyLintPass for Pass { } } else if mac.node.path == "write" { if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 { - if fmtstr.ends_with("\\n") && - // don't warn about strings with several `\n`s (#3126) - fmtstr.matches("\\n").count() == 1 - { + if check_newlines(&fmtstr) { span_lint( cx, WRITE_WITH_NEWLINE, @@ -375,3 +369,29 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) - } } } + +// Checks if `s` constains a single newline that terminates it +fn check_newlines(s: &str) -> bool { + if s.len() < 2 { + return false; + } + + let bytes = s.as_bytes(); + if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' { + return false; + } + + let mut escaping = false; + for (index, &byte) in bytes.iter().enumerate() { + if escaping { + if byte == b'n' { + return index == bytes.len() - 1; + } + escaping = false; + } else if byte == b'\\' { + escaping = true; + } + } + + false +} diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index e9fcff0b3dd..e8bea5f7675 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -38,4 +38,9 @@ fn main() { write!(&mut v, "Hello {} {}\n\n", "world", "#2"); writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 writeln!(&mut v, "\nbla\n\n"); // #3126 + + // Escaping + write!(&mut v, "\\n"); // #3514 + write!(&mut v, "\\\n"); + write!(&mut v, "\\\\n"); } diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index dd7f223c517..30ca5e30b85 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -24,5 +24,11 @@ error: using `write!()` with a format string that ends in a single newline, cons 25 | write!(&mut v, "{}/n", 1265); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: using `write!()` with a format string that ends in a single newline, consider using `writeln!()` instead + --> $DIR/write_with_newline.rs:44:5 + | +44 | write!(&mut v, "//n"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors