Skip to content

Commit 3ebdaa4

Browse files
committed
Do not deduplicate captured args while expanding format_args!
1 parent c4dc70e commit 3ebdaa4

File tree

7 files changed

+51
-20
lines changed

7 files changed

+51
-20
lines changed

compiler/rustc_ast/src/format.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ pub struct FormatArguments {
7777
arguments: Vec<FormatArgument>,
7878
num_unnamed_args: usize,
7979
num_explicit_args: usize,
80-
names: FxHashMap<Symbol, usize>,
80+
explicit_names: FxHashMap<Symbol, usize>,
8181
}
8282

8383
impl FormatArguments {
8484
pub fn new() -> Self {
8585
Self {
8686
arguments: Vec::new(),
87-
names: FxHashMap::default(),
87+
explicit_names: FxHashMap::default(),
8888
num_unnamed_args: 0,
8989
num_explicit_args: 0,
9090
}
@@ -93,13 +93,16 @@ impl FormatArguments {
9393
pub fn add(&mut self, arg: FormatArgument) -> usize {
9494
let index = self.arguments.len();
9595
if let Some(name) = arg.kind.ident() {
96-
self.names.insert(name.name, index);
97-
} else if self.names.is_empty() {
96+
self.explicit_names.insert(name.name, index);
97+
} else if let FormatArgumentKind::Normal = arg.kind
98+
&& self.explicit_names.is_empty()
99+
&& self.arguments.len() == self.num_unnamed_args
100+
{
98101
// Only count the unnamed args before the first named arg.
99102
// (Any later ones are errors.)
100103
self.num_unnamed_args += 1;
101104
}
102-
if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
105+
if !matches!(arg.kind, FormatArgumentKind::Captured) {
103106
// This is an explicit argument.
104107
// Make sure that all arguments so far are explicit.
105108
assert_eq!(
@@ -113,8 +116,8 @@ impl FormatArguments {
113116
index
114117
}
115118

116-
pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
117-
let i = *self.names.get(&name)?;
119+
pub fn by_explicit_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
120+
let i = *self.explicit_names.get(&name)?;
118121
Some((i, &self.arguments[i]))
119122
}
120123

@@ -156,15 +159,15 @@ pub enum FormatArgumentKind {
156159
/// `format_args(…, arg = 1)`
157160
Named(Ident),
158161
/// `format_args("… {arg} …")`
159-
Captured(Ident),
162+
Captured,
160163
}
161164

162165
impl FormatArgumentKind {
163166
pub fn ident(&self) -> Option<Ident> {
164167
match self {
165168
&Self::Normal => None,
166169
&Self::Named(id) => Some(id),
167-
&Self::Captured(id) => Some(id),
170+
&Self::Captured => None,
168171
}
169172
}
170173
}

compiler/rustc_ast_lowering/src/format.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ fn expand_format_args<'hir>(
462462
let placeholder_span =
463463
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
464464
let arg_span = match arg.kind {
465-
FormatArgumentKind::Captured(_) => placeholder_span,
465+
FormatArgumentKind::Captured => placeholder_span,
466466
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
467467
};
468468
let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);

compiler/rustc_builtin_macros/src/format.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a,
121121
p.bump();
122122
p.expect(exp!(Eq))?;
123123
let expr = p.parse_expr()?;
124-
if let Some((_, prev)) = args.by_name(ident.name) {
124+
if let Some((_, prev)) = args.by_explicit_name(ident.name) {
125125
ecx.dcx().emit_err(errors::FormatDuplicateArg {
126126
span: ident.span,
127127
prev: prev.kind.ident().unwrap().span,
@@ -374,12 +374,11 @@ fn make_format_args(
374374
}
375375
Name(name, span) => {
376376
let name = Symbol::intern(name);
377-
if let Some((index, _)) = args.by_name(name) {
377+
if let Some((index, _)) = args.by_explicit_name(name) {
378378
// Name found in `args`, so we resolve it to its index.
379-
if index < args.explicit_args().len() {
380-
// Mark it as used, if it was an explicit argument.
381-
used[index] = true;
382-
}
379+
assert!(index < args.explicit_args().len());
380+
// Mark it as used, as this is an explicit argument.
381+
used[index] = true;
383382
Ok(index)
384383
} else {
385384
// Name not found in `args`, so we add it as an implicitly captured argument.
@@ -394,7 +393,7 @@ fn make_format_args(
394393
unnamed_arg_after_named_arg = true;
395394
DummyResult::raw_expr(span, Some(guar))
396395
};
397-
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
396+
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured, expr }))
398397
}
399398
}
400399
};

src/tools/clippy/clippy_lints/src/format_args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
447447
let index = pos.index.unwrap();
448448
let arg = &self.format_args.arguments.all_args()[index];
449449

450-
if !matches!(arg.kind, FormatArgumentKind::Captured(_))
450+
if !matches!(arg.kind, FormatArgumentKind::Captured)
451451
&& let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind
452452
&& let [segment] = path.segments.as_slice()
453453
&& segment.args.is_none()
@@ -467,7 +467,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
467467
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
468468
// * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
469469
pos.kind != FormatArgPositionKind::Number
470-
&& (!self.ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
470+
&& (!self.ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured))
471471
}
472472
}
473473

src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl<'tcx> Visitor<'tcx> for UnitVariableCollector<'_, 'tcx> {
202202
{
203203
if let Some(macro_call) = self.macro_call
204204
&& macro_call.arguments.all_args().iter().any(|arg| {
205-
matches!(arg.kind, FormatArgumentKind::Captured(_)) && find_format_arg_expr(ex, arg).is_some()
205+
matches!(arg.kind, FormatArgumentKind::Captured) && find_format_arg_expr(ex, arg).is_some()
206206
})
207207
{
208208
self.spans.push(VariableUsage::FormatCapture);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#[attr = MacroUse {arguments: UseAll}]
2+
extern crate std;
3+
#[prelude_import]
4+
use ::std::prelude::rust_2015::*;
5+
//@ pretty-compare-only
6+
//@ pretty-mode:hir
7+
//@ pp-exact:hir-format-args-duplicated-captures.pp
8+
9+
const X: i32 = 42;
10+
11+
fn main() {
12+
let _ =
13+
{
14+
super let args = (&X, &X);
15+
super let args =
16+
[format_argument::new_display(args.0),
17+
format_argument::new_display(args.1)];
18+
unsafe { format_arguments::new(b"\xc0\x01 \xc0\x00", &args) }
19+
};
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ pretty-compare-only
2+
//@ pretty-mode:hir
3+
//@ pp-exact:hir-format-args-duplicated-captures.pp
4+
5+
const X: i32 = 42;
6+
7+
fn main() {
8+
let _ = format_args!("{X} {X}");
9+
}

0 commit comments

Comments
 (0)