1
Fork 0

Retain information on whether a format argument has explicit position

This commit is contained in:
Tommy Ip 2017-11-09 17:16:25 +00:00
parent 13a416298c
commit b577b9aef3
4 changed files with 57 additions and 32 deletions

View file

@ -73,7 +73,9 @@ 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<'a> {
/// The argument is located at a specific index. /// The arugment is implied to be located at an index
ArgumentImplicitlyIs(usize),
/// 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(&'a str),
@ -275,7 +277,7 @@ impl<'a> Parser<'a> {
None => { None => {
let i = self.curarg; let i = self.curarg;
self.curarg += 1; self.curarg += 1;
ArgumentIs(i) ArgumentImplicitlyIs(i)
} }
}; };
@ -517,7 +519,7 @@ mod tests {
fn format_nothing() { fn format_nothing() {
same("{}", same("{}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: fmtdflt(), format: fmtdflt(),
})]); })]);
} }
@ -595,7 +597,7 @@ mod tests {
fn format_counts() { fn format_counts() {
same("{:10s}", same("{:10s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -607,7 +609,7 @@ mod tests {
})]); })]);
same("{:10$.10s}", same("{:10$.10s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -619,7 +621,7 @@ mod tests {
})]); })]);
same("{:.*s}", same("{:.*s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(1), position: ArgumentImplicitlyIs(1),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -631,7 +633,7 @@ mod tests {
})]); })]);
same("{:.10$s}", same("{:.10$s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -643,7 +645,7 @@ mod tests {
})]); })]);
same("{:a$.b$s}", same("{:a$.b$s}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -658,7 +660,7 @@ mod tests {
fn format_flags() { fn format_flags() {
same("{:-}", same("{:-}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,
@ -670,7 +672,7 @@ mod tests {
})]); })]);
same("{:+#}", same("{:+#}",
&[NextArgument(Argument { &[NextArgument(Argument {
position: ArgumentIs(0), position: ArgumentImplicitlyIs(0),
format: FormatSpec { format: FormatSpec {
fill: None, fill: None,
align: AlignUnknown, align: AlignUnknown,

View file

@ -254,7 +254,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedFormatString {
} }
}, },
// `{:1}` and `{}` are not to be used // `{:1}` and `{}` are not to be used
Position::ArgumentIs(_) => { Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
span_err!(tcx.sess, span, E0231, span_err!(tcx.sess, span, E0231,
"only named substitution \ "only named substitution \
parameters are allowed"); parameters are allowed");

View file

@ -228,7 +228,7 @@ impl<'a, 'b> Context<'a, 'b> {
// argument second, if it's an implicit positional parameter // argument second, if it's an implicit positional parameter
// 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) => Exact(i), parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
parse::ArgumentNamed(s) => Named(s.to_string()), parse::ArgumentNamed(s) => Named(s.to_string()),
}; };
@ -254,25 +254,28 @@ impl<'a, 'b> Context<'a, 'b> {
fn describe_num_args(&self) -> String { fn describe_num_args(&self) -> String {
match self.args.len() { match self.args.len() {
0 => "no arguments were given".to_string(), 0 => "no arguments were given".to_string(),
1 => "there is only 1 argument".to_string(), 1 => "there is 1 argument".to_string(),
x => format!("there are only {} arguments", x), x => format!("there are {} arguments", x),
} }
} }
/// Handle invalid references to positional arguments. Output different /// Handle invalid references to positional arguments. Output different
/// errors for the case where all arguments are positional and for when /// errors for the case where all arguments are positional and for when
/// there are named arguments in the format string. /// there are named arguments or numbered positional arguments in the
fn report_invalid_references(&self) { /// format string.
fn report_invalid_references(&self, numbered_position_args: bool) {
let mut e;
let mut refs: Vec<String> = self.invalid_refs let mut refs: Vec<String> = self.invalid_refs
.iter() .iter()
.map(|r| r.to_string()) .map(|r| r.to_string())
.collect(); .collect();
let msg = if self.names.is_empty() { if self.names.is_empty() && !numbered_position_args {
format!("{} positional argument{} in format string, but {}", e = self.ecx.mut_span_err(self.fmtsp,
self.pieces.len(), &format!("{} positional argument{} in format string, but {}",
if self.pieces.len() > 1 { "s" } else { "" }, self.pieces.len(),
self.describe_num_args()) if self.pieces.len() > 1 { "s" } else { "" },
self.describe_num_args()));
} else { } else {
let arg_list = match refs.len() { let arg_list = match refs.len() {
1 => format!("argument {}", refs.pop().unwrap()), 1 => format!("argument {}", refs.pop().unwrap()),
@ -281,12 +284,14 @@ impl<'a, 'b> Context<'a, 'b> {
head=refs.join(", ")) head=refs.join(", "))
}; };
format!("invalid reference to positional {} ({})", e = self.ecx.mut_span_err(self.fmtsp,
arg_list, &format!("invalid reference to positional {} ({})",
self.describe_num_args()) arg_list,
self.describe_num_args()));
e.note("positional arguments are zero-based");
}; };
self.ecx.span_err(self.fmtsp, &msg[..]); e.emit();
} }
/// Actually verifies and tracks a given format placeholder /// Actually verifies and tracks a given format placeholder
@ -431,7 +436,8 @@ impl<'a, 'b> Context<'a, 'b> {
} }
}; };
match arg.position { match arg.position {
parse::ArgumentIs(i) => { parse::ArgumentIs(i)
| parse::ArgumentImplicitlyIs(i) => {
// Map to index in final generated argument array // Map to index in final generated argument array
// in case of multiple types specified // in case of multiple types specified
let arg_idx = match arg_index_consumed.get_mut(i) { let arg_idx = match arg_index_consumed.get_mut(i) {
@ -740,6 +746,18 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
} }
} }
let numbered_position_args = pieces.iter().any(|arg: &parse::Piece| {
match *arg {
parse::String(_) => false,
parse::NextArgument(arg) => {
match arg.position {
parse::Position::ArgumentIs(_) => true,
_ => false,
}
}
}
});
cx.build_index_map(); cx.build_index_map();
let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()]; let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()];
@ -766,7 +784,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
} }
if cx.invalid_refs.len() >= 1 { if cx.invalid_refs.len() >= 1 {
cx.report_invalid_references(); cx.report_invalid_references(numbered_position_args);
} }
// Make sure that all arguments were used and all arguments have types. // Make sure that all arguments were used and all arguments have types.

View file

@ -17,22 +17,26 @@ fn main() {
//~^ ERROR: 1 positional argument in format string, but no arguments were given //~^ ERROR: 1 positional argument in format string, but no arguments were given
format!("{1}", 1); format!("{1}", 1);
//~^ ERROR: 1 positional argument in format string, but there is only 1 argument //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
//~^^ ERROR: argument never used //~^^ ERROR: argument never used
format!("{} {}"); format!("{} {}");
//~^ ERROR: 2 positional arguments in format string, but no arguments were given //~^ ERROR: 2 positional arguments in format string, but no arguments were given
format!("{0} {1}", 1); format!("{0} {1}", 1);
//~^ ERROR: 2 positional arguments in format string, but there is only 1 argument //~^ ERROR: invalid reference to positional argument 1 (there is 1 argument)
format!("{0} {1} {2}", 1, 2); format!("{0} {1} {2}", 1, 2);
//~^ ERROR: 3 positional arguments in format string, but there are only 2 arguments //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
format!("{} {value} {} {}", 1, value=2); format!("{} {value} {} {}", 1, value=2);
//~^ ERROR: invalid reference to positional argument 2 (there are only 2 arguments) //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments)
format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2);
//~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are only 3 arguments) //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments)
format!("{} {foo} {} {bar} {}", 1, 2, 3);
//~^ ERROR: there is no argument named `foo`
//~^^ ERROR: there is no argument named `bar`
format!("{foo}"); //~ ERROR: no argument named `foo` format!("{foo}"); //~ ERROR: no argument named `foo`
format!("", 1, 2); //~ ERROR: multiple unused formatting arguments format!("", 1, 2); //~ ERROR: multiple unused formatting arguments
@ -41,6 +45,7 @@ fn main() {
format!("{}", 1, foo=2); //~ ERROR: named argument never used format!("{}", 1, foo=2); //~ ERROR: named argument never used
format!("{foo}", 1, foo=2); //~ ERROR: argument never used format!("{foo}", 1, foo=2); //~ ERROR: argument never used
format!("", foo=2); //~ ERROR: named argument never used format!("", foo=2); //~ ERROR: named argument never used
format!("{} {}", 1, 2, foo=1, bar=2); //~ ERROR: multiple unused formatting arguments
format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument format!("{foo}", foo=1, foo=2); //~ ERROR: duplicate argument
format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow format!("", foo=1, 2); //~ ERROR: positional arguments cannot follow