1
Fork 0

Use correct spans for format string errors

When encountering format string errors in a raw string, or regular
string literal with embedded newlines, account for the positional
change to use correct spans.

:drive by fix: 🚗
This commit is contained in:
Esteban Küber 2018-07-19 23:14:00 -07:00
parent 154dee2dcc
commit f4306ffbfc
7 changed files with 123 additions and 21 deletions

View file

@ -715,6 +715,9 @@ dependencies = [
[[package]] [[package]]
name = "fmt_macros" name = "fmt_macros"
version = "0.0.0" version = "0.0.0"
dependencies = [
"syntax 0.0.0",
]
[[package]] [[package]]
name = "fnv" name = "fnv"

View file

@ -7,3 +7,6 @@ version = "0.0.0"
name = "fmt_macros" name = "fmt_macros"
path = "lib.rs" path = "lib.rs"
crate-type = ["dylib"] crate-type = ["dylib"]
[dependencies]
syntax = { path = "../libsyntax" }

View file

@ -28,6 +28,8 @@ pub use self::Alignment::*;
pub use self::Flag::*; pub use self::Flag::*;
pub use self::Count::*; pub use self::Count::*;
extern crate syntax;
use std::str; use std::str;
use std::string; use std::string;
use std::iter; use std::iter;
@ -150,18 +152,27 @@ pub struct Parser<'a> {
pub errors: Vec<ParseError>, pub errors: Vec<ParseError>,
/// Current position of implicit positional argument pointer /// Current position of implicit positional argument pointer
curarg: usize, curarg: usize,
/// The style of the string (raw or not), used to position spans correctly
style: syntax::ast::StrStyle,
/// How many newlines have been seen in the string so far, to adjust the error spans
seen_newlines: usize,
} }
impl<'a> Iterator for Parser<'a> { impl<'a> Iterator for Parser<'a> {
type Item = Piece<'a>; type Item = Piece<'a>;
fn next(&mut self) -> Option<Piece<'a>> { fn next(&mut self) -> Option<Piece<'a>> {
let raw = match self.style {
syntax::ast::StrStyle::Raw(raw) => raw as usize + self.seen_newlines,
_ => 0,
};
if let Some(&(pos, c)) = self.cur.peek() { if let Some(&(pos, c)) = self.cur.peek() {
match c { match c {
'{' => { '{' => {
let pos = pos + raw + 1;
self.cur.next(); self.cur.next();
if self.consume('{') { if self.consume('{') {
Some(String(self.string(pos + 1))) Some(String(self.string(pos)))
} else { } else {
let ret = Some(NextArgument(self.argument())); let ret = Some(NextArgument(self.argument()));
self.must_consume('}'); self.must_consume('}');
@ -169,8 +180,8 @@ impl<'a> Iterator for Parser<'a> {
} }
} }
'}' => { '}' => {
let pos = pos + raw + 1;
self.cur.next(); self.cur.next();
let pos = pos + 1;
if self.consume('}') { if self.consume('}') {
Some(String(self.string(pos))) Some(String(self.string(pos)))
} else { } else {
@ -184,6 +195,10 @@ impl<'a> Iterator for Parser<'a> {
None None
} }
} }
'\n' => {
self.seen_newlines += 1;
Some(String(self.string(pos)))
}
_ => Some(String(self.string(pos))), _ => Some(String(self.string(pos))),
} }
} else { } else {
@ -194,12 +209,14 @@ impl<'a> Iterator for Parser<'a> {
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
/// Creates a new parser for the given format string /// Creates a new parser for the given format string
pub fn new(s: &'a str) -> Parser<'a> { pub fn new(s: &'a str, style: syntax::ast::StrStyle) -> Parser<'a> {
Parser { Parser {
input: s, input: s,
cur: s.char_indices().peekable(), cur: s.char_indices().peekable(),
errors: vec![], errors: vec![],
curarg: 0, curarg: 0,
style,
seen_newlines: 0,
} }
} }
@ -262,14 +279,19 @@ impl<'a> Parser<'a> {
/// found, an error is emitted. /// found, an error is emitted.
fn must_consume(&mut self, c: char) { fn must_consume(&mut self, c: char) {
self.ws(); self.ws();
let raw = match self.style {
syntax::ast::StrStyle::Raw(raw) => raw as usize,
_ => 0,
};
let padding = raw + self.seen_newlines;
if let Some(&(pos, maybe)) = self.cur.peek() { if let Some(&(pos, maybe)) = self.cur.peek() {
if c == maybe { if c == maybe {
self.cur.next(); self.cur.next();
} else { } else {
self.err(format!("expected `{:?}`, found `{:?}`", c, maybe), self.err(format!("expected `{:?}`, found `{:?}`", c, maybe),
format!("expected `{}`", c), format!("expected `{}`", c),
pos + 1, pos + padding + 1,
pos + 1); pos + padding + 1);
} }
} else { } else {
let msg = format!("expected `{:?}` but string was terminated", c); let msg = format!("expected `{:?}` but string was terminated", c);
@ -282,8 +304,8 @@ impl<'a> Parser<'a> {
self.err_with_note(msg, self.err_with_note(msg,
format!("expected `{:?}`", c), format!("expected `{:?}`", c),
"if you intended to print `{`, you can escape it using `{{`", "if you intended to print `{`, you can escape it using `{{`",
pos, pos + padding,
pos); pos + padding);
} else { } else {
self.err(msg, format!("expected `{:?}`", c), pos, pos); self.err(msg, format!("expected `{:?}`", c), pos, pos);
} }
@ -540,7 +562,7 @@ mod tests {
use super::*; use super::*;
fn same(fmt: &'static str, p: &[Piece<'static>]) { fn same(fmt: &'static str, p: &[Piece<'static>]) {
let parser = Parser::new(fmt); let parser = Parser::new(fmt, syntax::ast::StrStyle::Cooked);
assert!(parser.collect::<Vec<Piece<'static>>>() == p); assert!(parser.collect::<Vec<Piece<'static>>>() == p);
} }
@ -556,7 +578,7 @@ mod tests {
} }
fn musterr(s: &str) { fn musterr(s: &str) {
let mut p = Parser::new(s); let mut p = Parser::new(fmt, syntax::ast::StrStyle::Cooked);
p.next(); p.next();
assert!(!p.errors.is_empty()); assert!(!p.errors.is_empty());
} }

View file

@ -15,7 +15,7 @@ use ty::{self, TyCtxt, GenericParamDefKind};
use util::common::ErrorReported; use util::common::ErrorReported;
use util::nodemap::FxHashMap; use util::nodemap::FxHashMap;
use syntax::ast::{MetaItem, NestedMetaItem}; use syntax::ast::{self, MetaItem, NestedMetaItem};
use syntax::attr; use syntax::attr;
use syntax_pos::Span; use syntax_pos::Span;
use syntax_pos::symbol::LocalInternedString; use syntax_pos::symbol::LocalInternedString;
@ -242,7 +242,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
{ {
let name = tcx.item_name(trait_def_id); let name = tcx.item_name(trait_def_id);
let generics = tcx.generics_of(trait_def_id); let generics = tcx.generics_of(trait_def_id);
let parser = Parser::new(&self.0); let parser = Parser::new(&self.0, ast::StrStyle::Cooked);
let mut result = Ok(()); let mut result = Ok(());
for token in parser { for token in parser {
match token { match token {
@ -298,7 +298,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
Some((name, value)) Some((name, value))
}).collect::<FxHashMap<String, String>>(); }).collect::<FxHashMap<String, String>>();
let parser = Parser::new(&self.0); let parser = Parser::new(&self.0, ast::StrStyle::Cooked);
parser.map(|p| { parser.map(|p| {
match p { match p {
Piece::String(s) => s, Piece::String(s) => s,

View file

@ -763,7 +763,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
}; };
let fmt_str = &*fmt.node.0.as_str(); let fmt_str = &*fmt.node.0.as_str();
let mut parser = parse::Parser::new(fmt_str); let mut parser = parse::Parser::new(fmt_str, fmt.node.1);
let mut pieces = vec![]; let mut pieces = vec![];
while let Some(mut piece) = parser.next() { while let Some(mut piece) = parser.next() {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// ignore-tidy-tab
fn main() { fn main() {
println!("{"); println!("{");
//~^ ERROR invalid format string: expected `'}'` but string was terminated //~^ ERROR invalid format string: expected `'}'` but string was terminated
@ -24,4 +26,36 @@ fn main() {
//~^ ERROR invalid format string: unmatched `}` found //~^ ERROR invalid format string: unmatched `}` found
let _ = format!("{\\}"); let _ = format!("{\\}");
//~^ ERROR invalid format string: expected `'}'`, found `'\\'` //~^ ERROR invalid format string: expected `'}'`, found `'\\'`
let _ = format!("\n\n\n{\n\n\n");
//~^ ERROR invalid format string
let _ = format!(r###"
{"###);
//~^ ERROR invalid format string
let _ = format!(r###"
{
"###);
//~^^ ERROR invalid format string
let _ = format!(r###"
}
"###);
//~^^^ ERROR invalid format string
let _ = format!(r###"
}
"###);
//~^^^ ERROR invalid format string: unmatched `}` found
} }

View file

@ -1,5 +1,5 @@
error: invalid format string: expected `'}'` but string was terminated error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:12:16 --> $DIR/format-string-error.rs:14:16
| |
LL | println!("{"); LL | println!("{");
| ^ expected `'}'` in format string | ^ expected `'}'` in format string
@ -7,7 +7,7 @@ LL | println!("{");
= note: if you intended to print `{`, you can escape it using `{{` = note: if you intended to print `{`, you can escape it using `{{`
error: invalid format string: unmatched `}` found error: invalid format string: unmatched `}` found
--> $DIR/format-string-error.rs:15:15 --> $DIR/format-string-error.rs:17:15
| |
LL | println!("}"); LL | println!("}");
| ^ unmatched `}` in format string | ^ unmatched `}` in format string
@ -15,7 +15,7 @@ LL | println!("}");
= note: if you intended to print `}`, you can escape it using `}}` = note: if you intended to print `}`, you can escape it using `}}`
error: invalid format string: invalid argument name `_foo` error: invalid format string: invalid argument name `_foo`
--> $DIR/format-string-error.rs:17:23 --> $DIR/format-string-error.rs:19:23
| |
LL | let _ = format!("{_foo}", _foo = 6usize); LL | let _ = format!("{_foo}", _foo = 6usize);
| ^^^^ invalid argument name in format string | ^^^^ invalid argument name in format string
@ -23,7 +23,7 @@ LL | let _ = format!("{_foo}", _foo = 6usize);
= note: argument names cannot start with an underscore = note: argument names cannot start with an underscore
error: invalid format string: invalid argument name `_` error: invalid format string: invalid argument name `_`
--> $DIR/format-string-error.rs:19:23 --> $DIR/format-string-error.rs:21:23
| |
LL | let _ = format!("{_}", _ = 6usize); LL | let _ = format!("{_}", _ = 6usize);
| ^ invalid argument name in format string | ^ invalid argument name in format string
@ -31,7 +31,7 @@ LL | let _ = format!("{_}", _ = 6usize);
= note: argument names cannot start with an underscore = note: argument names cannot start with an underscore
error: invalid format string: expected `'}'` but string was terminated error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:21:23 --> $DIR/format-string-error.rs:23:23
| |
LL | let _ = format!("{"); LL | let _ = format!("{");
| ^ expected `'}'` in format string | ^ expected `'}'` in format string
@ -39,7 +39,7 @@ LL | let _ = format!("{");
= note: if you intended to print `{`, you can escape it using `{{` = note: if you intended to print `{`, you can escape it using `{{`
error: invalid format string: unmatched `}` found error: invalid format string: unmatched `}` found
--> $DIR/format-string-error.rs:23:22 --> $DIR/format-string-error.rs:25:22
| |
LL | let _ = format!("}"); LL | let _ = format!("}");
| ^ unmatched `}` in format string | ^ unmatched `}` in format string
@ -47,10 +47,50 @@ LL | let _ = format!("}");
= note: if you intended to print `}`, you can escape it using `}}` = note: if you intended to print `}`, you can escape it using `}}`
error: invalid format string: expected `'}'`, found `'/'` error: invalid format string: expected `'}'`, found `'/'`
--> $DIR/format-string-error.rs:25:23 --> $DIR/format-string-error.rs:27:23
| |
LL | let _ = format!("{/}"); LL | let _ = format!("{/}");
| ^ expected `}` in format string | ^ expected `}` in format string
error: aborting due to 7 previous errors error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:29:29
|
LL | let _ = format!("/n/n/n{/n/n/n");
| ^ expected `'}'` in format string
|
= note: if you intended to print `{`, you can escape it using `{{`
error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:35:3
|
LL | {"###);
| ^ expected `'}'` in format string
|
= note: if you intended to print `{`, you can escape it using `{{`
error: invalid format string: expected `'}'` but string was terminated
--> $DIR/format-string-error.rs:42:1
|
LL |
| ^ expected `'}'` in format string
|
= note: if you intended to print `{`, you can escape it using `{{`
error: invalid format string: unmatched `}` found
--> $DIR/format-string-error.rs:49:2
|
LL | }
| ^ unmatched `}` in format string
|
= note: if you intended to print `}`, you can escape it using `}}`
error: invalid format string: unmatched `}` found
--> $DIR/format-string-error.rs:57:9
|
LL | }
| ^ unmatched `}` in format string
|
= note: if you intended to print `}`, you can escape it using `}}`
error: aborting due to 12 previous errors