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:
parent
154dee2dcc
commit
f4306ffbfc
7 changed files with 123 additions and 21 deletions
|
@ -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"
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue