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

View File

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

View File

@ -228,7 +228,7 @@ impl<'a, 'b> Context<'a, 'b> {
// argument second, if it's an implicit positional parameter
// it's written second, so it should come after width/precision.
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()),
};
@ -254,25 +254,28 @@ impl<'a, 'b> Context<'a, 'b> {
fn describe_num_args(&self) -> String {
match self.args.len() {
0 => "no arguments were given".to_string(),
1 => "there is only 1 argument".to_string(),
x => format!("there are only {} arguments", x),
1 => "there is 1 argument".to_string(),
x => format!("there are {} arguments", x),
}
}
/// Handle invalid references to positional arguments. Output different
/// errors for the case where all arguments are positional and for when
/// there are named arguments in the format string.
fn report_invalid_references(&self) {
/// there are named arguments or numbered positional arguments in the
/// format string.
fn report_invalid_references(&self, numbered_position_args: bool) {
let mut e;
let mut refs: Vec<String> = self.invalid_refs
.iter()
.map(|r| r.to_string())
.collect();
let msg = if self.names.is_empty() {
format!("{} positional argument{} in format string, but {}",
self.pieces.len(),
if self.pieces.len() > 1 { "s" } else { "" },
self.describe_num_args())
if self.names.is_empty() && !numbered_position_args {
e = self.ecx.mut_span_err(self.fmtsp,
&format!("{} positional argument{} in format string, but {}",
self.pieces.len(),
if self.pieces.len() > 1 { "s" } else { "" },
self.describe_num_args()));
} else {
let arg_list = match refs.len() {
1 => format!("argument {}", refs.pop().unwrap()),
@ -281,12 +284,14 @@ impl<'a, 'b> Context<'a, 'b> {
head=refs.join(", "))
};
format!("invalid reference to positional {} ({})",
arg_list,
self.describe_num_args())
e = self.ecx.mut_span_err(self.fmtsp,
&format!("invalid reference to positional {} ({})",
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
@ -431,7 +436,8 @@ impl<'a, 'b> Context<'a, 'b> {
}
};
match arg.position {
parse::ArgumentIs(i) => {
parse::ArgumentIs(i)
| parse::ArgumentImplicitlyIs(i) => {
// Map to index in final generated argument array
// in case of multiple types specified
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();
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 {
cx.report_invalid_references();
cx.report_invalid_references(numbered_position_args);
}
// 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
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
format!("{} {}");
//~^ ERROR: 2 positional arguments in format string, but no arguments were given
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);
//~^ 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);
//~^ 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);
//~^ 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!("", 1, 2); //~ ERROR: multiple unused formatting arguments
@ -41,6 +45,7 @@ fn main() {
format!("{}", 1, foo=2); //~ ERROR: named argument never used
format!("{foo}", 1, foo=2); //~ ERROR: 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=1, 2); //~ ERROR: positional arguments cannot follow