Don't allow {} to refer to implicit captures in format_args.
This commit is contained in:
parent
9ad5d82f82
commit
9b8e4c63de
1 changed files with 24 additions and 11 deletions
|
@ -23,6 +23,7 @@ enum ArgumentType {
|
||||||
|
|
||||||
enum Position {
|
enum Position {
|
||||||
Exact(usize),
|
Exact(usize),
|
||||||
|
Capture(usize),
|
||||||
Named(Symbol),
|
Named(Symbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +48,8 @@ struct Context<'a, 'b> {
|
||||||
/// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
|
/// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
|
||||||
/// * `names` (in JSON): `{"foo": 2}`
|
/// * `names` (in JSON): `{"foo": 2}`
|
||||||
args: Vec<P<ast::Expr>>,
|
args: Vec<P<ast::Expr>>,
|
||||||
|
/// The number of arguments that were added by implicit capturing.
|
||||||
|
num_captured_args: usize,
|
||||||
/// Placeholder slot numbers indexed by argument.
|
/// Placeholder slot numbers indexed by argument.
|
||||||
arg_types: Vec<Vec<usize>>,
|
arg_types: Vec<Vec<usize>>,
|
||||||
/// Unique format specs seen for each argument.
|
/// Unique format specs seen for each argument.
|
||||||
|
@ -229,6 +232,11 @@ fn parse_args<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Context<'a, 'b> {
|
impl<'a, 'b> Context<'a, 'b> {
|
||||||
|
/// The number of arguments that were explicitly given.
|
||||||
|
fn num_args(&self) -> usize {
|
||||||
|
self.args.len() - self.num_captured_args
|
||||||
|
}
|
||||||
|
|
||||||
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}")`.
|
||||||
|
@ -343,7 +351,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_num_args(&self) -> Cow<'_, str> {
|
fn describe_num_args(&self) -> Cow<'_, str> {
|
||||||
match self.args.len() {
|
match self.num_args() {
|
||||||
0 => "no arguments were given".into(),
|
0 => "no arguments were given".into(),
|
||||||
1 => "there is 1 argument".into(),
|
1 => "there is 1 argument".into(),
|
||||||
x => format!("there are {} arguments", x).into(),
|
x => format!("there are {} arguments", x).into(),
|
||||||
|
@ -369,7 +377,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
|
|
||||||
let count = self.pieces.len()
|
let count = self.pieces.len()
|
||||||
+ self.arg_with_formatting.iter().filter(|fmt| fmt.precision_span.is_some()).count();
|
+ self.arg_with_formatting.iter().filter(|fmt| fmt.precision_span.is_some()).count();
|
||||||
if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
|
if self.names.is_empty() && !numbered_position_args && count != self.num_args() {
|
||||||
e = self.ecx.struct_span_err(
|
e = self.ecx.struct_span_err(
|
||||||
sp,
|
sp,
|
||||||
&format!(
|
&format!(
|
||||||
|
@ -417,7 +425,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
if let Some(span) = fmt.precision_span {
|
if let Some(span) = fmt.precision_span {
|
||||||
let span = self.fmtsp.from_inner(span);
|
let span = self.fmtsp.from_inner(span);
|
||||||
match fmt.precision {
|
match fmt.precision {
|
||||||
parse::CountIsParam(pos) if pos > self.args.len() => {
|
parse::CountIsParam(pos) if pos > self.num_args() => {
|
||||||
e.span_label(
|
e.span_label(
|
||||||
span,
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
|
@ -460,7 +468,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
if let Some(span) = fmt.width_span {
|
if let Some(span) = fmt.width_span {
|
||||||
let span = self.fmtsp.from_inner(span);
|
let span = self.fmtsp.from_inner(span);
|
||||||
match fmt.width {
|
match fmt.width {
|
||||||
parse::CountIsParam(pos) if pos > self.args.len() => {
|
parse::CountIsParam(pos) if pos > self.num_args() => {
|
||||||
e.span_label(
|
e.span_label(
|
||||||
span,
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
|
@ -492,12 +500,15 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
/// Actually verifies and tracks a given format placeholder
|
/// Actually verifies and tracks a given format placeholder
|
||||||
/// (a.k.a. argument).
|
/// (a.k.a. argument).
|
||||||
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
|
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
|
||||||
match arg {
|
if let Exact(arg) = arg {
|
||||||
Exact(arg) => {
|
if arg >= self.num_args() {
|
||||||
if self.args.len() <= arg {
|
|
||||||
self.invalid_refs.push((arg, self.curpiece));
|
self.invalid_refs.push((arg, self.curpiece));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
Exact(arg) | Capture(arg) => {
|
||||||
match ty {
|
match ty {
|
||||||
Placeholder(_) => {
|
Placeholder(_) => {
|
||||||
// record every (position, type) combination only once
|
// record every (position, type) combination only once
|
||||||
|
@ -524,7 +535,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
match self.names.get(&name) {
|
match self.names.get(&name) {
|
||||||
Some(&idx) => {
|
Some(&idx) => {
|
||||||
// Treat as positional arg.
|
// Treat as positional arg.
|
||||||
self.verify_arg_type(Exact(idx), ty)
|
self.verify_arg_type(Capture(idx), ty)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// For the moment capturing variables from format strings expanded from macros is
|
// For the moment capturing variables from format strings expanded from macros is
|
||||||
|
@ -539,9 +550,10 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||||
} else {
|
} else {
|
||||||
self.fmtsp
|
self.fmtsp
|
||||||
};
|
};
|
||||||
|
self.num_captured_args += 1;
|
||||||
self.args.push(self.ecx.expr_ident(span, Ident::new(name, span)));
|
self.args.push(self.ecx.expr_ident(span, Ident::new(name, span)));
|
||||||
self.names.insert(name, idx);
|
self.names.insert(name, idx);
|
||||||
self.verify_arg_type(Exact(idx), ty)
|
self.verify_arg_type(Capture(idx), ty)
|
||||||
} else {
|
} else {
|
||||||
let msg = format!("there is no argument named `{}`", name);
|
let msg = format!("there is no argument named `{}`", name);
|
||||||
let sp = if self.is_literal {
|
let sp = if self.is_literal {
|
||||||
|
@ -1010,6 +1022,7 @@ pub fn expand_preparsed_format_args(
|
||||||
let mut cx = Context {
|
let mut cx = Context {
|
||||||
ecx,
|
ecx,
|
||||||
args,
|
args,
|
||||||
|
num_captured_args: 0,
|
||||||
arg_types,
|
arg_types,
|
||||||
arg_unique_types,
|
arg_unique_types,
|
||||||
names,
|
names,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue