1use base_db::FxIndexSet;
4use hir_expand::name::{AsName, Name};
5use intern::{Symbol, sym};
6use span::SyntaxContext;
7use syntax::{
8 AstPtr, AstToken as _,
9 ast::{self, HasName},
10};
11
12use crate::{
13 expr_store::{HygieneId, lower::ExprCollector, path::Path},
14 hir::{
15 Array, BindingAnnotation, Expr, ExprId, Literal, Pat, Statement, Unsafe,
16 format_args::{
17 self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
18 FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatSign, FormatTrait,
19 },
20 },
21 lang_item::LangItemTarget,
22 type_ref::{Mutability, Rawness},
23};
24
25impl<'db> ExprCollector<'db> {
26 pub(super) fn collect_format_args(
27 &mut self,
28 f: ast::FormatArgsExpr,
29 syntax_ptr: AstPtr<ast::Expr>,
30 ) -> ExprId {
31 let mut args = FormatArgumentsCollector::default();
32 f.args().for_each(|arg| {
33 let expr = arg.expr();
34 args.add(FormatArgument {
35 kind: match arg.name() {
36 Some(name) => FormatArgumentKind::Named(name.as_name()),
37 None => FormatArgumentKind::Normal,
38 },
39 syntax: expr.as_ref().map(AstPtr::new),
40 expr: self.collect_expr_opt(expr),
41 });
42 });
43 let template = f.template();
44 let fmt_snippet = template.as_ref().and_then(|it| match it {
45 ast::Expr::Literal(literal) => match literal.kind() {
46 ast::LiteralKind::String(s) => Some(s.text().to_owned()),
47 _ => None,
48 },
49 _ => None,
50 });
51 let mut mappings = vec![];
52 let (fmt, hygiene) = match template.and_then(|template| {
53 self.expand_macros_to_string(template.clone()).map(|it| (it, template))
54 }) {
55 Some(((s, is_direct_literal), template)) => {
56 let call_ctx = SyntaxContext::root(self.def_map.edition());
57 let hygiene = self.hygiene_id_for(s.syntax().text_range());
58 let template_ptr = AstPtr::new(&template);
59 let fmt = format_args::parse(
60 &s,
61 template_ptr,
62 fmt_snippet,
63 args,
64 is_direct_literal,
65 |name, range| {
66 let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
67 if let Some(range) = range {
68 self.store
69 .template_map
70 .get_or_insert_with(Default::default)
71 .implicit_capture_to_source
72 .insert(expr_id, self.expander.in_file((template_ptr, range)));
73 }
74 if !hygiene.is_root() {
75 self.store.ident_hygiene.insert(expr_id.into(), hygiene);
76 }
77 expr_id
78 },
79 |name, span| {
80 if let Some(span) = span {
81 mappings.push((span, name))
82 }
83 },
84 call_ctx,
85 );
86 (fmt, hygiene)
87 }
88 None => (
89 FormatArgs {
90 template: Default::default(),
91 arguments: args.finish(),
92 orphans: Default::default(),
93 },
94 HygieneId::ROOT,
95 ),
96 };
97
98 let idx = self.collect_format_args_impl(syntax_ptr, fmt);
99
100 self.store
101 .template_map
102 .get_or_insert_with(Default::default)
103 .format_args_to_captures
104 .insert(idx, (hygiene, mappings));
105 idx
106 }
107
108 fn collect_format_args_impl(
111 &mut self,
112 syntax_ptr: AstPtr<ast::Expr>,
113 fmt: FormatArgs,
114 ) -> ExprId {
115 let lang_items = self.lang_items();
116
117 let mut argmap = FxIndexSet::default();
123
124 let mut incomplete_lit = String::new();
125
126 let mut implicit_arg_index = 0;
127
128 let mut bytecode = Vec::new();
129
130 let template = if fmt.template.is_empty() {
131 &[FormatArgsPiece::Literal(sym::__empty)][..]
134 } else {
135 &fmt.template[..]
136 };
137
138 for (i, piece) in template.iter().enumerate() {
141 match piece {
142 FormatArgsPiece::Literal(sym) => {
143 if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {
145 incomplete_lit.push_str(sym.as_str());
146 continue;
147 }
148 let mut s = if incomplete_lit.is_empty() {
149 sym.as_str()
150 } else {
151 incomplete_lit.push_str(sym.as_str());
152 &incomplete_lit
153 };
154
155 if i + 1 == template.len() && bytecode.is_empty() {
160 let from_str = self.ty_rel_lang_path_desugared_expr(
163 lang_items.FormatArguments,
164 sym::from_str,
165 );
166 let sym =
167 if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) };
168 let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym)));
169 let from_str = self.alloc_expr(
170 Expr::Call { callee: from_str, args: Box::new([s]) },
171 syntax_ptr,
172 );
173 return if !fmt.arguments.arguments.is_empty() {
174 self.alloc_expr(
177 Expr::Block {
178 id: None,
179 statements: fmt
180 .arguments
181 .arguments
182 .iter()
183 .map(|arg| Statement::Expr {
184 expr: arg.expr,
185 has_semi: true,
186 })
187 .collect(),
188 tail: Some(from_str),
189 label: None,
190 unsafe_: Unsafe::No,
191 },
192 syntax_ptr,
193 )
194 } else {
195 from_str
196 };
197 }
198
199 while !s.is_empty() {
201 let len = s.floor_char_boundary(usize::from(u16::MAX));
202 if len < 0x80 {
203 bytecode.push(len as u8);
204 } else {
205 bytecode.push(0x80);
206 bytecode.extend_from_slice(&(len as u16).to_le_bytes());
207 }
208 bytecode.extend(&s.as_bytes()[..len]);
209 s = &s[len..];
210 }
211
212 incomplete_lit.clear();
213 }
214 FormatArgsPiece::Placeholder(p) => {
215 let i = bytecode.len();
217 bytecode.push(0xC0);
218
219 let position = match &p.argument.index {
220 &Ok(it) => it,
221 Err(_) => usize::MAX,
222 };
223 let position = argmap
224 .insert_full((position, ArgumentType::Format(p.format_trait)))
225 .0 as u64;
226
227 let o = &p.format_options;
229 let align = match o.alignment {
230 Some(FormatAlignment::Left) => 0,
231 Some(FormatAlignment::Right) => 1,
232 Some(FormatAlignment::Center) => 2,
233 None => 3,
234 };
235 let default_flags = 0x6000_0020;
236 let flags: u32 = o.fill.unwrap_or(' ') as u32
237 | ((o.sign == Some(FormatSign::Plus)) as u32) << 21
238 | ((o.sign == Some(FormatSign::Minus)) as u32) << 22
239 | (o.alternate as u32) << 23
240 | (o.zero_pad as u32) << 24
241 | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
242 | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
243 | (o.width.is_some() as u32) << 27
244 | (o.precision.is_some() as u32) << 28
245 | align << 29;
246 if flags != default_flags {
247 bytecode[i] |= 1;
248 bytecode.extend_from_slice(&flags.to_le_bytes());
249 if let Some(val) = &o.width {
250 let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
251 if indirect || val != 0 {
253 bytecode[i] |= 1 << 1 | (indirect as u8) << 4;
254 bytecode.extend_from_slice(&val.to_le_bytes());
255 }
256 }
257 if let Some(val) = &o.precision {
258 let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
259 if indirect || val != 0 {
261 bytecode[i] |= 1 << 2 | (indirect as u8) << 5;
262 bytecode.extend_from_slice(&val.to_le_bytes());
263 }
264 }
265 }
266 if implicit_arg_index != position {
267 bytecode[i] |= 1 << 3;
268 bytecode.extend_from_slice(&(position as u16).to_le_bytes());
269 }
270 implicit_arg_index = position + 1;
271 }
272 }
273 }
274
275 assert!(incomplete_lit.is_empty());
276
277 bytecode.push(0);
279
280 if argmap.len() > u16::MAX as usize {
282 }
285
286 let arguments = &fmt.arguments.arguments[..];
287
288 let (mut statements, args) = if arguments.is_empty() {
289 (
292 Vec::new(),
293 self.alloc_expr_desugared(Expr::Array(Array::ElementList {
294 elements: Box::new([]),
295 })),
296 )
297 } else {
298 let args_name = self.generate_new_name();
301 let args_path = Path::from(args_name.clone());
302 let args_binding = self.alloc_binding(
303 args_name.clone(),
304 BindingAnnotation::Unannotated,
305 HygieneId::ROOT,
306 );
307 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
308 self.add_definition_to_binding(args_binding, args_pat);
309 let elements = arguments
310 .iter()
311 .map(|arg| {
312 self.alloc_expr_desugared(Expr::Ref {
313 expr: arg.expr,
314 rawness: Rawness::Ref,
315 mutability: Mutability::Shared,
316 })
317 })
318 .collect();
319 let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });
320 let let_statement_1 = Statement::Let {
322 pat: args_pat,
323 type_ref: None,
324 initializer: Some(args_tuple),
325 else_branch: None,
326 };
327
328 let args = argmap
336 .iter()
337 .map(|&(arg_index, ty)| {
338 let args_ident_expr = self.alloc_expr_desugared(Expr::Path(args_path.clone()));
339 let arg = self.alloc_expr_desugared(Expr::Field {
340 expr: args_ident_expr,
341 name: Name::new_tuple_field(arg_index),
342 });
343 let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
344 self.make_argument(arg_ptr, arg, ty)
345 })
346 .collect();
347 let args =
348 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
349 let args_binding =
350 self.alloc_binding(args_name, BindingAnnotation::Unannotated, HygieneId::ROOT);
351 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
352 self.add_definition_to_binding(args_binding, args_pat);
353 let let_statement_2 = Statement::Let {
355 pat: args_pat,
356 type_ref: None,
357 initializer: Some(args),
358 else_branch: None,
359 };
360 (
361 vec![let_statement_1, let_statement_2],
362 self.alloc_expr_desugared(Expr::Path(args_path)),
363 )
364 };
365
366 let template = self
371 .alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice())));
372 let call = {
373 let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new);
374 let args = self.alloc_expr_desugared(Expr::Ref {
375 expr: args,
376 rawness: Rawness::Ref,
377 mutability: Mutability::Shared,
378 });
379 self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) })
380 };
381 let call = self.alloc_expr(
382 Expr::Block {
383 id: None,
384 statements: Box::new([]),
385 tail: Some(call),
386 label: None,
387 unsafe_: Unsafe::Yes,
388 },
389 syntax_ptr,
390 );
391
392 statements
397 .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));
398
399 if !statements.is_empty() {
400 self.alloc_expr(
407 Expr::Block {
408 id: None,
409 statements: statements.into_boxed_slice(),
410 tail: Some(call),
411 label: None,
412 unsafe_: Unsafe::No,
413 },
414 syntax_ptr,
415 )
416 } else {
417 call
418 }
419 }
420
421 fn make_count_after_1_93_0(
425 &self,
426 count: &FormatCount,
427 argmap: &mut FxIndexSet<(usize, ArgumentType)>,
428 ) -> (bool, u16) {
429 match count {
430 FormatCount::Literal(n) => (false, *n),
431 FormatCount::Argument(arg) => {
432 let index = match &arg.index {
433 &Ok(it) => it,
434 Err(_) => usize::MAX,
435 };
436 (true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16)
437 }
438 }
439 }
440
441 fn make_argument(
449 &mut self,
450 arg_ptr: Option<AstPtr<ast::Expr>>,
451 arg: ExprId,
452 ty: ArgumentType,
453 ) -> ExprId {
454 use ArgumentType::*;
455 use FormatTrait::*;
456
457 let new_fn = self.ty_rel_lang_path(
458 self.lang_items().FormatArgument,
459 match ty {
460 Format(Display) => sym::new_display,
461 Format(Debug) => sym::new_debug,
462 Format(LowerExp) => sym::new_lower_exp,
463 Format(UpperExp) => sym::new_upper_exp,
464 Format(Octal) => sym::new_octal,
465 Format(Pointer) => sym::new_pointer,
466 Format(Binary) => sym::new_binary,
467 Format(LowerHex) => sym::new_lower_hex,
468 Format(UpperHex) => sym::new_upper_hex,
469 Usize => sym::from_usize,
470 },
471 );
472 let new_fn = match new_fn {
473 Some(new_fn) => {
474 let new_fn = self.store.exprs.alloc(Expr::Path(new_fn));
475 if let Some(arg_ptr) = arg_ptr {
476 self.store
481 .expr_map_back
482 .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left()));
483 }
484 new_fn
485 }
486 None => self.missing_expr(),
487 };
488 self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
489 }
490
491 fn ty_rel_lang_path_desugared_expr(
492 &mut self,
493 lang: Option<impl Into<LangItemTarget>>,
494 relative_name: Symbol,
495 ) -> ExprId {
496 self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name))
497 }
498}
499
500#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
501enum ArgumentType {
502 Format(FormatTrait),
503 Usize,
504}