src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs RUST 1,041 lines View on github.com → Search inside
1//! Lowering of `format_args!()`.23use base_db::FxIndexSet;4use hir_expand::name::Name;5use intern::{Symbol, sym};6use span::SyntaxContext;7use syntax::{AstPtr, AstToken as _, ast};89use crate::{10    builtin_type::BuiltinUint,11    expr_store::{HygieneId, lower::ExprCollector, path::Path},12    hir::{13        Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, RecordSpread,14        Statement,15        format_args::{16            self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,17            FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,18            FormatPlaceholder, FormatSign, FormatTrait,19        },20    },21    lang_item::LangItemTarget,22    type_ref::{Mutability, Rawness},23};2425impl<'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.arg_name() {36                    Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),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.store69                                .template_map70                                .get_or_insert_with(Default::default)71                                .implicit_capture_to_source72                                .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_id78                    },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        };9798        let idx = if self.lang_items().FormatCount.is_none() {99            self.collect_format_args_after_1_93_0_impl(syntax_ptr, fmt)100        } else {101            self.collect_format_args_before_1_93_0_impl(syntax_ptr, fmt)102        };103104        self.store105            .template_map106            .get_or_insert_with(Default::default)107            .format_args_to_captures108            .insert(idx, (hygiene, mappings));109        idx110    }111112    fn collect_format_args_after_1_93_0_impl(113        &mut self,114        syntax_ptr: AstPtr<ast::Expr>,115        fmt: FormatArgs,116    ) -> ExprId {117        let lang_items = self.lang_items();118119        // Create a list of all _unique_ (argument, format trait) combinations.120        // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]121        //122        // We use usize::MAX for arguments that don't exist, because that can never be a valid index123        // into the arguments array.124        let mut argmap = FxIndexSet::default();125126        let mut incomplete_lit = String::new();127128        let mut implicit_arg_index = 0;129130        let mut bytecode = Vec::new();131132        let template = if fmt.template.is_empty() {133            // Treat empty templates as a single literal piece (with an empty string),134            // so we produce `from_str("")` for those.135            &[FormatArgsPiece::Literal(sym::__empty)][..]136        } else {137            &fmt.template[..]138        };139140        // See library/core/src/fmt/mod.rs for the format string encoding format.141142        for (i, piece) in template.iter().enumerate() {143            match piece {144                FormatArgsPiece::Literal(sym) => {145                    // Coalesce adjacent literal pieces.146                    if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {147                        incomplete_lit.push_str(sym.as_str());148                        continue;149                    }150                    let mut s = if incomplete_lit.is_empty() {151                        sym.as_str()152                    } else {153                        incomplete_lit.push_str(sym.as_str());154                        &incomplete_lit155                    };156157                    // If this is the last piece and was the only piece, that means158                    // there are no placeholders and the entire format string is just a literal.159                    //160                    // In that case, we can just use `from_str`.161                    if i + 1 == template.len() && bytecode.is_empty() {162                        // Generate:163                        //     <core::fmt::Arguments>::from_str("meow")164                        let from_str = self.ty_rel_lang_path_desugared_expr(165                            lang_items.FormatArguments,166                            sym::from_str,167                        );168                        let sym =169                            if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) };170                        let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym)));171                        let from_str = self.alloc_expr(172                            Expr::Call { callee: from_str, args: Box::new([s]) },173                            syntax_ptr,174                        );175                        return if !fmt.arguments.arguments.is_empty() {176                            // With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments`177                            // to be non-empty when reaching this code path.178                            self.alloc_expr(179                                Expr::Block {180                                    id: None,181                                    statements: fmt182                                        .arguments183                                        .arguments184                                        .iter()185                                        .map(|arg| Statement::Expr {186                                            expr: arg.expr,187                                            has_semi: true,188                                        })189                                        .collect(),190                                    tail: Some(from_str),191                                    label: None,192                                },193                                syntax_ptr,194                            )195                        } else {196                            from_str197                        };198                    }199200                    // Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries.201                    while !s.is_empty() {202                        let len = s.floor_char_boundary(usize::from(u16::MAX));203                        if len < 0x80 {204                            bytecode.push(len as u8);205                        } else {206                            bytecode.push(0x80);207                            bytecode.extend_from_slice(&(len as u16).to_le_bytes());208                        }209                        bytecode.extend(&s.as_bytes()[..len]);210                        s = &s[len..];211                    }212213                    incomplete_lit.clear();214                }215                FormatArgsPiece::Placeholder(p) => {216                    // Push the start byte and remember its index so we can set the option bits later.217                    let i = bytecode.len();218                    bytecode.push(0xC0);219220                    let position = match &p.argument.index {221                        &Ok(it) => it,222                        Err(_) => usize::MAX,223                    };224                    let position = argmap225                        .insert_full((position, ArgumentType::Format(p.format_trait)))226                        .0 as u64;227228                    // This needs to match the constants in library/core/src/fmt/mod.rs.229                    let o = &p.format_options;230                    let align = match o.alignment {231                        Some(FormatAlignment::Left) => 0,232                        Some(FormatAlignment::Right) => 1,233                        Some(FormatAlignment::Center) => 2,234                        None => 3,235                    };236                    let default_flags = 0x6000_0020;237                    let flags: u32 = o.fill.unwrap_or(' ') as u32238                        | ((o.sign == Some(FormatSign::Plus)) as u32) << 21239                        | ((o.sign == Some(FormatSign::Minus)) as u32) << 22240                        | (o.alternate as u32) << 23241                        | (o.zero_pad as u32) << 24242                        | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25243                        | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26244                        | (o.width.is_some() as u32) << 27245                        | (o.precision.is_some() as u32) << 28246                        | align << 29;247                    if flags != default_flags {248                        bytecode[i] |= 1;249                        bytecode.extend_from_slice(&flags.to_le_bytes());250                        if let Some(val) = &o.width {251                            let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);252                            // Only encode if nonzero; zero is the default.253                            if indirect || val != 0 {254                                bytecode[i] |= 1 << 1 | (indirect as u8) << 4;255                                bytecode.extend_from_slice(&val.to_le_bytes());256                            }257                        }258                        if let Some(val) = &o.precision {259                            let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);260                            // Only encode if nonzero; zero is the default.261                            if indirect || val != 0 {262                                bytecode[i] |= 1 << 2 | (indirect as u8) << 5;263                                bytecode.extend_from_slice(&val.to_le_bytes());264                            }265                        }266                    }267                    if implicit_arg_index != position {268                        bytecode[i] |= 1 << 3;269                        bytecode.extend_from_slice(&(position as u16).to_le_bytes());270                    }271                    implicit_arg_index = position + 1;272                }273            }274        }275276        assert!(incomplete_lit.is_empty());277278        // Zero terminator.279        bytecode.push(0);280281        // Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before.282        if argmap.len() > u16::MAX as usize {283            // FIXME: Emit an error.284            // ctx.dcx().span_err(macsp, "too many format arguments");285        }286287        let arguments = &fmt.arguments.arguments[..];288289        let (mut statements, args) = if arguments.is_empty() {290            // Generate:291            //     []292            (293                Vec::new(),294                self.alloc_expr_desugared(Expr::Array(Array::ElementList {295                    elements: Box::new([]),296                })),297            )298        } else {299            // Generate:300            //     super let args = (&arg0, &arg1, &…);301            let args_name = self.generate_new_name();302            let args_path = Path::from(args_name.clone());303            let args_binding = self.alloc_binding(304                args_name.clone(),305                BindingAnnotation::Unannotated,306                HygieneId::ROOT,307            );308            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });309            self.add_definition_to_binding(args_binding, args_pat);310            let elements = arguments311                .iter()312                .map(|arg| {313                    self.alloc_expr_desugared(Expr::Ref {314                        expr: arg.expr,315                        rawness: Rawness::Ref,316                        mutability: Mutability::Shared,317                    })318                })319                .collect();320            let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });321            // FIXME: Make this a `super let` when we have this statement.322            let let_statement_1 = Statement::Let {323                pat: args_pat,324                type_ref: None,325                initializer: Some(args_tuple),326                else_branch: None,327            };328329            // Generate:330            //     super let args = [331            //         <core::fmt::Argument>::new_display(args.0),332            //         <core::fmt::Argument>::new_lower_hex(args.1),333            //         <core::fmt::Argument>::new_debug(args.0),334            //         …335            //     ];336            let args = argmap337                .iter()338                .map(|&(arg_index, ty)| {339                    let args_ident_expr = self.alloc_expr_desugared(Expr::Path(args_path.clone()));340                    let arg = self.alloc_expr_desugared(Expr::Field {341                        expr: args_ident_expr,342                        name: Name::new_tuple_field(arg_index),343                    });344                    let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);345                    self.make_argument(arg_ptr, arg, ty)346                })347                .collect();348            let args =349                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));350            let args_binding =351                self.alloc_binding(args_name, BindingAnnotation::Unannotated, HygieneId::ROOT);352            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });353            self.add_definition_to_binding(args_binding, args_pat);354            // FIXME: Make this a `super let` when we have this statement.355            let let_statement_2 = Statement::Let {356                pat: args_pat,357                type_ref: None,358                initializer: Some(args),359                else_branch: None,360            };361            (362                vec![let_statement_1, let_statement_2],363                self.alloc_expr_desugared(Expr::Path(args_path)),364            )365        };366367        // Generate:368        //     unsafe {369        //         <core::fmt::Arguments>::new(b"…", &args)370        //     }371        let template = self372            .alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice())));373        let call = {374            let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new);375            let args = self.alloc_expr_desugared(Expr::Ref {376                expr: args,377                rawness: Rawness::Ref,378                mutability: Mutability::Shared,379            });380            self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) })381        };382        let call = self.alloc_expr(383            Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) },384            syntax_ptr,385        );386387        // We collect the unused expressions here so that we still infer them instead of388        // dropping them out of the expression tree. We cannot store them in the `Unsafe`389        // block because then unsafe blocks within them will get a false "unused unsafe"390        // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).391        statements392            .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));393394        if !statements.is_empty() {395            // Generate:396            //     {397            //         super let …398            //         super let …399            //         <core::fmt::Arguments>::new(…)400            //     }401            self.alloc_expr(402                Expr::Block {403                    id: None,404                    statements: statements.into_boxed_slice(),405                    tail: Some(call),406                    label: None,407                },408                syntax_ptr,409            )410        } else {411            call412        }413    }414415    /// Get the value for a `width` or `precision` field.416    ///417    /// Returns the value and whether it is indirect (an indexed argument) or not.418    fn make_count_after_1_93_0(419        &self,420        count: &FormatCount,421        argmap: &mut FxIndexSet<(usize, ArgumentType)>,422    ) -> (bool, u16) {423        match count {424            FormatCount::Literal(n) => (false, *n),425            FormatCount::Argument(arg) => {426                let index = match &arg.index {427                    &Ok(it) => it,428                    Err(_) => usize::MAX,429                };430                (true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16)431            }432        }433    }434435    fn collect_format_args_before_1_93_0_impl(436        &mut self,437        syntax_ptr: AstPtr<ast::Expr>,438        fmt: FormatArgs,439    ) -> ExprId {440        // Create a list of all _unique_ (argument, format trait) combinations.441        // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]442        let mut argmap = FxIndexSet::default();443        for piece in fmt.template.iter() {444            let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };445            if let Ok(index) = placeholder.argument.index {446                argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));447            }448        }449450        let lit_pieces = fmt451            .template452            .iter()453            .enumerate()454            .filter_map(|(i, piece)| {455                match piece {456                    FormatArgsPiece::Literal(s) => {457                        Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))))458                    }459                    &FormatArgsPiece::Placeholder(_) => {460                        // Inject empty string before placeholders when not already preceded by a literal piece.461                        if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))462                        {463                            Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(464                                Symbol::empty(),465                            ))))466                        } else {467                            None468                        }469                    }470                }471            })472            .collect();473        let lit_pieces =474            self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));475        let lit_pieces = self.alloc_expr_desugared(Expr::Ref {476            expr: lit_pieces,477            rawness: Rawness::Ref,478            mutability: Mutability::Shared,479        });480        let format_options = {481            // Generate:482            //     &[format_spec_0, format_spec_1, format_spec_2]483            let elements = fmt484                .template485                .iter()486                .filter_map(|piece| {487                    let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };488                    Some(self.make_format_spec(placeholder, &mut argmap))489                })490                .collect();491            let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));492            self.alloc_expr_desugared(Expr::Ref {493                expr: array,494                rawness: Rawness::Ref,495                mutability: Mutability::Shared,496            })497        };498499        // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists500        // but `format_unsafe_arg` does not501        let lang_items = self.lang_items();502        let fmt_args = lang_items.FormatArguments;503        let fmt_unsafe_arg = lang_items.FormatUnsafeArg;504        let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none();505506        if use_format_args_since_1_89_0 {507            self.collect_format_args_after_1_89_0_impl(508                syntax_ptr,509                fmt,510                argmap,511                lit_pieces,512                format_options,513            )514        } else {515            self.collect_format_args_before_1_89_0_impl(516                syntax_ptr,517                fmt,518                argmap,519                lit_pieces,520                format_options,521            )522        }523    }524525    /// `format_args!` expansion implementation for rustc versions < `1.89.0`526    fn collect_format_args_before_1_89_0_impl(527        &mut self,528        syntax_ptr: AstPtr<ast::Expr>,529        fmt: FormatArgs,530        argmap: FxIndexSet<(usize, ArgumentType)>,531        lit_pieces: ExprId,532        format_options: ExprId,533    ) -> ExprId {534        let arguments = &*fmt.arguments.arguments;535536        let args = if arguments.is_empty() {537            let expr = self538                .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));539            self.alloc_expr_desugared(Expr::Ref {540                expr,541                rawness: Rawness::Ref,542                mutability: Mutability::Shared,543            })544        } else {545            // Generate:546            //     &match (&arg0, &arg1, &…) {547            //         args => [548            //             <core::fmt::Argument>::new_display(args.0),549            //             <core::fmt::Argument>::new_lower_hex(args.1),550            //             <core::fmt::Argument>::new_debug(args.0),551            //             …552            //         ]553            //     }554            let args = argmap555                .iter()556                .map(|&(arg_index, ty)| {557                    let arg = self.alloc_expr_desugared(Expr::Ref {558                        expr: arguments[arg_index].expr,559                        rawness: Rawness::Ref,560                        mutability: Mutability::Shared,561                    });562                    let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);563                    self.make_argument(arg_ptr, arg, ty)564                })565                .collect();566            let array =567                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));568            self.alloc_expr_desugared(Expr::Ref {569                expr: array,570                rawness: Rawness::Ref,571                mutability: Mutability::Shared,572            })573        };574575        // Generate:576        //     <core::fmt::Arguments>::new_v1_formatted(577        //         lit_pieces,578        //         args,579        //         format_options,580        //         unsafe { ::core::fmt::UnsafeArg::new() }581        //     )582583        let lang_items = self.lang_items();584        let new_v1_formatted =585            self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new_v1_formatted);586        let unsafe_arg_new =587            self.ty_rel_lang_path_desugared_expr(lang_items.FormatUnsafeArg, sym::new);588        let unsafe_arg_new =589            self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });590        let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {591            id: None,592            statements: Box::new([]),593            tail: Some(unsafe_arg_new),594        });595        if !fmt.orphans.is_empty() {596            unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {597                id: None,598                // We collect the unused expressions here so that we still infer them instead of599                // dropping them out of the expression tree. We cannot store them in the `Unsafe`600                // block because then unsafe blocks within them will get a false "unused unsafe"601                // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).602                statements: fmt603                    .orphans604                    .into_iter()605                    .map(|expr| Statement::Expr { expr, has_semi: true })606                    .collect(),607                tail: Some(unsafe_arg_new),608                label: None,609            });610        }611612        self.alloc_expr(613            Expr::Call {614                callee: new_v1_formatted,615                args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),616            },617            syntax_ptr,618        )619    }620621    /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,622    /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)623    fn collect_format_args_after_1_89_0_impl(624        &mut self,625        syntax_ptr: AstPtr<ast::Expr>,626        fmt: FormatArgs,627        argmap: FxIndexSet<(usize, ArgumentType)>,628        lit_pieces: ExprId,629        format_options: ExprId,630    ) -> ExprId {631        let arguments = &*fmt.arguments.arguments;632633        let (let_stmts, args) = if arguments.is_empty() {634            (635                // Generate:636                //     []637                vec![],638                self.alloc_expr_desugared(Expr::Array(Array::ElementList {639                    elements: Box::default(),640                })),641            )642        } else if argmap.len() == 1 && arguments.len() == 1 {643            // Only one argument, so we don't need to make the `args` tuple.644            //645            // Generate:646            //     super let args = [<core::fmt::Arguments>::new_display(&arg)];647            let args = argmap648                .iter()649                .map(|&(arg_index, ty)| {650                    let ref_arg = self.alloc_expr_desugared(Expr::Ref {651                        expr: arguments[arg_index].expr,652                        rawness: Rawness::Ref,653                        mutability: Mutability::Shared,654                    });655                    let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);656                    self.make_argument(arg_ptr, ref_arg, ty)657                })658                .collect();659            let args =660                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));661            let args_name = self.generate_new_name();662            let args_binding = self.alloc_binding(663                args_name.clone(),664                BindingAnnotation::Unannotated,665                HygieneId::ROOT,666            );667            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });668            self.add_definition_to_binding(args_binding, args_pat);669            // TODO: We don't have `super let` yet.670            let let_stmt = Statement::Let {671                pat: args_pat,672                type_ref: None,673                initializer: Some(args),674                else_branch: None,675            };676            (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into())))677        } else {678            // Generate:679            //     super let args = (&arg0, &arg1, &...);680            let args_name = self.generate_new_name();681            let args_binding = self.alloc_binding(682                args_name.clone(),683                BindingAnnotation::Unannotated,684                HygieneId::ROOT,685            );686            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });687            self.add_definition_to_binding(args_binding, args_pat);688            let elements = arguments689                .iter()690                .map(|arg| {691                    self.alloc_expr_desugared(Expr::Ref {692                        expr: arg.expr,693                        rawness: Rawness::Ref,694                        mutability: Mutability::Shared,695                    })696                })697                .collect();698            let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });699            // TODO: We don't have `super let` yet700            let let_stmt1 = Statement::Let {701                pat: args_pat,702                type_ref: None,703                initializer: Some(args_tuple),704                else_branch: None,705            };706707            // Generate:708            //     super let args = [709            //         <core::fmt::Argument>::new_display(args.0),710            //         <core::fmt::Argument>::new_lower_hex(args.1),711            //         <core::fmt::Argument>::new_debug(args.0),712            //         …713            //     ];714            let args = argmap715                .iter()716                .map(|&(arg_index, ty)| {717                    let args_ident_expr =718                        self.alloc_expr_desugared(Expr::Path(args_name.clone().into()));719                    let arg = self.alloc_expr_desugared(Expr::Field {720                        expr: args_ident_expr,721                        name: Name::new_tuple_field(arg_index),722                    });723                    let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);724                    self.make_argument(arg_ptr, arg, ty)725                })726                .collect();727            let array =728                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));729            let args_binding = self.alloc_binding(730                args_name.clone(),731                BindingAnnotation::Unannotated,732                HygieneId::ROOT,733            );734            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });735            self.add_definition_to_binding(args_binding, args_pat);736            let let_stmt2 = Statement::Let {737                pat: args_pat,738                type_ref: None,739                initializer: Some(array),740                else_branch: None,741            };742            (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into())))743        };744745        // Generate:746        //     &args747        let args = self.alloc_expr_desugared(Expr::Ref {748            expr: args,749            rawness: Rawness::Ref,750            mutability: Mutability::Shared,751        });752753        let call_block = {754            // Generate:755            //     unsafe {756            //         <core::fmt::Arguments>::new_v1_formatted(757            //             lit_pieces,758            //             args,759            //             format_options,760            //         )761            //     }762763            let new_v1_formatted = self.ty_rel_lang_path_desugared_expr(764                self.lang_items().FormatArguments,765                sym::new_v1_formatted,766            );767            let args = [lit_pieces, args, format_options];768            let call = self769                .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });770771            Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) }772        };773774        if !let_stmts.is_empty() {775            // Generate:776            //     {777            //         super let …778            //         super let …779            //         <core::fmt::Arguments>::new_…(…)780            //     }781            let call = self.alloc_expr_desugared(call_block);782            self.alloc_expr(783                Expr::Block {784                    id: None,785                    statements: let_stmts.into(),786                    tail: Some(call),787                    label: None,788                },789                syntax_ptr,790            )791        } else {792            self.alloc_expr(call_block, syntax_ptr)793        }794    }795796    /// Generate a hir expression for a format_args placeholder specification.797    ///798    /// Generates799    ///800    /// ```text801    ///     <core::fmt::rt::Placeholder::new(802    ///         …usize, // position803    ///         '…', // fill804    ///         <core::fmt::rt::Alignment>::…, // alignment805    ///         …u32, // flags806    ///         <core::fmt::rt::Count::…>, // width807    ///         <core::fmt::rt::Count::…>, // precision808    ///     )809    /// ```810    fn make_format_spec(811        &mut self,812        placeholder: &FormatPlaceholder,813        argmap: &mut FxIndexSet<(usize, ArgumentType)>,814    ) -> ExprId {815        let lang_items = self.lang_items();816        let position = match placeholder.argument.index {817            Ok(arg_index) => {818                let (i, _) =819                    argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));820                self.alloc_expr_desugared(Expr::Literal(Literal::Uint(821                    i as u128,822                    Some(BuiltinUint::Usize),823                )))824            }825            Err(_) => self.missing_expr(),826        };827        let &FormatOptions {828            ref width,829            ref precision,830            alignment,831            fill,832            sign,833            alternate,834            zero_pad,835            debug_hex,836        } = &placeholder.format_options;837838        let precision_expr = self.make_count_before_1_93_0(precision, argmap);839        let width_expr = self.make_count_before_1_93_0(width, argmap);840841        if self.krate.workspace_data(self.db).is_atleast_187() {842            // These need to match the constants in library/core/src/fmt/rt.rs.843            let align = match alignment {844                Some(FormatAlignment::Left) => 0,845                Some(FormatAlignment::Right) => 1,846                Some(FormatAlignment::Center) => 2,847                None => 3,848            };849            // This needs to match `Flag` in library/core/src/fmt/rt.rs.850            let flags = fill.unwrap_or(' ') as u32851                | ((sign == Some(FormatSign::Plus)) as u32) << 21852                | ((sign == Some(FormatSign::Minus)) as u32) << 22853                | (alternate as u32) << 23854                | (zero_pad as u32) << 24855                | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25856                | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26857                | (width.is_some() as u32) << 27858                | (precision.is_some() as u32) << 28859                | align << 29860                | 1 << 31; // Highest bit always set.861            let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(862                flags as u128,863                Some(BuiltinUint::U32),864            )));865866            let position =867                RecordLitField { name: Name::new_symbol_root(sym::position), expr: position };868            let flags = RecordLitField { name: Name::new_symbol_root(sym::flags), expr: flags };869            let precision = RecordLitField {870                name: Name::new_symbol_root(sym::precision),871                expr: precision_expr,872            };873            let width =874                RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr };875            match self.lang_path(lang_items.FormatPlaceholder) {876                Some(path) => self.alloc_expr_desugared(Expr::RecordLit {877                    path,878                    fields: Box::new([position, flags, precision, width]),879                    spread: RecordSpread::None,880                }),881                None => self.missing_expr(),882            }883        } else {884            let format_placeholder_new =885                self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new);886            // This needs to match `Flag` in library/core/src/fmt/rt.rs.887            let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)888                | (((sign == Some(FormatSign::Minus)) as u32) << 1)889                | ((alternate as u32) << 2)890                | ((zero_pad as u32) << 3)891                | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4)892                | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5);893            let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(894                flags as u128,895                Some(BuiltinUint::U32),896            )));897            let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));898            let align = self.ty_rel_lang_path_desugared_expr(899                lang_items.FormatAlignment,900                match alignment {901                    Some(FormatAlignment::Left) => sym::Left,902                    Some(FormatAlignment::Right) => sym::Right,903                    Some(FormatAlignment::Center) => sym::Center,904                    None => sym::Unknown,905                },906            );907            self.alloc_expr_desugared(Expr::Call {908                callee: format_placeholder_new,909                args: Box::new([position, fill, align, flags, precision_expr, width_expr]),910            })911        }912    }913914    /// Generate a hir expression for a format_args Count.915    ///916    /// Generates:917    ///918    /// ```text919    ///     <core::fmt::rt::Count>::Is(…)920    /// ```921    ///922    /// or923    ///924    /// ```text925    ///     <core::fmt::rt::Count>::Param(…)926    /// ```927    ///928    /// or929    ///930    /// ```text931    ///     <core::fmt::rt::Count>::Implied932    /// ```933    fn make_count_before_1_93_0(934        &mut self,935        count: &Option<FormatCount>,936        argmap: &mut FxIndexSet<(usize, ArgumentType)>,937    ) -> ExprId {938        let lang_items = self.lang_items();939        match count {940            Some(FormatCount::Literal(n)) => {941                let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(942                    *n as u128,943                    // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88944                    None,945                )));946                let count_is =947                    self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Is);948                self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })949            }950            Some(FormatCount::Argument(arg)) => {951                if let Ok(arg_index) = arg.index {952                    let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));953954                    let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(955                        i as u128,956                        Some(BuiltinUint::Usize),957                    )));958                    let count_param =959                        self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Param);960                    self.alloc_expr_desugared(Expr::Call {961                        callee: count_param,962                        args: Box::new([args]),963                    })964                } else {965                    // FIXME: This drops arg causing it to potentially not be resolved/type checked966                    // when typing?967                    self.missing_expr()968                }969            }970            None => match self.ty_rel_lang_path(lang_items.FormatCount, sym::Implied) {971                Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),972                None => self.missing_expr(),973            },974        }975    }976977    /// Generate a hir expression representing an argument to a format_args invocation.978    ///979    /// Generates:980    ///981    /// ```text982    ///     <core::fmt::Argument>::new_…(arg)983    /// ```984    fn make_argument(985        &mut self,986        arg_ptr: Option<AstPtr<ast::Expr>>,987        arg: ExprId,988        ty: ArgumentType,989    ) -> ExprId {990        use ArgumentType::*;991        use FormatTrait::*;992993        let new_fn = self.ty_rel_lang_path(994            self.lang_items().FormatArgument,995            match ty {996                Format(Display) => sym::new_display,997                Format(Debug) => sym::new_debug,998                Format(LowerExp) => sym::new_lower_exp,999                Format(UpperExp) => sym::new_upper_exp,1000                Format(Octal) => sym::new_octal,1001                Format(Pointer) => sym::new_pointer,1002                Format(Binary) => sym::new_binary,1003                Format(LowerHex) => sym::new_lower_hex,1004                Format(UpperHex) => sym::new_upper_hex,1005                Usize => sym::from_usize,1006            },1007        );1008        let new_fn = match new_fn {1009            Some(new_fn) => {1010                let new_fn = self.store.exprs.alloc(Expr::Path(new_fn));1011                if let Some(arg_ptr) = arg_ptr {1012                    // Trait errors (the argument does not implement the expected fmt trait) will show1013                    // on this path, so to not end up with synthetic syntax we insert this mapping. We1014                    // don't want to insert the other way's mapping in order to not override the source1015                    // for the argument.1016                    self.store1017                        .expr_map_back1018                        .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left()));1019                }1020                new_fn1021            }1022            None => self.missing_expr(),1023        };1024        self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })1025    }10261027    fn ty_rel_lang_path_desugared_expr(1028        &mut self,1029        lang: Option<impl Into<LangItemTarget>>,1030        relative_name: Symbol,1031    ) -> ExprId {1032        self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name))1033    }1034}10351036#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]1037enum ArgumentType {1038    Format(FormatTrait),1039    Usize,1040}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.