Skip to content

Commit 9b8e4c6

Browse files
committed
Don't allow {} to refer to implicit captures in format_args.
1 parent 9ad5d82 commit 9b8e4c6

File tree

1 file changed

+24
-11
lines changed

1 file changed

+24
-11
lines changed

compiler/rustc_builtin_macros/src/format.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum ArgumentType {
2323

2424
enum Position {
2525
Exact(usize),
26+
Capture(usize),
2627
Named(Symbol),
2728
}
2829

@@ -47,6 +48,8 @@ struct Context<'a, 'b> {
4748
/// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
4849
/// * `names` (in JSON): `{"foo": 2}`
4950
args: Vec<P<ast::Expr>>,
51+
/// The number of arguments that were added by implicit capturing.
52+
num_captured_args: usize,
5053
/// Placeholder slot numbers indexed by argument.
5154
arg_types: Vec<Vec<usize>>,
5255
/// Unique format specs seen for each argument.
@@ -229,6 +232,11 @@ fn parse_args<'a>(
229232
}
230233

231234
impl<'a, 'b> Context<'a, 'b> {
235+
/// The number of arguments that were explicitly given.
236+
fn num_args(&self) -> usize {
237+
self.args.len() - self.num_captured_args
238+
}
239+
232240
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
233241
// NOTE: the `unwrap_or` branch is needed in case of invalid format
234242
// arguments, e.g., `format_args!("{foo}")`.
@@ -343,7 +351,7 @@ impl<'a, 'b> Context<'a, 'b> {
343351
}
344352

345353
fn describe_num_args(&self) -> Cow<'_, str> {
346-
match self.args.len() {
354+
match self.num_args() {
347355
0 => "no arguments were given".into(),
348356
1 => "there is 1 argument".into(),
349357
x => format!("there are {} arguments", x).into(),
@@ -369,7 +377,7 @@ impl<'a, 'b> Context<'a, 'b> {
369377

370378
let count = self.pieces.len()
371379
+ self.arg_with_formatting.iter().filter(|fmt| fmt.precision_span.is_some()).count();
372-
if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
380+
if self.names.is_empty() && !numbered_position_args && count != self.num_args() {
373381
e = self.ecx.struct_span_err(
374382
sp,
375383
&format!(
@@ -417,7 +425,7 @@ impl<'a, 'b> Context<'a, 'b> {
417425
if let Some(span) = fmt.precision_span {
418426
let span = self.fmtsp.from_inner(span);
419427
match fmt.precision {
420-
parse::CountIsParam(pos) if pos > self.args.len() => {
428+
parse::CountIsParam(pos) if pos > self.num_args() => {
421429
e.span_label(
422430
span,
423431
&format!(
@@ -460,7 +468,7 @@ impl<'a, 'b> Context<'a, 'b> {
460468
if let Some(span) = fmt.width_span {
461469
let span = self.fmtsp.from_inner(span);
462470
match fmt.width {
463-
parse::CountIsParam(pos) if pos > self.args.len() => {
471+
parse::CountIsParam(pos) if pos > self.num_args() => {
464472
e.span_label(
465473
span,
466474
&format!(
@@ -492,12 +500,15 @@ impl<'a, 'b> Context<'a, 'b> {
492500
/// Actually verifies and tracks a given format placeholder
493501
/// (a.k.a. argument).
494502
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
503+
if let Exact(arg) = arg {
504+
if arg >= self.num_args() {
505+
self.invalid_refs.push((arg, self.curpiece));
506+
return;
507+
}
508+
}
509+
495510
match arg {
496-
Exact(arg) => {
497-
if self.args.len() <= arg {
498-
self.invalid_refs.push((arg, self.curpiece));
499-
return;
500-
}
511+
Exact(arg) | Capture(arg) => {
501512
match ty {
502513
Placeholder(_) => {
503514
// record every (position, type) combination only once
@@ -524,7 +535,7 @@ impl<'a, 'b> Context<'a, 'b> {
524535
match self.names.get(&name) {
525536
Some(&idx) => {
526537
// Treat as positional arg.
527-
self.verify_arg_type(Exact(idx), ty)
538+
self.verify_arg_type(Capture(idx), ty)
528539
}
529540
None => {
530541
// For the moment capturing variables from format strings expanded from macros is
@@ -539,9 +550,10 @@ impl<'a, 'b> Context<'a, 'b> {
539550
} else {
540551
self.fmtsp
541552
};
553+
self.num_captured_args += 1;
542554
self.args.push(self.ecx.expr_ident(span, Ident::new(name, span)));
543555
self.names.insert(name, idx);
544-
self.verify_arg_type(Exact(idx), ty)
556+
self.verify_arg_type(Capture(idx), ty)
545557
} else {
546558
let msg = format!("there is no argument named `{}`", name);
547559
let sp = if self.is_literal {
@@ -1010,6 +1022,7 @@ pub fn expand_preparsed_format_args(
10101022
let mut cx = Context {
10111023
ecx,
10121024
args,
1025+
num_captured_args: 0,
10131026
arg_types,
10141027
arg_unique_types,
10151028
names,

0 commit comments

Comments
 (0)