about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lexer/src/unescape.rs17
-rw-r--r--compiler/rustc_lexer/src/unescape/tests.rs5
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs5
-rw-r--r--src/test/ui/fmt/format-string-error-2.rs6
-rw-r--r--src/test/ui/fmt/format-string-error-2.stderr2
-rw-r--r--src/test/ui/str/str-escape.rs11
-rw-r--r--src/test/ui/str/str-escape.stderr21
7 files changed, 59 insertions, 8 deletions
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs
index 9a96c03cd3c..b970c9e4911 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,
         }
     }
@@ -315,12 +319,17 @@ where
     where
         F: FnMut(Range<usize>, Result<char, EscapeError>),
     {
-        let str = chars.as_str();
-        let first_non_space = str
+        let tail = chars.as_str();
+        let first_non_space = tail
             .bytes()
             .position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r')
-            .unwrap_or(str.len());
-        let tail = &str[first_non_space..];
+            .unwrap_or(tail.len());
+        if tail[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 = &tail[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
             // skipped.  The +1 is necessary to account for the leading \ that started the escape.
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 test_unescape_str_warn() {
         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 test_unescape_str_warn() {
             (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();
+        }
     }
 }
 
diff --git a/src/test/ui/fmt/format-string-error-2.rs b/src/test/ui/fmt/format-string-error-2.rs
index 69fed2cb69a..1f7f0d8f6be 100644
--- a/src/test/ui/fmt/format-string-error-2.rs
+++ b/src/test/ui/fmt/format-string-error-2.rs
@@ -5,7 +5,7 @@ fn main() {
     a");
     //~^ ERROR invalid format string
     format!("{ \
-
+               \
     b");
     //~^ ERROR invalid format string
     format!(r#"{ \
@@ -38,12 +38,12 @@ fn main() {
     { \
     	\
     b \
-
+      \
     ");
     //~^^^ ERROR invalid format string
     format!(r#"
 raw  { \
-
+       \
     c"#);
     //~^^^ ERROR invalid format string
     format!(r#"
diff --git a/src/test/ui/fmt/format-string-error-2.stderr b/src/test/ui/fmt/format-string-error-2.stderr
index c421fe49ef0..76cdfbb93bf 100644
--- a/src/test/ui/fmt/format-string-error-2.stderr
+++ b/src/test/ui/fmt/format-string-error-2.stderr
@@ -19,7 +19,7 @@ error: invalid format string: expected `'}'`, found `'b'`
    |
 LL |     format!("{ \
    |              - because of this opening brace
-LL | 
+LL |                \
 LL |     b");
    |     ^ expected `}` in format string
    |
diff --git a/src/test/ui/str/str-escape.rs b/src/test/ui/str/str-escape.rs
new file mode 100644
index 00000000000..0264632fd24
--- /dev/null
+++ b/src/test/ui/str/str-escape.rs
@@ -0,0 +1,11 @@
+// check-pass
+fn main() {
+    let s = "\
+
+             ";
+    //~^^^ WARNING multiple lines skipped by escaped newline
+    let s = "foo\
+             bar
+             ";
+    //~^^^ WARNING non-ASCII whitespace symbol '\u{a0}' is not skipped
+}
diff --git a/src/test/ui/str/str-escape.stderr b/src/test/ui/str/str-escape.stderr
new file mode 100644
index 00000000000..b2501f1a214
--- /dev/null
+++ b/src/test/ui/str/str-escape.stderr
@@ -0,0 +1,21 @@
+warning: multiple lines skipped by escaped newline
+  --> $DIR/str-escape.rs:3:14
+   |
+LL |       let s = "\
+   |  ______________^
+LL | |
+LL | |              ";
+   | |_____________^ skipping everything up to and including this point
+
+warning: non-ASCII whitespace symbol '\u{a0}' is not skipped
+  --> $DIR/str-escape.rs:7:17
+   |
+LL |       let s = "foo\
+   |  _________________^
+LL | |              bar
+   | |   ^ non-ASCII whitespace symbol '\u{a0}' is not skipped
+   | |___|
+   | 
+
+warning: 2 warnings emitted
+