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 dbfa02d20a1..5d8543e578d 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -35,4 +35,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 8d2ad1b4d97..ead6b5d08a0 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 22 | 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:41:5 + | +41 | write!(&mut v, "//n"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors