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.