Prefer new_v1_formatted instead of new_v1 with duplicates.

This commit is contained in:
Mara Bos 2022-08-27 17:51:28 +02:00
parent 00074926bb
commit ae238efe91

View File

@ -164,18 +164,32 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
.collect(), .collect(),
); );
let has_any_format_options = fmt.template.iter().any(|piece| { // Whether we'll use the `Arguments::new_v1_formatted` form (true),
let FormatArgsPiece::Placeholder(placeholder) = piece else { return false }; // or the `Arguments::new_v1` form (false).
placeholder.format_options != Default::default() let mut use_format_options = false;
});
let (args, format_options) = if has_any_format_options { // Create a list of all _unique_ (argument, format trait) combinations.
// Create a list of all _unique_ (argument, format trait) combinations. // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] let mut argmap = FxIndexSet::default();
let mut argmap = FxIndexSet::default(); for piece in &fmt.template {
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
if placeholder.format_options != Default::default() {
// Can't use basic form if there's any formatting options.
use_format_options = true;
}
if let Ok(index) = placeholder.argument.index {
if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
// Duplicate (argument, format trait) combination,
// which we'll only put once in the args array.
use_format_options = true;
}
}
}
let format_options = use_format_options.then(|| {
// Generate: // Generate:
// &[format_spec_0, format_spec_1, format_spec_2] // &[format_spec_0, format_spec_1, format_spec_2]
let format_options = ecx.expr_array_ref( ecx.expr_array_ref(
macsp, macsp,
fmt.template fmt.template
.iter() .iter()
@ -184,34 +198,18 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
Some(make_format_spec(ecx, macsp, placeholder, &mut argmap)) Some(make_format_spec(ecx, macsp, placeholder, &mut argmap))
}) })
.collect(), .collect(),
); )
(Vec::from_iter(argmap), Some(format_options)) });
} else {
// Create a list of all (argument, format trait) pairs, one for each placeholder.
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (0, Display), (1, Display)]
let args = fmt
.template
.iter()
.filter_map(|piece| {
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
Some((
placeholder.argument.index.ok()?,
ArgumentType::Format(placeholder.format_trait),
))
})
.collect();
(args, None)
};
// If the args array contains exactly all the original arguments once, // If the args array contains exactly all the original arguments once,
// in order, we can use a simple array instead of a `match` construction. // in order, we can use a simple array instead of a `match` construction.
// However, if there's a yield point in any argument except the first one, // However, if there's a yield point in any argument except the first one,
// we don't do this, because an ArgumentV1 cannot be kept across yield points. // we don't do this, because an ArgumentV1 cannot be kept across yield points.
let use_simple_array = args.len() == fmt.arguments.len() let use_simple_array = argmap.len() == fmt.arguments.len()
&& args.iter().enumerate().all(|(i, &(j, _))| i == j) && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
&& fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg)); && fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg));
let args_expr = if use_simple_array { let args = if use_simple_array {
// Generate: // Generate:
// &[ // &[
// ::core::fmt::ArgumentV1::new_display(&arg0), // ::core::fmt::ArgumentV1::new_display(&arg0),
@ -222,7 +220,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
macsp, macsp,
fmt.arguments fmt.arguments
.into_iter() .into_iter()
.zip(args) .zip(argmap)
.map(|((arg, _), (_, ty))| { .map(|((arg, _), (_, ty))| {
let sp = arg.span.with_ctxt(macsp.ctxt()); let sp = arg.span.with_ctxt(macsp.ctxt());
make_argument(ecx, sp, ecx.expr_addr_of(sp, arg), ty) make_argument(ecx, sp, ecx.expr_addr_of(sp, arg), ty)
@ -239,7 +237,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
// ] // ]
// } // }
let args_ident = Ident::new(sym::args, macsp); let args_ident = Ident::new(sym::args, macsp);
let args = args let args = argmap
.iter() .iter()
.map(|&(arg_index, ty)| { .map(|&(arg_index, ty)| {
if let Some((arg, _)) = fmt.arguments.get(arg_index) { if let Some((arg, _)) = fmt.arguments.get(arg_index) {
@ -270,8 +268,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
.map(|(arg, _)| ecx.expr_addr_of(arg.span.with_ctxt(macsp.ctxt()), arg)) .map(|(arg, _)| ecx.expr_addr_of(arg.span.with_ctxt(macsp.ctxt()), arg))
.collect(), .collect(),
), ),
[ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))] vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))],
.into(),
), ),
) )
}; };
@ -289,7 +286,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]), ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]),
vec![ vec![
lit_pieces, lit_pieces,
args_expr, args,
format_options, format_options,
ecx.expr_block(P(ast::Block { ecx.expr_block(P(ast::Block {
stmts: vec![ecx.stmt_expr(ecx.expr_call_global( stmts: vec![ecx.stmt_expr(ecx.expr_call_global(
@ -314,7 +311,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<as
ecx.expr_call_global( ecx.expr_call_global(
macsp, macsp,
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]), ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]),
vec![lit_pieces, args_expr], vec![lit_pieces, args],
) )
} }
} }