Rollup merge of #61568 - Mark-Simulacrum:symbol-fmt-macros, r=estebank

Use Symbol, Span in libfmt_macros

I'm not super happy with this, personally, but I think it might be a decent start -- happy to take suggestions as to how to expand this or change things further.

r? @estebank

Fixes #60795
This commit is contained in:
Mazdak Farrokhzad 2019-06-12 04:22:47 +02:00 committed by GitHub
commit d78260d781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 175 additions and 151 deletions

View File

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

View File

@ -8,3 +8,6 @@ edition = "2018"
name = "fmt_macros" name = "fmt_macros"
path = "lib.rs" path = "lib.rs"
crate-type = ["dylib"] crate-type = ["dylib"]
[dependencies]
syntax_pos = { path = "../libsyntax_pos" }

View File

@ -25,6 +25,17 @@ use std::str;
use std::string; use std::string;
use std::iter; use std::iter;
use syntax_pos::{InnerSpan, Symbol};
#[derive(Copy, Clone)]
struct InnerOffset(usize);
impl InnerOffset {
fn to(self, end: InnerOffset) -> InnerSpan {
InnerSpan::new(self.0, end.0)
}
}
/// A piece is a portion of the format string which represents the next part /// A piece is a portion of the format string which represents the next part
/// to emit. These are emitted as a stream by the `Parser` class. /// to emit. These are emitted as a stream by the `Parser` class.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
@ -40,7 +51,7 @@ pub enum Piece<'a> {
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct Argument<'a> { pub struct Argument<'a> {
/// Where to find this argument /// Where to find this argument
pub position: Position<'a>, pub position: Position,
/// How to format the argument /// How to format the argument
pub format: FormatSpec<'a>, pub format: FormatSpec<'a>,
} }
@ -55,9 +66,9 @@ pub struct FormatSpec<'a> {
/// Packed version of various flags provided /// Packed version of various flags provided
pub flags: u32, pub flags: u32,
/// The integer precision to use /// The integer precision to use
pub precision: Count<'a>, pub precision: Count,
/// The string width requested for the resulting format /// The string width requested for the resulting format
pub width: Count<'a>, pub width: Count,
/// The descriptor string representing the name of the format desired for /// The descriptor string representing the name of the format desired for
/// this argument, this can be empty or any number of characters, although /// this argument, this can be empty or any number of characters, although
/// it is required to be one word. /// it is required to be one word.
@ -66,16 +77,16 @@ pub struct FormatSpec<'a> {
/// Enum describing where an argument for a format can be located. /// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum Position<'a> { pub enum Position {
/// The argument is implied to be located at an index /// The argument is implied to be located at an index
ArgumentImplicitlyIs(usize), ArgumentImplicitlyIs(usize),
/// The argument is located at a specific index given in the format /// The argument is located at a specific index given in the format
ArgumentIs(usize), ArgumentIs(usize),
/// The argument has a name. /// The argument has a name.
ArgumentNamed(&'a str), ArgumentNamed(Symbol),
} }
impl Position<'_> { impl Position {
pub fn index(&self) -> Option<usize> { pub fn index(&self) -> Option<usize> {
match self { match self {
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
@ -120,11 +131,11 @@ pub enum Flag {
/// A count is used for the precision and width parameters of an integer, and /// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer. /// can reference either an argument or a literal integer.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum Count<'a> { pub enum Count {
/// The count is specified explicitly. /// The count is specified explicitly.
CountIs(usize), CountIs(usize),
/// The count is specified by the argument with the given name. /// The count is specified by the argument with the given name.
CountIsName(&'a str), CountIsName(Symbol),
/// The count is specified by the argument at the given index. /// The count is specified by the argument at the given index.
CountIsParam(usize), CountIsParam(usize),
/// The count is implied and cannot be explicitly specified. /// The count is implied and cannot be explicitly specified.
@ -135,9 +146,8 @@ pub struct ParseError {
pub description: string::String, pub description: string::String,
pub note: Option<string::String>, pub note: Option<string::String>,
pub label: string::String, pub label: string::String,
pub start: SpanIndex, pub span: InnerSpan,
pub end: SpanIndex, pub secondary_label: Option<(string::String, InnerSpan)>,
pub secondary_label: Option<(string::String, SpanIndex, SpanIndex)>,
} }
/// The parser structure for interpreting the input format string. This is /// The parser structure for interpreting the input format string. This is
@ -156,24 +166,15 @@ pub struct Parser<'a> {
/// `Some(raw count)` when the string is "raw", used to position spans correctly /// `Some(raw count)` when the string is "raw", used to position spans correctly
style: Option<usize>, style: Option<usize>,
/// Start and end byte offset of every successfully parsed argument /// Start and end byte offset of every successfully parsed argument
pub arg_places: Vec<(SpanIndex, SpanIndex)>, pub arg_places: Vec<InnerSpan>,
/// Characters that need to be shifted /// Characters that need to be shifted
skips: Vec<usize>, skips: Vec<usize>,
/// Span offset of the last opening brace seen, used for error reporting /// Span of the last opening brace seen, used for error reporting
last_opening_brace_pos: Option<SpanIndex>, last_opening_brace: Option<InnerSpan>,
/// Wether the source string is comes from `println!` as opposed to `format!` or `print!` /// Wether the source string is comes from `println!` as opposed to `format!` or `print!`
append_newline: bool, append_newline: bool,
} }
#[derive(Clone, Copy, Debug)]
pub struct SpanIndex(pub usize);
impl SpanIndex {
pub fn unwrap(self) -> usize {
self.0
}
}
impl<'a> Iterator for Parser<'a> { impl<'a> Iterator for Parser<'a> {
type Item = Piece<'a>; type Item = Piece<'a>;
@ -181,19 +182,20 @@ impl<'a> Iterator for Parser<'a> {
if let Some(&(pos, c)) = self.cur.peek() { if let Some(&(pos, c)) = self.cur.peek() {
match c { match c {
'{' => { '{' => {
let curr_last_brace = self.last_opening_brace_pos; let curr_last_brace = self.last_opening_brace;
self.last_opening_brace_pos = Some(self.to_span_index(pos)); let byte_pos = self.to_span_index(pos);
self.last_opening_brace = Some(byte_pos.to(InnerOffset(byte_pos.0 + 1)));
self.cur.next(); self.cur.next();
if self.consume('{') { if self.consume('{') {
self.last_opening_brace_pos = curr_last_brace; self.last_opening_brace = curr_last_brace;
Some(String(self.string(pos + 1))) Some(String(self.string(pos + 1)))
} else { } else {
let arg = self.argument(); let arg = self.argument();
if let Some(arg_pos) = self.must_consume('}').map(|end| { if let Some(end) = self.must_consume('}') {
(self.to_span_index(pos), self.to_span_index(end + 1)) let start = self.to_span_index(pos);
}) { let end = self.to_span_index(end + 1);
self.arg_places.push(arg_pos); self.arg_places.push(start.to(end));
} }
Some(NextArgument(arg)) Some(NextArgument(arg))
} }
@ -208,8 +210,7 @@ impl<'a> Iterator for Parser<'a> {
"unmatched `}` found", "unmatched `}` found",
"unmatched `}`", "unmatched `}`",
"if you intended to print `}`, you can escape it using `}}`", "if you intended to print `}`, you can escape it using `}}`",
err_pos, err_pos.to(err_pos),
err_pos,
); );
None None
} }
@ -241,7 +242,7 @@ impl<'a> Parser<'a> {
style, style,
arg_places: vec![], arg_places: vec![],
skips, skips,
last_opening_brace_pos: None, last_opening_brace: None,
append_newline, append_newline,
} }
} }
@ -253,15 +254,13 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
description: S1, description: S1,
label: S2, label: S2,
start: SpanIndex, span: InnerSpan,
end: SpanIndex,
) { ) {
self.errors.push(ParseError { self.errors.push(ParseError {
description: description.into(), description: description.into(),
note: None, note: None,
label: label.into(), label: label.into(),
start, span,
end,
secondary_label: None, secondary_label: None,
}); });
} }
@ -274,15 +273,13 @@ impl<'a> Parser<'a> {
description: S1, description: S1,
label: S2, label: S2,
note: S3, note: S3,
start: SpanIndex, span: InnerSpan,
end: SpanIndex,
) { ) {
self.errors.push(ParseError { self.errors.push(ParseError {
description: description.into(), description: description.into(),
note: Some(note.into()), note: Some(note.into()),
label: label.into(), label: label.into(),
start, span,
end,
secondary_label: None, secondary_label: None,
}); });
} }
@ -303,22 +300,21 @@ impl<'a> Parser<'a> {
} }
} }
fn raw(&self) -> usize { fn to_span_index(&self, pos: usize) -> InnerOffset {
self.style.map(|raw| raw + 1).unwrap_or(0)
}
fn to_span_index(&self, pos: usize) -> SpanIndex {
let mut pos = pos; let mut pos = pos;
// This handles the raw string case, the raw argument is the number of #
// in r###"..."### (we need to add one because of the `r`).
let raw = self.style.map(|raw| raw + 1).unwrap_or(0);
for skip in &self.skips { for skip in &self.skips {
if pos > *skip { if pos > *skip {
pos += 1; pos += 1;
} else if pos == *skip && self.raw() == 0 { } else if pos == *skip && raw == 0 {
pos += 1; pos += 1;
} else { } else {
break; break;
} }
} }
SpanIndex(self.raw() + pos + 1) InnerOffset(raw + pos + 1)
} }
/// Forces consumption of the specified character. If the character is not /// Forces consumption of the specified character. If the character is not
@ -336,8 +332,8 @@ impl<'a> Parser<'a> {
let label = "expected `}`".to_owned(); let label = "expected `}`".to_owned();
let (note, secondary_label) = if c == '}' { let (note, secondary_label) = if c == '}' {
(Some("if you intended to print `{`, you can escape it using `{{`".to_owned()), (Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
self.last_opening_brace_pos.map(|pos| { self.last_opening_brace.map(|sp| {
("because of this opening brace".to_owned(), pos, pos) ("because of this opening brace".to_owned(), sp)
})) }))
} else { } else {
(None, None) (None, None)
@ -346,8 +342,7 @@ impl<'a> Parser<'a> {
description, description,
note, note,
label, label,
start: pos, span: pos.to(pos),
end: pos,
secondary_label, secondary_label,
}); });
None None
@ -361,8 +356,8 @@ impl<'a> Parser<'a> {
let label = format!("expected `{:?}`", c); let label = format!("expected `{:?}`", c);
let (note, secondary_label) = if c == '}' { let (note, secondary_label) = if c == '}' {
(Some("if you intended to print `{`, you can escape it using `{{`".to_owned()), (Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
self.last_opening_brace_pos.map(|pos| { self.last_opening_brace.map(|sp| {
("because of this opening brace".to_owned(), pos, pos) ("because of this opening brace".to_owned(), sp)
})) }))
} else { } else {
(None, None) (None, None)
@ -371,12 +366,11 @@ impl<'a> Parser<'a> {
description, description,
note, note,
label, label,
start: pos, span: pos.to(pos),
end: pos,
secondary_label, secondary_label,
}); });
} else { } else {
self.err(description, format!("expected `{:?}`", c), pos, pos); self.err(description, format!("expected `{:?}`", c), pos.to(pos));
} }
None None
} }
@ -435,20 +429,24 @@ impl<'a> Parser<'a> {
/// integer index of an argument, a named argument, or a blank string. /// integer index of an argument, a named argument, or a blank string.
/// Returns `Some(parsed_position)` if the position is not implicitly /// Returns `Some(parsed_position)` if the position is not implicitly
/// consuming a macro argument, `None` if it's the case. /// consuming a macro argument, `None` if it's the case.
fn position(&mut self) -> Option<Position<'a>> { fn position(&mut self) -> Option<Position> {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
Some(ArgumentIs(i)) Some(ArgumentIs(i))
} else { } else {
match self.cur.peek() { match self.cur.peek() {
Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())), Some(&(_, c)) if c.is_alphabetic() => {
Some(ArgumentNamed(Symbol::intern(self.word())))
}
Some(&(pos, c)) if c == '_' => { Some(&(pos, c)) if c == '_' => {
let invalid_name = self.string(pos); let invalid_name = self.string(pos);
self.err_with_note(format!("invalid argument name `{}`", invalid_name), self.err_with_note(format!("invalid argument name `{}`", invalid_name),
"invalid argument name", "invalid argument name",
"argument names cannot start with an underscore", "argument names cannot start with an underscore",
self.to_span_index(pos), self.to_span_index(pos).to(
self.to_span_index(pos + invalid_name.len())); self.to_span_index(pos + invalid_name.len())
Some(ArgumentNamed(invalid_name)) ),
);
Some(ArgumentNamed(Symbol::intern(invalid_name)))
}, },
// This is an `ArgumentNext`. // This is an `ArgumentNext`.
@ -556,7 +554,7 @@ impl<'a> Parser<'a> {
/// Parses a Count parameter at the current position. This does not check /// Parses a Count parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not /// for 'CountIsNextParam' because that is only used in precision, not
/// width. /// width.
fn count(&mut self) -> Count<'a> { fn count(&mut self) -> Count {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
if self.consume('$') { if self.consume('$') {
CountIsParam(i) CountIsParam(i)
@ -570,7 +568,7 @@ impl<'a> Parser<'a> {
self.cur = tmp; self.cur = tmp;
CountImplied CountImplied
} else if self.consume('$') { } else if self.consume('$') {
CountIsName(word) CountIsName(Symbol::intern(word))
} else { } else {
self.cur = tmp; self.cur = tmp;
CountImplied CountImplied
@ -760,6 +758,8 @@ mod tests {
} }
#[test] #[test]
fn format_counts() { fn format_counts() {
use syntax_pos::{GLOBALS, Globals, edition};
GLOBALS.set(&Globals::new(edition::DEFAULT_EDITION), || {
same("{:10s}", same("{:10s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentImplicitlyIs(0), position: ArgumentImplicitlyIs(0),
@ -815,11 +815,12 @@ mod tests {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
flags: 0, flags: 0,
precision: CountIsName("b"), precision: CountIsName(Symbol::intern("b")),
width: CountIsName("a"), width: CountIsName(Symbol::intern("a")),
ty: "s", ty: "s",
}, },
})]); })]);
});
} }
#[test] #[test]
fn format_flags() { fn format_flags() {

View File

@ -353,7 +353,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
_ => { _ => {
// this is a "direct", user-specified, rather than derived, // this is a "direct", user-specified, rather than derived,
// obligation. // obligation.
flags.push(("direct".to_owned(), None)); flags.push((sym::direct, None));
} }
} }
@ -365,27 +365,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// Currently I'm leaving it for what I need for `try`. // Currently I'm leaving it for what I need for `try`.
if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
let method = self.tcx.item_name(item); let method = self.tcx.item_name(item);
flags.push(("from_method".to_owned(), None)); flags.push((sym::from_method, None));
flags.push(("from_method".to_owned(), Some(method.to_string()))); flags.push((sym::from_method, Some(method.to_string())));
} }
} }
if let Some(t) = self.get_parent_trait_ref(&obligation.cause.code) { if let Some(t) = self.get_parent_trait_ref(&obligation.cause.code) {
flags.push(("parent_trait".to_owned(), Some(t))); flags.push((sym::parent_trait, Some(t)));
} }
if let Some(k) = obligation.cause.span.compiler_desugaring_kind() { if let Some(k) = obligation.cause.span.compiler_desugaring_kind() {
flags.push(("from_desugaring".to_owned(), None)); flags.push((sym::from_desugaring, None));
flags.push(("from_desugaring".to_owned(), Some(k.name().to_string()))); flags.push((sym::from_desugaring, Some(k.name().to_string())));
} }
let generics = self.tcx.generics_of(def_id); let generics = self.tcx.generics_of(def_id);
let self_ty = trait_ref.self_ty(); let self_ty = trait_ref.self_ty();
// This is also included through the generics list as `Self`, // This is also included through the generics list as `Self`,
// but the parser won't allow you to use it // but the parser won't allow you to use it
flags.push(("_Self".to_owned(), Some(self_ty.to_string()))); flags.push((sym::_Self, Some(self_ty.to_string())));
if let Some(def) = self_ty.ty_adt_def() { if let Some(def) = self_ty.ty_adt_def() {
// We also want to be able to select self's original // We also want to be able to select self's original
// signature with no type arguments resolved // signature with no type arguments resolved
flags.push(("_Self".to_owned(), Some(self.tcx.type_of(def.did).to_string()))); flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
} }
for param in generics.params.iter() { for param in generics.params.iter() {
@ -396,38 +396,38 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}, },
GenericParamDefKind::Lifetime => continue, GenericParamDefKind::Lifetime => continue,
}; };
let name = param.name.to_string(); let name = param.name.as_symbol();
flags.push((name, Some(value))); flags.push((name, Some(value)));
} }
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
flags.push(("crate_local".to_owned(), None)); flags.push((sym::crate_local, None));
} }
// Allow targeting all integers using `{integral}`, even if the exact type was resolved // Allow targeting all integers using `{integral}`, even if the exact type was resolved
if self_ty.is_integral() { if self_ty.is_integral() {
flags.push(("_Self".to_owned(), Some("{integral}".to_owned()))); flags.push((sym::_Self, Some("{integral}".to_owned())));
} }
if let ty::Array(aty, len) = self_ty.sty { if let ty::Array(aty, len) = self_ty.sty {
flags.push(("_Self".to_owned(), Some("[]".to_owned()))); flags.push((sym::_Self, Some("[]".to_owned())));
flags.push(("_Self".to_owned(), Some(format!("[{}]", aty)))); flags.push((sym::_Self, Some(format!("[{}]", aty))));
if let Some(def) = aty.ty_adt_def() { if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the array's type's original // We also want to be able to select the array's type's original
// signature with no type arguments resolved // signature with no type arguments resolved
flags.push(( flags.push((
"_Self".to_owned(), sym::_Self,
Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), Some(format!("[{}]", self.tcx.type_of(def.did).to_string())),
)); ));
let tcx = self.tcx; let tcx = self.tcx;
if let Some(len) = len.assert_usize(tcx) { if let Some(len) = len.assert_usize(tcx) {
flags.push(( flags.push((
"_Self".to_owned(), sym::_Self,
Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)),
)); ));
} else { } else {
flags.push(( flags.push((
"_Self".to_owned(), sym::_Self,
Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())),
)); ));
} }

View File

@ -7,7 +7,7 @@ use crate::util::nodemap::FxHashMap;
use syntax::ast::{MetaItem, NestedMetaItem}; use syntax::ast::{MetaItem, NestedMetaItem};
use syntax::attr; use syntax::attr;
use syntax::symbol::sym; use syntax::symbol::{Symbol, kw, sym};
use syntax_pos::Span; use syntax_pos::Span;
use syntax_pos::symbol::LocalInternedString; use syntax_pos::symbol::LocalInternedString;
@ -52,7 +52,7 @@ fn parse_error(tcx: TyCtxt<'_, '_, '_>, span: Span,
} }
impl<'a, 'gcx, 'tcx> OnUnimplementedDirective { impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
pub fn parse(tcx: TyCtxt<'a, 'gcx, 'tcx>, fn parse(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId, trait_def_id: DefId,
items: &[NestedMetaItem], items: &[NestedMetaItem],
span: Span, span: Span,
@ -167,7 +167,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
pub fn evaluate(&self, pub fn evaluate(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: ty::TraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
options: &[(String, Option<String>)]) options: &[(Symbol, Option<String>)])
-> OnUnimplementedNote -> OnUnimplementedNote
{ {
let mut message = None; let mut message = None;
@ -180,7 +180,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| { if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
c.ident().map_or(false, |ident| { c.ident().map_or(false, |ident| {
options.contains(&( options.contains(&(
ident.to_string(), ident.name,
c.value_str().map(|s| s.as_str().to_string()) c.value_str().map(|s| s.as_str().to_string())
)) ))
}) })
@ -203,8 +203,8 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
} }
} }
let options: FxHashMap<String, String> = options.into_iter() let options: FxHashMap<Symbol, String> = options.into_iter()
.filter_map(|(k, v)| v.as_ref().map(|v| (k.to_owned(), v.to_owned()))) .filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned())))
.collect(); .collect();
OnUnimplementedNote { OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref, &options)), label: label.map(|l| l.format(tcx, trait_ref, &options)),
@ -215,7 +215,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
} }
impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString { impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
pub fn try_parse(tcx: TyCtxt<'a, 'gcx, 'tcx>, fn try_parse(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_def_id: DefId, trait_def_id: DefId,
from: LocalInternedString, from: LocalInternedString,
err_sp: Span) err_sp: Span)
@ -241,16 +241,16 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
Piece::String(_) => (), // Normal string, no need to check it Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position { Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed // `{Self}` is allowed
Position::ArgumentNamed(s) if s == "Self" => (), Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed // `{ThisTraitsName}` is allowed
Position::ArgumentNamed(s) if s == name.as_str() => (), Position::ArgumentNamed(s) if s == name => (),
// `{from_method}` is allowed // `{from_method}` is allowed
Position::ArgumentNamed(s) if s == "from_method" => (), Position::ArgumentNamed(s) if s == sym::from_method => (),
// `{from_desugaring}` is allowed // `{from_desugaring}` is allowed
Position::ArgumentNamed(s) if s == "from_desugaring" => (), Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
// So is `{A}` if A is a type parameter // So is `{A}` if A is a type parameter
Position::ArgumentNamed(s) => match generics.params.iter().find(|param| { Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
param.name.as_str() == s param.name.as_symbol() == s
}) { }) {
Some(_) => (), Some(_) => (),
None => { None => {
@ -276,7 +276,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
&self, &self,
tcx: TyCtxt<'a, 'gcx, 'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: ty::TraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
options: &FxHashMap<String, String>, options: &FxHashMap<Symbol, String>,
) -> String { ) -> String {
let name = tcx.item_name(trait_ref.def_id); let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id); let trait_str = tcx.def_path_str(trait_ref.def_id);
@ -289,9 +289,9 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
}, },
GenericParamDefKind::Lifetime => return None GenericParamDefKind::Lifetime => return None
}; };
let name = param.name.to_string(); let name = param.name.as_symbol();
Some((name, value)) Some((name, value))
}).collect::<FxHashMap<String, String>>(); }).collect::<FxHashMap<Symbol, String>>();
let empty_string = String::new(); let empty_string = String::new();
let parser = Parser::new(&self.0, None, vec![], false); let parser = Parser::new(&self.0, None, vec![], false);
@ -299,15 +299,15 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
match p { match p {
Piece::String(s) => s, Piece::String(s) => s,
Piece::NextArgument(a) => match a.position { Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => match generic_map.get(s) { Position::ArgumentNamed(s) => match generic_map.get(&s) {
Some(val) => val, Some(val) => val,
None if s == name.as_str() => { None if s == name => {
&trait_str &trait_str
} }
None => { None => {
if let Some(val) = options.get(s) { if let Some(val) = options.get(&s) {
val val
} else if s == "from_desugaring" || s == "from_method" { } else if s == sym::from_desugaring || s == sym::from_method {
// don't break messages using these two arguments incorrectly // don't break messages using these two arguments incorrectly
&empty_string &empty_string
} else { } else {

View File

@ -2,7 +2,7 @@ use errors::Applicability;
use syntax::parse::lexer::{StringReader as Lexer}; use syntax::parse::lexer::{StringReader as Lexer};
use syntax::parse::{ParseSess, token}; use syntax::parse::{ParseSess, token};
use syntax::source_map::FilePathMapping; use syntax::source_map::FilePathMapping;
use syntax_pos::FileName; use syntax_pos::{InnerSpan, FileName};
use crate::clean; use crate::clean;
use crate::core::DocContext; use crate::core::DocContext;
@ -63,7 +63,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
} }
if code_block.syntax.is_none() && code_block.is_fenced { if code_block.syntax.is_none() && code_block.is_fenced {
let sp = sp.from_inner_byte_pos(0, 3); let sp = sp.from_inner(InnerSpan::new(0, 3));
diag.span_suggestion( diag.span_suggestion(
sp, sp,
"mark blocks that do not contain Rust code as text", "mark blocks that do not contain Rust code as text",

View File

@ -6,7 +6,7 @@ use rustc::lint as lint;
use rustc::middle::privacy::AccessLevels; use rustc::middle::privacy::AccessLevels;
use rustc::util::nodemap::DefIdSet; use rustc::util::nodemap::DefIdSet;
use std::mem; use std::mem;
use syntax_pos::{DUMMY_SP, Span}; use syntax_pos::{DUMMY_SP, InnerSpan, Span};
use std::ops::Range; use std::ops::Range;
use crate::clean::{self, GetDefId, Item}; use crate::clean::{self, GetDefId, Item};
@ -440,10 +440,10 @@ crate fn source_span_for_markdown_range(
} }
} }
let sp = span_of_attrs(attrs).from_inner_byte_pos( let sp = span_of_attrs(attrs).from_inner(InnerSpan::new(
md_range.start + start_bytes, md_range.start + start_bytes,
md_range.end + start_bytes + end_bytes, md_range.end + start_bytes + end_bytes,
); ));
Some(sp) Some(sp)
} }

View File

@ -28,7 +28,7 @@ enum ArgumentType {
enum Position { enum Position {
Exact(usize), Exact(usize),
Named(String), Named(Symbol),
} }
struct Context<'a, 'b: 'a> { struct Context<'a, 'b: 'a> {
@ -57,7 +57,7 @@ struct Context<'a, 'b: 'a> {
/// Unique format specs seen for each argument. /// Unique format specs seen for each argument.
arg_unique_types: Vec<Vec<ArgumentType>>, arg_unique_types: Vec<Vec<ArgumentType>>,
/// Map from named arguments to their resolved indices. /// Map from named arguments to their resolved indices.
names: FxHashMap<String, usize>, names: FxHashMap<Symbol, usize>,
/// The latest consecutive literal strings, or empty if there weren't any. /// The latest consecutive literal strings, or empty if there weren't any.
literal: String, literal: String,
@ -127,9 +127,9 @@ fn parse_args<'a>(
ecx: &mut ExtCtxt<'a>, ecx: &mut ExtCtxt<'a>,
sp: Span, sp: Span,
tts: &[tokenstream::TokenTree] tts: &[tokenstream::TokenTree]
) -> Result<(P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<String, usize>), DiagnosticBuilder<'a>> { ) -> Result<(P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>), DiagnosticBuilder<'a>> {
let mut args = Vec::<P<ast::Expr>>::new(); let mut args = Vec::<P<ast::Expr>>::new();
let mut names = FxHashMap::<String, usize>::default(); let mut names = FxHashMap::<Symbol, usize>::default();
let mut p = ecx.new_parser_from_tts(tts); let mut p = ecx.new_parser_from_tts(tts);
@ -158,11 +158,10 @@ fn parse_args<'a>(
"expected ident, positional arguments cannot follow named arguments", "expected ident, positional arguments cannot follow named arguments",
)); ));
}; };
let name: &str = &name.as_str();
p.expect(&token::Eq)?; p.expect(&token::Eq)?;
let e = p.parse_expr()?; let e = p.parse_expr()?;
if let Some(prev) = names.get(name) { if let Some(prev) = names.get(&name) {
ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name)) ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name))
.span_note(args[*prev].span, "previously here") .span_note(args[*prev].span, "previously here")
.emit(); .emit();
@ -174,7 +173,7 @@ fn parse_args<'a>(
// if the input is valid, we can simply append to the positional // if the input is valid, we can simply append to the positional
// args. And remember the names. // args. And remember the names.
let slot = args.len(); let slot = args.len();
names.insert(name.to_string(), slot); names.insert(name, slot);
args.push(e); args.push(e);
} else { } else {
let e = p.parse_expr()?; let e = p.parse_expr()?;
@ -188,7 +187,7 @@ impl<'a, 'b> Context<'a, 'b> {
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format // NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g., `format_args!("{foo}")`. // arguments, e.g., `format_args!("{foo}")`.
let lookup = |s| *self.names.get(s).unwrap_or(&0); let lookup = |s: Symbol| *self.names.get(&s).unwrap_or(&0);
match *p { match *p {
parse::String(_) => {} parse::String(_) => {}
@ -222,7 +221,7 @@ impl<'a, 'b> Context<'a, 'b> {
// it's written second, so it should come after width/precision. // it's written second, so it should come after width/precision.
let pos = match arg.position { let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
parse::ArgumentNamed(s) => Named(s.to_string()), parse::ArgumentNamed(s) => Named(s),
}; };
let ty = Placeholder(arg.format.ty.to_string()); let ty = Placeholder(arg.format.ty.to_string());
@ -232,7 +231,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
} }
fn verify_count(&mut self, c: parse::Count<'_>) { fn verify_count(&mut self, c: parse::Count) {
match c { match c {
parse::CountImplied | parse::CountImplied |
parse::CountIs(..) => {} parse::CountIs(..) => {}
@ -240,7 +239,7 @@ impl<'a, 'b> Context<'a, 'b> {
self.verify_arg_type(Exact(i), Count); self.verify_arg_type(Exact(i), Count);
} }
parse::CountIsName(s) => { parse::CountIsName(s) => {
self.verify_arg_type(Named(s.to_string()), Count); self.verify_arg_type(Named(s), Count);
} }
} }
} }
@ -390,7 +389,7 @@ impl<'a, 'b> Context<'a, 'b> {
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, Symbol::intern(s)]) ecx.std_path(&[sym::fmt, sym::rt, sym::v1, Symbol::intern(s)])
} }
fn build_count(&self, c: parse::Count<'_>) -> P<ast::Expr> { fn build_count(&self, c: parse::Count) -> P<ast::Expr> {
let sp = self.macsp; let sp = self.macsp;
let count = |c, arg| { let count = |c, arg| {
let mut path = Context::rtpath(self.ecx, "Count"); let mut path = Context::rtpath(self.ecx, "Count");
@ -739,7 +738,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
sp: Span, sp: Span,
efmt: P<ast::Expr>, efmt: P<ast::Expr>,
args: Vec<P<ast::Expr>>, args: Vec<P<ast::Expr>>,
names: FxHashMap<String, usize>, names: FxHashMap<Symbol, usize>,
append_newline: bool) append_newline: bool)
-> P<ast::Expr> { -> P<ast::Expr> {
// NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because // NOTE: this verbose way of initializing `Vec<Vec<ArgumentType>>` is because
@ -901,15 +900,15 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
if !parser.errors.is_empty() { if !parser.errors.is_empty() {
let err = parser.errors.remove(0); let err = parser.errors.remove(0);
let sp = fmt.span.from_inner_byte_pos(err.start.unwrap(), err.end.unwrap()); let sp = fmt.span.from_inner(err.span);
let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}",
err.description)); err.description));
e.span_label(sp, err.label + " in format string"); e.span_label(sp, err.label + " in format string");
if let Some(note) = err.note { if let Some(note) = err.note {
e.note(&note); e.note(&note);
} }
if let Some((label, start, end)) = err.secondary_label { if let Some((label, span)) = err.secondary_label {
let sp = fmt.span.from_inner_byte_pos(start.unwrap(), end.unwrap()); let sp = fmt.span.from_inner(span);
e.span_label(sp, label); e.span_label(sp, label);
} }
e.emit(); e.emit();
@ -917,9 +916,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
} }
let arg_spans = parser.arg_places.iter() let arg_spans = parser.arg_places.iter()
.map(|&(parse::SpanIndex(start), parse::SpanIndex(end))| { .map(|span| fmt.span.from_inner(*span))
fmt.span.from_inner_byte_pos(start, end)
})
.collect(); .collect();
let mut cx = Context { let mut cx = Context {
@ -1044,7 +1041,9 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
let mut show_doc_note = false; let mut show_doc_note = false;
let mut suggestions = vec![]; let mut suggestions = vec![];
for sub in foreign::$kind::iter_subs(fmt_str) { // account for `"` and account for raw strings `r#`
let padding = str_style.map(|i| i + 2).unwrap_or(1);
for sub in foreign::$kind::iter_subs(fmt_str, padding) {
let trn = match sub.translate() { let trn = match sub.translate() {
Some(trn) => trn, Some(trn) => trn,
@ -1064,10 +1063,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt<'_>,
show_doc_note = true; show_doc_note = true;
} }
if let Some((start, end)) = pos { if let Some(inner_sp) = pos {
// account for `"` and account for raw strings `r#` let sp = fmt_sp.from_inner(inner_sp);
let padding = str_style.map(|i| i + 2).unwrap_or(1);
let sp = fmt_sp.from_inner_byte_pos(start + padding, end + padding);
suggestions.push((sp, trn)); suggestions.push((sp, trn));
} else { } else {
diag.help(&format!("`{}` should be written as `{}`", sub, trn)); diag.help(&format!("`{}` should be written as `{}`", sub, trn));

View File

@ -1,5 +1,6 @@
pub mod printf { pub mod printf {
use super::strcursor::StrCursor as Cur; use super::strcursor::StrCursor as Cur;
use syntax_pos::InnerSpan;
/// Represents a single `printf`-style substitution. /// Represents a single `printf`-style substitution.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
@ -18,7 +19,7 @@ pub mod printf {
} }
} }
pub fn position(&self) -> Option<(usize, usize)> { pub fn position(&self) -> Option<InnerSpan> {
match *self { match *self {
Substitution::Format(ref fmt) => Some(fmt.position), Substitution::Format(ref fmt) => Some(fmt.position),
_ => None, _ => None,
@ -28,7 +29,7 @@ pub mod printf {
pub fn set_position(&mut self, start: usize, end: usize) { pub fn set_position(&mut self, start: usize, end: usize) {
match self { match self {
Substitution::Format(ref mut fmt) => { Substitution::Format(ref mut fmt) => {
fmt.position = (start, end); fmt.position = InnerSpan::new(start, end);
} }
_ => {} _ => {}
} }
@ -65,7 +66,7 @@ pub mod printf {
/// Type of parameter being converted. /// Type of parameter being converted.
pub type_: &'a str, pub type_: &'a str,
/// Byte offset for the start and end of this formatting directive. /// Byte offset for the start and end of this formatting directive.
pub position: (usize, usize), pub position: InnerSpan,
} }
impl Format<'_> { impl Format<'_> {
@ -263,10 +264,10 @@ pub mod printf {
} }
/// Returns an iterator over all substitutions in a given string. /// Returns an iterator over all substitutions in a given string.
pub fn iter_subs(s: &str) -> Substitutions<'_> { pub fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
Substitutions { Substitutions {
s, s,
pos: 0, pos: start_pos,
} }
} }
@ -282,9 +283,9 @@ pub mod printf {
let (mut sub, tail) = parse_next_substitution(self.s)?; let (mut sub, tail) = parse_next_substitution(self.s)?;
self.s = tail; self.s = tail;
match sub { match sub {
Substitution::Format(_) => if let Some((start, end)) = sub.position() { Substitution::Format(_) => if let Some(inner_span) = sub.position() {
sub.set_position(start + self.pos, end + self.pos); sub.set_position(inner_span.start + self.pos, inner_span.end + self.pos);
self.pos += end; self.pos += inner_span.end;
} }
Substitution::Escape => self.pos += 2, Substitution::Escape => self.pos += 2,
} }
@ -373,7 +374,7 @@ pub mod printf {
precision: None, precision: None,
length: None, length: None,
type_: at.slice_between(next).unwrap(), type_: at.slice_between(next).unwrap(),
position: (start.at, next.at), position: InnerSpan::new(start.at, next.at),
}), }),
next.slice_after() next.slice_after()
)); ));
@ -560,7 +561,7 @@ pub mod printf {
drop(next); drop(next);
end = at; end = at;
let position = (start.at, end.at); let position = InnerSpan::new(start.at, end.at);
let f = Format { let f = Format {
span: start.slice_between(end).unwrap(), span: start.slice_between(end).unwrap(),
@ -650,7 +651,7 @@ pub mod printf {
precision: $prec, precision: $prec,
length: $len, length: $len,
type_: $type_, type_: $type_,
position: $pos, position: syntax_pos::InnerSpan::new($pos.0, $pos.1),
}), }),
"!" "!"
)) ))
@ -711,7 +712,7 @@ pub mod printf {
#[test] #[test]
fn test_iter() { fn test_iter() {
let s = "The %d'th word %% is: `%.*s` %!\n"; let s = "The %d'th word %% is: `%.*s` %!\n";
let subs: Vec<_> = iter_subs(s).map(|sub| sub.translate()).collect(); let subs: Vec<_> = iter_subs(s, 0).map(|sub| sub.translate()).collect();
assert_eq!( assert_eq!(
subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(), subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(),
vec![Some("{}"), None, Some("{:.*}"), None] vec![Some("{}"), None, Some("{:.*}"), None]
@ -761,6 +762,7 @@ pub mod printf {
pub mod shell { pub mod shell {
use super::strcursor::StrCursor as Cur; use super::strcursor::StrCursor as Cur;
use syntax_pos::InnerSpan;
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Substitution<'a> { pub enum Substitution<'a> {
@ -778,11 +780,11 @@ pub mod shell {
} }
} }
pub fn position(&self) -> Option<(usize, usize)> { pub fn position(&self) -> Option<InnerSpan> {
match self { match self {
Substitution::Ordinal(_, pos) | Substitution::Ordinal(_, pos) |
Substitution::Name(_, pos) | Substitution::Name(_, pos) |
Substitution::Escape(pos) => Some(*pos), Substitution::Escape(pos) => Some(InnerSpan::new(pos.0, pos.1)),
} }
} }
@ -804,10 +806,10 @@ pub mod shell {
} }
/// Returns an iterator over all substitutions in a given string. /// Returns an iterator over all substitutions in a given string.
pub fn iter_subs(s: &str) -> Substitutions<'_> { pub fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
Substitutions { Substitutions {
s, s,
pos: 0, pos: start_pos,
} }
} }
@ -823,7 +825,7 @@ pub mod shell {
match parse_next_substitution(self.s) { match parse_next_substitution(self.s) {
Some((mut sub, tail)) => { Some((mut sub, tail)) => {
self.s = tail; self.s = tail;
if let Some((start, end)) = sub.position() { if let Some(InnerSpan { start, end }) = sub.position() {
sub.set_position(start + self.pos, end + self.pos); sub.set_position(start + self.pos, end + self.pos);
self.pos += end; self.pos += end;
} }
@ -940,7 +942,7 @@ pub mod shell {
fn test_iter() { fn test_iter() {
use super::iter_subs; use super::iter_subs;
let s = "The $0'th word $$ is: `$WORD` $!\n"; let s = "The $0'th word $$ is: `$WORD` $!\n";
let subs: Vec<_> = iter_subs(s).map(|sub| sub.translate()).collect(); let subs: Vec<_> = iter_subs(s, 0).map(|sub| sub.translate()).collect();
assert_eq!( assert_eq!(
subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(), subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(),
vec![Some("{0}"), None, Some("{WORD}")] vec![Some("{0}"), None, Some("{WORD}")]

View File

@ -505,10 +505,10 @@ impl Span {
) )
} }
pub fn from_inner_byte_pos(self, start: usize, end: usize) -> Span { pub fn from_inner(self, inner: InnerSpan) -> Span {
let span = self.data(); let span = self.data();
Span::new(span.lo + BytePos::from_usize(start), Span::new(span.lo + BytePos::from_usize(inner.start),
span.lo + BytePos::from_usize(end), span.lo + BytePos::from_usize(inner.end),
span.ctxt) span.ctxt)
} }
@ -1396,6 +1396,18 @@ pub struct MalformedSourceMapPositions {
pub end_pos: BytePos pub end_pos: BytePos
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct InnerSpan {
pub start: usize,
pub end: usize,
}
impl InnerSpan {
pub fn new(start: usize, end: usize) -> InnerSpan {
InnerSpan { start, end }
}
}
// Given a slice of line start positions and a position, returns the index of // Given a slice of line start positions and a position, returns the index of
// the line the position is on. Returns -1 if the position is located before // the line the position is on. Returns -1 if the position is located before
// the first line. // the first line.

View File

@ -203,6 +203,7 @@ symbols! {
core_intrinsics, core_intrinsics,
crate_id, crate_id,
crate_in_paths, crate_in_paths,
crate_local,
crate_name, crate_name,
crate_type, crate_type,
crate_visibility_modifier, crate_visibility_modifier,
@ -221,6 +222,7 @@ symbols! {
deref, deref,
deref_mut, deref_mut,
derive, derive,
direct,
doc, doc,
doc_alias, doc_alias,
doc_cfg, doc_cfg,
@ -278,8 +280,10 @@ symbols! {
format_args_nl, format_args_nl,
from, from,
From, From,
from_desugaring,
from_error, from_error,
from_generator, from_generator,
from_method,
from_ok, from_ok,
from_usize, from_usize,
fundamental, fundamental,
@ -443,6 +447,7 @@ symbols! {
panic_impl, panic_impl,
panic_implementation, panic_implementation,
panic_runtime, panic_runtime,
parent_trait,
partial_cmp, partial_cmp,
PartialOrd, PartialOrd,
passes, passes,
@ -569,6 +574,7 @@ symbols! {
__rust_unstable_column, __rust_unstable_column,
rvalue_static_promotion, rvalue_static_promotion,
sanitizer_runtime, sanitizer_runtime,
_Self,
self_in_typedefs, self_in_typedefs,
self_struct_ctor, self_struct_ctor,
Send, Send,