Auto merge of #4769 - euclio:crlf, r=flip1995

don't warn on CRLF in `with_newline` lints

changelog: don't warn on CRLF in `print_with_newline` and `write_with_newline`
fixes #4208.

This PR also transitions the unescaping logic to use the compiler's lexer.
This commit is contained in:
bors 2019-11-12 13:56:14 +00:00
commit 180f87065f
6 changed files with 85 additions and 31 deletions

View File

@ -29,6 +29,8 @@ extern crate rustc_errors;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_index; extern crate rustc_index;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_lexer;
#[allow(unused_extern_crates)]
extern crate rustc_mir; extern crate rustc_mir;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_parse; extern crate rustc_parse;

View File

@ -1,9 +1,12 @@
use std::borrow::Cow;
use std::ops::Range;
use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass}; use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint}; use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lexer::unescape::{self, EscapeError};
use rustc_parse::parser; use rustc_parse::parser;
use std::borrow::Cow;
use syntax::ast::*; use syntax::ast::*;
use syntax::token; use syntax::token;
use syntax::tokenstream::TokenStream; use syntax::tokenstream::TokenStream;
@ -202,7 +205,7 @@ impl EarlyLintPass for Write {
} else if mac.path == sym!(print) { } else if mac.path == sym!(print) {
span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`"); span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) { if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) {
if check_newlines(&fmt_str) { if check_newlines(&fmt_str.contents, fmt_str.style) {
span_lint_and_then( span_lint_and_then(
cx, cx,
PRINT_WITH_NEWLINE, PRINT_WITH_NEWLINE,
@ -223,7 +226,7 @@ impl EarlyLintPass for Write {
} }
} else if mac.path == sym!(write) { } else if mac.path == sym!(write) {
if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, true) { if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, true) {
if check_newlines(&fmt_str) { if check_newlines(&fmt_str.contents, fmt_str.style) {
span_lint_and_then( span_lint_and_then(
cx, cx,
WRITE_WITH_NEWLINE, WRITE_WITH_NEWLINE,
@ -442,38 +445,31 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
} }
} }
/// Checks if the format string constains a single newline that terminates it. /// Checks if the format string contains a single newline that terminates it.
/// ///
/// Literal and escaped newlines are both checked (only literal for raw strings). /// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmt_str: &FmtStr) -> bool { fn check_newlines(contents: &str, style: StrStyle) -> bool {
let s = &fmt_str.contents; let mut has_internal_newline = false;
let mut last_was_cr = false;
let mut should_lint = false;
if s.ends_with('\n') { let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
return true; let c = c.unwrap();
} else if let StrStyle::Raw(_) = fmt_str.style {
return false;
}
if s.len() < 2 { if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
return false; should_lint = true;
} } else {
last_was_cr = c == '\r';
let bytes = s.as_bytes(); if c == '\n' {
if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' { has_internal_newline = true;
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;
} }
};
match style {
StrStyle::Cooked => unescape::unescape_str(contents, &mut cb),
StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb),
} }
false should_lint
} }

View File

@ -42,4 +42,10 @@ fn main() {
r" r"
" "
); );
// Don't warn on CRLF (#4208)
print!("\r\n");
print!("foo\r\n");
print!("\\r\n"); //~ ERROR
print!("foo\rbar\n") // ~ ERROR
} }

View File

@ -84,5 +84,27 @@ LL | println!(
LL | r"" LL | r""
| |
error: aborting due to 7 previous errors error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:49:5
|
LL | print!("/r/n"); //~ ERROR
| ^^^^^^^^^^^^^^^
|
help: use `println!` instead
|
LL | println!("/r"); //~ ERROR
| ^^^^^^^ --
error: using `print!()` with a format string that ends in a single newline
--> $DIR/print_with_newline.rs:50:5
|
LL | print!("foo/rbar/n") // ~ ERROR
| ^^^^^^^^^^^^^^^^^^^^
|
help: use `println!` instead
|
LL | println!("foo/rbar") // ~ ERROR
| ^^^^^^^ --
error: aborting due to 9 previous errors

View File

@ -49,4 +49,10 @@ fn main() {
r" r"
" "
); );
// Don't warn on CRLF (#4208)
write!(&mut v, "\r\n");
write!(&mut v, "foo\r\n");
write!(&mut v, "\\r\n"); //~ ERROR
write!(&mut v, "foo\rbar\n");
} }

View File

@ -88,5 +88,27 @@ LL | &mut v,
LL | r"" LL | r""
| |
error: aborting due to 7 previous errors error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:56:5
|
LL | write!(&mut v, "/r/n"); //~ ERROR
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
LL | writeln!(&mut v, "/r"); //~ ERROR
| ^^^^^^^ --
error: using `write!()` with a format string that ends in a single newline
--> $DIR/write_with_newline.rs:57:5
|
LL | write!(&mut v, "foo/rbar/n");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `writeln!()` instead
|
LL | writeln!(&mut v, "foo/rbar");
| ^^^^^^^ --
error: aborting due to 9 previous errors