3 use rustc_ast::visit::{self, Visitor};
4 use rustc_ast::{BlockCheckMode, UnsafeSource};
5 use rustc_data_structures::fx::FxIndexSet;
6 use rustc_span::{sym, symbol::kw};
8 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
14 fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P<ast::Expr>, ty: ArgumentType) -> P<ast::Expr> {
16 // ::core::fmt::ArgumentV1::new_…(arg)
25 Format(Display) => sym::new_display,
26 Format(Debug) => sym::new_debug,
27 Format(LowerExp) => sym::new_lower_exp,
28 Format(UpperExp) => sym::new_upper_exp,
29 Format(Octal) => sym::new_octal,
30 Format(Pointer) => sym::new_pointer,
31 Format(Binary) => sym::new_binary,
32 Format(LowerHex) => sym::new_lower_hex,
33 Format(UpperHex) => sym::new_upper_hex,
34 Usize => sym::from_usize,
44 count: &Option<FormatCount>,
45 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
48 // ::core::fmt::rt::v1::Count::…(…)
50 Some(FormatCount::Literal(n)) => ecx.expr_call_global(
52 ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]),
53 vec![ecx.expr_usize(sp, *n)],
55 Some(FormatCount::Argument(arg)) => {
56 if let Ok(arg_index) = arg.index {
57 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
60 ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]),
61 vec![ecx.expr_usize(sp, i)],
64 DummyResult::raw_expr(sp, true)
67 None => ecx.expr_path(ecx.path_global(
69 ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]),
77 placeholder: &FormatPlaceholder,
78 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
81 // ::core::fmt::rt::v1::Argument {
83 // format: ::core::fmt::rt::v1::FormatSpec {
85 // align: ::core::fmt::rt::v1::Alignment::Unknown,
87 // precision: ::core::fmt::rt::v1::Count::Implied,
88 // width: ::core::fmt::rt::v1::Count::Implied,
91 let position = match placeholder.argument.index {
94 argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
97 Err(_) => DummyResult::raw_expr(sp, true),
99 let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
100 let align = ecx.expr_path(ecx.path_global(
107 match placeholder.format_options.alignment {
108 Some(FormatAlignment::Left) => sym::Left,
109 Some(FormatAlignment::Right) => sym::Right,
110 Some(FormatAlignment::Center) => sym::Center,
111 None => sym::Unknown,
115 let flags = ecx.expr_u32(sp, placeholder.format_options.flags);
116 let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap);
117 let width = make_count(ecx, sp, &placeholder.format_options.width, argmap);
120 ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])),
122 ecx.field_imm(sp, Ident::new(sym::position, sp), position),
125 Ident::new(sym::format, sp),
130 ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]),
133 ecx.field_imm(sp, Ident::new(sym::fill, sp), fill),
134 ecx.field_imm(sp, Ident::new(sym::align, sp), align),
135 ecx.field_imm(sp, Ident::new(sym::flags, sp), flags),
136 ecx.field_imm(sp, Ident::new(sym::precision, sp), prec),
137 ecx.field_imm(sp, Ident::new(sym::width, sp), width),
145 pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> {
146 let macsp = ecx.with_def_site_ctxt(ecx.call_site());
148 let lit_pieces = ecx.expr_array_ref(
153 .filter_map(|(i, piece)| match piece {
154 &FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)),
155 &FormatArgsPiece::Placeholder(_) => {
156 // Inject empty string before placeholders when not already preceded by a literal piece.
157 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
158 Some(ecx.expr_str(fmt.span, kw::Empty))
167 // Whether we'll use the `Arguments::new_v1_formatted` form (true),
168 // or the `Arguments::new_v1` form (false).
169 let mut use_format_options = false;
171 // Create a list of all _unique_ (argument, format trait) combinations.
172 // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
173 let mut argmap = FxIndexSet::default();
174 for piece in &fmt.template {
175 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
176 if placeholder.format_options != Default::default() {
177 // Can't use basic form if there's any formatting options.
178 use_format_options = true;
180 if let Ok(index) = placeholder.argument.index {
181 if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
182 // Duplicate (argument, format trait) combination,
183 // which we'll only put once in the args array.
184 use_format_options = true;
189 let format_options = use_format_options.then(|| {
191 // &[format_spec_0, format_spec_1, format_spec_2]
196 .filter_map(|piece| {
197 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
198 Some(make_format_spec(ecx, macsp, placeholder, &mut argmap))
204 let arguments = fmt.arguments.into_vec();
206 // If the args array contains exactly all the original arguments once,
207 // in order, we can use a simple array instead of a `match` construction.
208 // However, if there's a yield point in any argument except the first one,
209 // we don't do this, because an ArgumentV1 cannot be kept across yield points.
210 let use_simple_array = argmap.len() == arguments.len()
211 && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
212 && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
214 let args = if use_simple_array {
217 // ::core::fmt::ArgumentV1::new_display(&arg0),
218 // ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
219 // ::core::fmt::ArgumentV1::new_debug(&arg2),
226 .map(|(arg, (_, ty))| {
227 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
228 make_argument(ecx, sp, ecx.expr_addr_of(sp, arg.expr), ty)
234 // match (&arg0, &arg1, &arg2) {
236 // ::core::fmt::ArgumentV1::new_display(args.0),
237 // ::core::fmt::ArgumentV1::new_lower_hex(args.1),
238 // ::core::fmt::ArgumentV1::new_debug(args.0),
241 let args_ident = Ident::new(sym::args, macsp);
244 .map(|&(arg_index, ty)| {
245 if let Some(arg) = arguments.get(arg_index) {
246 let sp = arg.expr.span.with_ctxt(macsp.ctxt());
252 ecx.expr_ident(macsp, args_ident),
253 Ident::new(sym::integer(arg_index), macsp),
258 DummyResult::raw_expr(macsp, true)
271 ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr)
275 vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))],
280 if let Some(format_options) = format_options {
282 // ::core::fmt::Arguments::new_v1_formatted(
286 // unsafe { ::core::fmt::UnsafeArg::new() }
288 ecx.expr_call_global(
290 ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]),
295 ecx.expr_block(P(ast::Block {
296 stmts: vec![ecx.stmt_expr(ecx.expr_call_global(
298 ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]),
301 id: ast::DUMMY_NODE_ID,
302 rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated),
305 could_be_bare_literal: false,
311 // ::core::fmt::Arguments::new_v1(
315 ecx.expr_call_global(
317 ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]),
318 vec![lit_pieces, args],
323 fn may_contain_yield_point(e: &ast::Expr) -> bool {
324 struct MayContainYieldPoint(bool);
326 impl Visitor<'_> for MayContainYieldPoint {
327 fn visit_expr(&mut self, e: &ast::Expr) {
328 if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
331 visit::walk_expr(self, e);
335 fn visit_mac_call(&mut self, _: &ast::MacCall) {
339 fn visit_attribute(&mut self, _: &ast::Attribute) {
340 // Conservatively assume this may be a proc macro attribute in
341 // expression position.
345 fn visit_item(&mut self, _: &ast::Item) {
346 // Do not recurse into nested items.
350 let mut visitor = MayContainYieldPoint(false);
351 visitor.visit_expr(e);