2022-08-08 14:17:08 +00:00
|
|
|
use super::*;
|
|
|
|
use rustc_ast as ast;
|
|
|
|
use rustc_ast::visit::{self, Visitor};
|
|
|
|
use rustc_ast::{BlockCheckMode, UnsafeSource};
|
|
|
|
use rustc_data_structures::fx::FxIndexSet;
|
|
|
|
use rustc_span::{sym, symbol::kw};
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
|
|
|
enum ArgumentType {
|
|
|
|
Format(FormatTrait),
|
|
|
|
Usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P<ast::Expr>, ty: ArgumentType) -> P<ast::Expr> {
|
|
|
|
// Generate:
|
|
|
|
// ::core::fmt::ArgumentV1::new_…(arg)
|
|
|
|
use ArgumentType::*;
|
|
|
|
use FormatTrait::*;
|
|
|
|
ecx.expr_call_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[
|
|
|
|
sym::fmt,
|
|
|
|
sym::ArgumentV1,
|
|
|
|
match ty {
|
|
|
|
Format(Display) => sym::new_display,
|
|
|
|
Format(Debug) => sym::new_debug,
|
|
|
|
Format(LowerExp) => sym::new_lower_exp,
|
|
|
|
Format(UpperExp) => sym::new_upper_exp,
|
|
|
|
Format(Octal) => sym::new_octal,
|
|
|
|
Format(Pointer) => sym::new_pointer,
|
|
|
|
Format(Binary) => sym::new_binary,
|
|
|
|
Format(LowerHex) => sym::new_lower_hex,
|
|
|
|
Format(UpperHex) => sym::new_upper_hex,
|
|
|
|
Usize => sym::from_usize,
|
|
|
|
},
|
|
|
|
]),
|
|
|
|
vec![arg],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_count(
|
|
|
|
ecx: &ExtCtxt<'_>,
|
|
|
|
sp: Span,
|
|
|
|
count: &Option<FormatCount>,
|
|
|
|
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
|
|
|
) -> P<ast::Expr> {
|
|
|
|
// Generate:
|
|
|
|
// ::core::fmt::rt::v1::Count::…(…)
|
|
|
|
match count {
|
|
|
|
Some(FormatCount::Literal(n)) => ecx.expr_call_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]),
|
|
|
|
vec![ecx.expr_usize(sp, *n)],
|
|
|
|
),
|
|
|
|
Some(FormatCount::Argument(arg)) => {
|
|
|
|
if let Ok(arg_index) = arg.index {
|
|
|
|
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
|
|
|
|
ecx.expr_call_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]),
|
|
|
|
vec![ecx.expr_usize(sp, i)],
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
DummyResult::raw_expr(sp, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => ecx.expr_path(ecx.path_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_format_spec(
|
|
|
|
ecx: &ExtCtxt<'_>,
|
|
|
|
sp: Span,
|
|
|
|
placeholder: &FormatPlaceholder,
|
|
|
|
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
|
|
|
) -> P<ast::Expr> {
|
|
|
|
// Generate:
|
|
|
|
// ::core::fmt::rt::v1::Argument {
|
|
|
|
// position: 0usize,
|
|
|
|
// format: ::core::fmt::rt::v1::FormatSpec {
|
|
|
|
// fill: ' ',
|
|
|
|
// align: ::core::fmt::rt::v1::Alignment::Unknown,
|
|
|
|
// flags: 0u32,
|
|
|
|
// precision: ::core::fmt::rt::v1::Count::Implied,
|
|
|
|
// width: ::core::fmt::rt::v1::Count::Implied,
|
|
|
|
// },
|
|
|
|
// }
|
|
|
|
let position = match placeholder.argument.index {
|
|
|
|
Ok(arg_index) => {
|
|
|
|
let (i, _) =
|
|
|
|
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
|
|
|
|
ecx.expr_usize(sp, i)
|
|
|
|
}
|
|
|
|
Err(_) => DummyResult::raw_expr(sp, true),
|
|
|
|
};
|
|
|
|
let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
|
|
|
|
let align = ecx.expr_path(ecx.path_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[
|
|
|
|
sym::fmt,
|
|
|
|
sym::rt,
|
|
|
|
sym::v1,
|
|
|
|
sym::Alignment,
|
|
|
|
match placeholder.format_options.alignment {
|
|
|
|
Some(FormatAlignment::Left) => sym::Left,
|
|
|
|
Some(FormatAlignment::Right) => sym::Right,
|
|
|
|
Some(FormatAlignment::Center) => sym::Center,
|
|
|
|
None => sym::Unknown,
|
|
|
|
},
|
|
|
|
]),
|
|
|
|
));
|
|
|
|
let flags = ecx.expr_u32(sp, placeholder.format_options.flags);
|
|
|
|
let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap);
|
|
|
|
let width = make_count(ecx, sp, &placeholder.format_options.width, argmap);
|
|
|
|
ecx.expr_struct(
|
|
|
|
sp,
|
|
|
|
ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])),
|
|
|
|
vec![
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::position, sp), position),
|
|
|
|
ecx.field_imm(
|
|
|
|
sp,
|
|
|
|
Ident::new(sym::format, sp),
|
|
|
|
ecx.expr_struct(
|
|
|
|
sp,
|
|
|
|
ecx.path_global(
|
|
|
|
sp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]),
|
|
|
|
),
|
|
|
|
vec![
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::fill, sp), fill),
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::align, sp), align),
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::flags, sp), flags),
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::precision, sp), prec),
|
|
|
|
ecx.field_imm(sp, Ident::new(sym::width, sp), width),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> {
|
|
|
|
let macsp = ecx.with_def_site_ctxt(ecx.call_site());
|
|
|
|
|
|
|
|
let lit_pieces = ecx.expr_array_ref(
|
|
|
|
fmt.span,
|
|
|
|
fmt.template
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, piece)| match piece {
|
|
|
|
&FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)),
|
|
|
|
&FormatArgsPiece::Placeholder(_) => {
|
|
|
|
// Inject empty string before placeholders when not already preceded by a literal piece.
|
|
|
|
if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
|
|
|
|
Some(ecx.expr_str(fmt.span, kw::Empty))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
);
|
|
|
|
|
2022-08-27 15:51:28 +00:00
|
|
|
// Whether we'll use the `Arguments::new_v1_formatted` form (true),
|
|
|
|
// or the `Arguments::new_v1` form (false).
|
|
|
|
let mut use_format_options = false;
|
2022-08-08 14:17:08 +00:00
|
|
|
|
2022-08-27 15:51:28 +00:00
|
|
|
// Create a list of all _unique_ (argument, format trait) combinations.
|
|
|
|
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
|
|
|
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(|| {
|
2022-08-08 14:17:08 +00:00
|
|
|
// Generate:
|
|
|
|
// &[format_spec_0, format_spec_1, format_spec_2]
|
2022-08-27 15:51:28 +00:00
|
|
|
ecx.expr_array_ref(
|
2022-08-08 14:17:08 +00:00
|
|
|
macsp,
|
|
|
|
fmt.template
|
|
|
|
.iter()
|
|
|
|
.filter_map(|piece| {
|
|
|
|
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
|
|
|
|
Some(make_format_spec(ecx, macsp, placeholder, &mut argmap))
|
|
|
|
})
|
|
|
|
.collect(),
|
2022-08-27 15:51:28 +00:00
|
|
|
)
|
|
|
|
});
|
2022-08-08 14:17:08 +00:00
|
|
|
|
|
|
|
// If the args array contains exactly all the original arguments once,
|
|
|
|
// 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,
|
|
|
|
// we don't do this, because an ArgumentV1 cannot be kept across yield points.
|
2022-08-27 15:51:28 +00:00
|
|
|
let use_simple_array = argmap.len() == fmt.arguments.len()
|
|
|
|
&& argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
|
2022-08-08 14:17:08 +00:00
|
|
|
&& fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg));
|
|
|
|
|
2022-08-27 15:51:28 +00:00
|
|
|
let args = if use_simple_array {
|
2022-08-08 14:17:08 +00:00
|
|
|
// Generate:
|
|
|
|
// &[
|
|
|
|
// ::core::fmt::ArgumentV1::new_display(&arg0),
|
|
|
|
// ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
|
|
|
|
// ::core::fmt::ArgumentV1::new_debug(&arg2),
|
|
|
|
// ]
|
|
|
|
ecx.expr_array_ref(
|
|
|
|
macsp,
|
|
|
|
fmt.arguments
|
|
|
|
.into_iter()
|
2022-08-27 15:51:28 +00:00
|
|
|
.zip(argmap)
|
2022-08-08 14:17:08 +00:00
|
|
|
.map(|((arg, _), (_, ty))| {
|
|
|
|
let sp = arg.span.with_ctxt(macsp.ctxt());
|
|
|
|
make_argument(ecx, sp, ecx.expr_addr_of(sp, arg), ty)
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// Generate:
|
|
|
|
// match (&arg0, &arg1, &arg2) {
|
|
|
|
// args => &[
|
|
|
|
// ::core::fmt::ArgumentV1::new_display(args.0),
|
|
|
|
// ::core::fmt::ArgumentV1::new_lower_hex(args.1),
|
|
|
|
// ::core::fmt::ArgumentV1::new_debug(args.0),
|
|
|
|
// ]
|
|
|
|
// }
|
|
|
|
let args_ident = Ident::new(sym::args, macsp);
|
2022-08-27 15:51:28 +00:00
|
|
|
let args = argmap
|
2022-08-08 14:17:08 +00:00
|
|
|
.iter()
|
|
|
|
.map(|&(arg_index, ty)| {
|
|
|
|
if let Some((arg, _)) = fmt.arguments.get(arg_index) {
|
|
|
|
let sp = arg.span.with_ctxt(macsp.ctxt());
|
|
|
|
make_argument(
|
|
|
|
ecx,
|
|
|
|
sp,
|
|
|
|
ecx.expr_field(
|
|
|
|
sp,
|
|
|
|
ecx.expr_ident(macsp, args_ident),
|
|
|
|
Ident::new(sym::integer(arg_index), macsp),
|
|
|
|
),
|
|
|
|
ty,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
DummyResult::raw_expr(macsp, true)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
ecx.expr_addr_of(
|
|
|
|
macsp,
|
|
|
|
ecx.expr_match(
|
|
|
|
macsp,
|
|
|
|
ecx.expr_tuple(
|
|
|
|
macsp,
|
|
|
|
fmt.arguments
|
|
|
|
.into_iter()
|
|
|
|
.map(|(arg, _)| ecx.expr_addr_of(arg.span.with_ctxt(macsp.ctxt()), arg))
|
|
|
|
.collect(),
|
|
|
|
),
|
2022-08-27 15:51:28 +00:00
|
|
|
vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))],
|
2022-08-08 14:17:08 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(format_options) = format_options {
|
|
|
|
// Generate:
|
|
|
|
// ::core::fmt::Arguments::new_v1_formatted(
|
|
|
|
// lit_pieces,
|
|
|
|
// args,
|
|
|
|
// format_options,
|
|
|
|
// unsafe { ::core::fmt::UnsafeArg::new() }
|
|
|
|
// )
|
|
|
|
ecx.expr_call_global(
|
|
|
|
macsp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]),
|
|
|
|
vec![
|
|
|
|
lit_pieces,
|
2022-08-27 15:51:28 +00:00
|
|
|
args,
|
2022-08-08 14:17:08 +00:00
|
|
|
format_options,
|
|
|
|
ecx.expr_block(P(ast::Block {
|
|
|
|
stmts: vec![ecx.stmt_expr(ecx.expr_call_global(
|
|
|
|
macsp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]),
|
|
|
|
Vec::new(),
|
|
|
|
))],
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated),
|
|
|
|
span: macsp,
|
|
|
|
tokens: None,
|
|
|
|
could_be_bare_literal: false,
|
|
|
|
})),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// Generate:
|
|
|
|
// ::core::fmt::Arguments::new_v1(
|
|
|
|
// lit_pieces,
|
|
|
|
// args,
|
|
|
|
// )
|
|
|
|
ecx.expr_call_global(
|
|
|
|
macsp,
|
|
|
|
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]),
|
2022-08-27 15:51:28 +00:00
|
|
|
vec![lit_pieces, args],
|
2022-08-08 14:17:08 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn may_contain_yield_point(e: &ast::Expr) -> bool {
|
|
|
|
struct MayContainYieldPoint(bool);
|
|
|
|
|
|
|
|
impl Visitor<'_> for MayContainYieldPoint {
|
|
|
|
fn visit_expr(&mut self, e: &ast::Expr) {
|
|
|
|
if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
|
|
|
|
self.0 = true;
|
|
|
|
} else {
|
|
|
|
visit::walk_expr(self, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_mac_call(&mut self, _: &ast::MacCall) {
|
|
|
|
self.0 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_attribute(&mut self, _: &ast::Attribute) {
|
|
|
|
// Conservatively assume this may be a proc macro attribute in
|
|
|
|
// expression position.
|
|
|
|
self.0 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_item(&mut self, _: &ast::Item) {
|
|
|
|
// Do not recurse into nested items.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut visitor = MayContainYieldPoint(false);
|
|
|
|
visitor.visit_expr(e);
|
|
|
|
visitor.0
|
|
|
|
}
|