about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2016-11-12 10:38:42 +0200
committerGitHub <noreply@github.com>2016-11-12 10:38:42 +0200
commit048daa62246abc5dd441c34ffd2834cb6e173060 (patch)
treea38e3b75c73b28708201e08b37268499879998a8
parent5439cb5bdc7557f20a9dbedb57a636da68445440 (diff)
parent3c17abc4d955080baa410e9b697bf5be37b0d079 (diff)
downloadrust-048daa62246abc5dd441c34ffd2834cb6e173060.tar.gz
rust-048daa62246abc5dd441c34ffd2834cb6e173060.zip
Rollup merge of #37695 - estebank:unescaped-curly, r=alexcrichton
On fmt string with unescaped `{` note how to escape

On cases of malformed format strings where a `{` hasn't been properly escaped, like `println!("{");`, present a NOTE explaining how to escape the `{` char.

Fix #34300.
-rw-r--r--src/libfmt_macros/lib.rs23
-rw-r--r--src/libsyntax_ext/format.rs8
-rw-r--r--src/test/ui/fmt/format-string-error.rs16
-rw-r--r--src/test/ui/fmt/format-string-error.stderr20
4 files changed, 61 insertions, 6 deletions
diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs
index e7d401f0929..b179a16e55e 100644
--- a/src/libfmt_macros/lib.rs
+++ b/src/libfmt_macros/lib.rs
@@ -139,7 +139,7 @@ pub struct Parser<'a> {
     input: &'a str,
     cur: iter::Peekable<str::CharIndices<'a>>,
     /// Error messages accumulated during parsing
-    pub errors: Vec<string::String>,
+    pub errors: Vec<(string::String, Option<string::String>)>,
     /// Current position of implicit positional argument pointer
     curarg: usize,
 }
@@ -165,7 +165,9 @@ impl<'a> Iterator for Parser<'a> {
                     if self.consume('}') {
                         Some(String(self.string(pos + 1)))
                     } else {
-                        self.err("unmatched `}` found");
+                        self.err_with_note("unmatched `}` found",
+                                           "if you intended to print `}`, \
+                                           you can escape it using `}}`");
                         None
                     }
                 }
@@ -192,7 +194,14 @@ impl<'a> Parser<'a> {
     /// String, but I think it does when this eventually uses conditions so it
     /// might as well start using it now.
     fn err(&mut self, msg: &str) {
-        self.errors.push(msg.to_owned());
+        self.errors.push((msg.to_owned(), None));
+    }
+
+    /// Notifies of an error. The message doesn't actually need to be of type
+    /// String, but I think it does when this eventually uses conditions so it
+    /// might as well start using it now.
+    fn err_with_note(&mut self, msg: &str, note: &str) {
+        self.errors.push((msg.to_owned(), Some(note.to_owned())));
     }
 
     /// Optionally consumes the specified character. If the character is not at
@@ -222,7 +231,13 @@ impl<'a> Parser<'a> {
                 self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe));
             }
         } else {
-            self.err(&format!("expected `{:?}` but string was terminated", c));
+            let msg = &format!("expected `{:?}` but string was terminated", c);
+            if c == '}' {
+                self.err_with_note(msg,
+                                   "if you intended to print `{`, you can escape it using `{{`");
+            } else {
+                self.err(msg);
+            }
         }
     }
 
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 117bb39f8e7..6eba8baf5b8 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -756,8 +756,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
     }
 
     if !parser.errors.is_empty() {
-        cx.ecx.span_err(cx.fmtsp,
-                        &format!("invalid format string: {}", parser.errors.remove(0)));
+        let (err, note) = parser.errors.remove(0);
+        let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err));
+        if let Some(note) = note {
+            e.note(&note);
+        }
+        e.emit();
         return DummyResult::raw_expr(sp);
     }
     if !cx.literal.is_empty() {
diff --git a/src/test/ui/fmt/format-string-error.rs b/src/test/ui/fmt/format-string-error.rs
new file mode 100644
index 00000000000..ec715b3f0ba
--- /dev/null
+++ b/src/test/ui/fmt/format-string-error.rs
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    println!("{");
+    println!("{{}}");
+    println!("}");
+}
+
diff --git a/src/test/ui/fmt/format-string-error.stderr b/src/test/ui/fmt/format-string-error.stderr
new file mode 100644
index 00000000000..58b392f0b8d
--- /dev/null
+++ b/src/test/ui/fmt/format-string-error.stderr
@@ -0,0 +1,20 @@
+error: invalid format string: expected `'}'` but string was terminated
+  --> $DIR/format-string-error.rs:12:5
+   |
+12 |     println!("{");
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: if you intended to print `{`, you can escape it using `{{`
+   = note: this error originates in a macro outside of the current crate
+
+error: invalid format string: unmatched `}` found
+  --> $DIR/format-string-error.rs:14:5
+   |
+14 |     println!("}");
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+   = note: this error originates in a macro outside of the current crate
+
+error: aborting due to 2 previous errors
+