Skip to main content

hir_def/expr_store/lower/
format_args.rs

1//! Lowering of `format_args!()`.
2
3use 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    // This is in separate functions because historically, changes in format_args lowering have forced us to change this
109    // function but not its caller, and for some time support both versions.
110    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        // Create a list of all _unique_ (argument, format trait) combinations.
118        // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
119        //
120        // We use usize::MAX for arguments that don't exist, because that can never be a valid index
121        // into the arguments array.
122        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            // Treat empty templates as a single literal piece (with an empty string),
132            // so we produce `from_str("")` for those.
133            &[FormatArgsPiece::Literal(sym::__empty)][..]
134        } else {
135            &fmt.template[..]
136        };
137
138        // See library/core/src/fmt/mod.rs for the format string encoding format.
139
140        for (i, piece) in template.iter().enumerate() {
141            match piece {
142                FormatArgsPiece::Literal(sym) => {
143                    // Coalesce adjacent literal pieces.
144                    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 this is the last piece and was the only piece, that means
156                    // there are no placeholders and the entire format string is just a literal.
157                    //
158                    // In that case, we can just use `from_str`.
159                    if i + 1 == template.len() && bytecode.is_empty() {
160                        // Generate:
161                        //     <core::fmt::Arguments>::from_str("meow")
162                        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                            // With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments`
175                            // to be non-empty when reaching this code path.
176                            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                    // Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries.
200                    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                    // Push the start byte and remember its index so we can set the option bits later.
216                    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                    // This needs to match the constants in library/core/src/fmt/mod.rs.
228                    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                            // Only encode if nonzero; zero is the default.
252                            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                            // Only encode if nonzero; zero is the default.
260                            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        // Zero terminator.
278        bytecode.push(0);
279
280        // Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before.
281        if argmap.len() > u16::MAX as usize {
282            // FIXME: Emit an error.
283            // ctx.dcx().span_err(macsp, "too many format arguments");
284        }
285
286        let arguments = &fmt.arguments.arguments[..];
287
288        let (mut statements, args) = if arguments.is_empty() {
289            // Generate:
290            //     []
291            (
292                Vec::new(),
293                self.alloc_expr_desugared(Expr::Array(Array::ElementList {
294                    elements: Box::new([]),
295                })),
296            )
297        } else {
298            // Generate:
299            //     super let args = (&arg0, &arg1, &…);
300            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            // FIXME: Make this a `super let` when we have this statement.
321            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            // Generate:
329            //     super let args = [
330            //         <core::fmt::Argument>::new_display(args.0),
331            //         <core::fmt::Argument>::new_lower_hex(args.1),
332            //         <core::fmt::Argument>::new_debug(args.0),
333            //         …
334            //     ];
335            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            // FIXME: Make this a `super let` when we have this statement.
354            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        // Generate:
367        //     unsafe {
368        //         <core::fmt::Arguments>::new(b"…", &args)
369        //     }
370        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        // We collect the unused expressions here so that we still infer them instead of
393        // dropping them out of the expression tree. We cannot store them in the `Unsafe`
394        // block because then unsafe blocks within them will get a false "unused unsafe"
395        // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
396        statements
397            .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));
398
399        if !statements.is_empty() {
400            // Generate:
401            //     {
402            //         super let …
403            //         super let …
404            //         <core::fmt::Arguments>::new(…)
405            //     }
406            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    /// Get the value for a `width` or `precision` field.
422    ///
423    /// Returns the value and whether it is indirect (an indexed argument) or not.
424    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    /// Generate a hir expression representing an argument to a format_args invocation.
442    ///
443    /// Generates:
444    ///
445    /// ```text
446    ///     <core::fmt::Argument>::new_…(arg)
447    /// ```
448    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                    // Trait errors (the argument does not implement the expected fmt trait) will show
477                    // on this path, so to not end up with synthetic syntax we insert this mapping. We
478                    // don't want to insert the other way's mapping in order to not override the source
479                    // for the argument.
480                    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}