From a03fbfe2ff3e7dd03af42d337b11552e782e2dc4 Mon Sep 17 00:00:00 2001 From: Anton Golov Date: Sat, 31 Jul 2021 20:35:37 +0200 Subject: [PATCH] Warn when an escaped newline skips multiple lines --- compiler/rustc_lexer/src/unescape.rs | 9 +++++++++ compiler/rustc_lexer/src/unescape/tests.rs | 5 +++++ .../rustc_parse/src/lexer/unescape_error_reporting.rs | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 9a96c03cd3c..6550eed3958 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -60,6 +60,9 @@ pub enum EscapeError { /// After a line ending with '\', the next line contains whitespace /// characters that are not skipped. UnskippedWhitespaceWarning, + + /// After a line ending with '\', multiple lines are skipped. + MultipleSkippedLinesWarning, } impl EscapeError { @@ -67,6 +70,7 @@ impl EscapeError { pub fn is_fatal(&self) -> bool { match self { EscapeError::UnskippedWhitespaceWarning => false, + EscapeError::MultipleSkippedLinesWarning => false, _ => true, } } @@ -320,6 +324,11 @@ fn skip_ascii_whitespace(chars: &mut Chars<'_>, start: usize, callback: &mut .bytes() .position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r') .unwrap_or(str.len()); + if str[1..first_non_space].contains('\n') { + // The +1 accounts for the escaping slash. + let end = start + first_non_space + 1; + callback(start..end, Err(EscapeError::MultipleSkippedLinesWarning)); + } let tail = &str[first_non_space..]; if let Some(c) = tail.chars().nth(0) { // For error reporting, we would like the span to contain the character that was not diff --git a/compiler/rustc_lexer/src/unescape/tests.rs b/compiler/rustc_lexer/src/unescape/tests.rs index 1f4dbb20f4e..fa61554afde 100644 --- a/compiler/rustc_lexer/src/unescape/tests.rs +++ b/compiler/rustc_lexer/src/unescape/tests.rs @@ -106,6 +106,10 @@ fn check(literal: &str, expected: &[(Range, Result)]) assert_eq!(unescaped, expected); } + // Check we can handle escaped newlines at the end of a file. + check("\\\n", &[]); + check("\\\n ", &[]); + check( "\\\n \u{a0} x", &[ @@ -115,6 +119,7 @@ fn check(literal: &str, expected: &[(Range, Result)]) (6..7, Ok('x')), ], ); + check("\\\n \n x", &[(0..7, Err(EscapeError::MultipleSkippedLinesWarning)), (7..8, Ok('x'))]); } #[test] diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 4e95cdc0efa..aa6b424ce2b 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -280,6 +280,11 @@ pub(crate) fn emit_unescape_error( format!("non-ASCII whitespace symbol '{}' is not skipped", c.escape_unicode()); handler.struct_span_warn(span, &msg).span_label(char_span, &msg).emit(); } + EscapeError::MultipleSkippedLinesWarning => { + let msg = "multiple lines skipped by escaped newline"; + let bottom_msg = "skipping everything up to and including this point"; + handler.struct_span_warn(span, msg).span_label(span, bottom_msg).emit(); + } } } -- 2.44.0