Skip to content

Commit 3c17abc

Browse files
committed
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.
1 parent da2ce22 commit 3c17abc

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

src/libfmt_macros/lib.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ pub struct Parser<'a> {
139139
input: &'a str,
140140
cur: iter::Peekable<str::CharIndices<'a>>,
141141
/// Error messages accumulated during parsing
142-
pub errors: Vec<string::String>,
142+
pub errors: Vec<(string::String, Option<string::String>)>,
143143
/// Current position of implicit positional argument pointer
144144
curarg: usize,
145145
}
@@ -165,7 +165,9 @@ impl<'a> Iterator for Parser<'a> {
165165
if self.consume('}') {
166166
Some(String(self.string(pos + 1)))
167167
} else {
168-
self.err("unmatched `}` found");
168+
self.err_with_note("unmatched `}` found",
169+
"if you intended to print `}`, \
170+
you can escape it using `}}`");
169171
None
170172
}
171173
}
@@ -192,7 +194,14 @@ impl<'a> Parser<'a> {
192194
/// String, but I think it does when this eventually uses conditions so it
193195
/// might as well start using it now.
194196
fn err(&mut self, msg: &str) {
195-
self.errors.push(msg.to_owned());
197+
self.errors.push((msg.to_owned(), None));
198+
}
199+
200+
/// Notifies of an error. The message doesn't actually need to be of type
201+
/// String, but I think it does when this eventually uses conditions so it
202+
/// might as well start using it now.
203+
fn err_with_note(&mut self, msg: &str, note: &str) {
204+
self.errors.push((msg.to_owned(), Some(note.to_owned())));
196205
}
197206

198207
/// Optionally consumes the specified character. If the character is not at
@@ -222,7 +231,13 @@ impl<'a> Parser<'a> {
222231
self.err(&format!("expected `{:?}`, found `{:?}`", c, maybe));
223232
}
224233
} else {
225-
self.err(&format!("expected `{:?}` but string was terminated", c));
234+
let msg = &format!("expected `{:?}` but string was terminated", c);
235+
if c == '}' {
236+
self.err_with_note(msg,
237+
"if you intended to print `{`, you can escape it using `{{`");
238+
} else {
239+
self.err(msg);
240+
}
226241
}
227242
}
228243

src/libsyntax_ext/format.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,8 +756,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
756756
}
757757

758758
if !parser.errors.is_empty() {
759-
cx.ecx.span_err(cx.fmtsp,
760-
&format!("invalid format string: {}", parser.errors.remove(0)));
759+
let (err, note) = parser.errors.remove(0);
760+
let mut e = cx.ecx.struct_span_err(cx.fmtsp, &format!("invalid format string: {}", err));
761+
if let Some(note) = note {
762+
e.note(&note);
763+
}
764+
e.emit();
761765
return DummyResult::raw_expr(sp);
762766
}
763767
if !cx.literal.is_empty() {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
println!("{");
13+
println!("{{}}");
14+
println!("}");
15+
}
16+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: invalid format string: expected `'}'` but string was terminated
2+
--> $DIR/format-string-error.rs:12:5
3+
|
4+
12 | println!("{");
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: if you intended to print `{`, you can escape it using `{{`
8+
= note: this error originates in a macro outside of the current crate
9+
10+
error: invalid format string: unmatched `}` found
11+
--> $DIR/format-string-error.rs:14:5
12+
|
13+
14 | println!("}");
14+
| ^^^^^^^^^^^^^^
15+
|
16+
= note: if you intended to print `}`, you can escape it using `}}`
17+
= note: this error originates in a macro outside of the current crate
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)