Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
// unsafe {
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 args.add(FormatArgument {34 kind: match arg.arg_name() {35 Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),36 None => FormatArgumentKind::Normal,37 },38 expr: self.collect_expr_opt(arg.expr()),39 });40 });41 let template = f.template();42 let fmt_snippet = template.as_ref().and_then(|it| match it {43 ast::Expr::Literal(literal) => match literal.kind() {44 ast::LiteralKind::String(s) => Some(s.text().to_owned()),45 _ => None,46 },47 _ => None,48 });49 let mut mappings = vec![];50 let (fmt, hygiene) = match template.and_then(|template| {51 self.expand_macros_to_string(template.clone()).map(|it| (it, template))52 }) {53 Some(((s, is_direct_literal), template)) => {54 let call_ctx = SyntaxContext::root(self.def_map.edition());55 let hygiene = self.hygiene_id_for(s.syntax().text_range());56 let fmt = format_args::parse(57 &s,58 fmt_snippet,59 args,60 is_direct_literal,61 |name, range| {62 let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));63 if let Some(range) = range {64 self.store65 .template_map66 .get_or_insert_with(Default::default)67 .implicit_capture_to_source68 .insert(69 expr_id,70 self.expander.in_file((AstPtr::new(&template), range)),71 );72 }73 if !hygiene.is_root() {74 self.store.ident_hygiene.insert(expr_id.into(), hygiene);75 }76 expr_id77 },78 |name, span| {79 if let Some(span) = span {80 mappings.push((span, name))81 }82 },83 call_ctx,84 );85 (fmt, hygiene)86 }87 None => (88 FormatArgs {89 template: Default::default(),90 arguments: args.finish(),91 orphans: Default::default(),92 },93 HygieneId::ROOT,94 ),95 };9697 let idx = if self.lang_items().FormatCount.is_none() {98 self.collect_format_args_after_1_93_0_impl(syntax_ptr, fmt)99 } else {100 self.collect_format_args_before_1_93_0_impl(syntax_ptr, fmt)101 };102103 self.store104 .template_map105 .get_or_insert_with(Default::default)106 .format_args_to_captures107 .insert(idx, (hygiene, mappings));108 idx109 }110111 fn collect_format_args_after_1_93_0_impl(112 &mut self,113 syntax_ptr: AstPtr<ast::Expr>,114 fmt: FormatArgs,115 ) -> ExprId {116 let lang_items = self.lang_items();117118 // Create a list of all _unique_ (argument, format trait) combinations.119 // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]120 //121 // We use usize::MAX for arguments that don't exist, because that can never be a valid index122 // into the arguments array.123 let mut argmap = FxIndexSet::default();124125 let mut incomplete_lit = String::new();126127 let mut implicit_arg_index = 0;128129 let mut bytecode = Vec::new();130131 let template = if fmt.template.is_empty() {132 // Treat empty templates as a single literal piece (with an empty string),133 // so we produce `from_str("")` for those.134 &[FormatArgsPiece::Literal(sym::__empty)][..]135 } else {136 &fmt.template[..]137 };138139 // See library/core/src/fmt/mod.rs for the format string encoding format.140141 for (i, piece) in template.iter().enumerate() {142 match piece {143 FormatArgsPiece::Literal(sym) => {144 // Coalesce adjacent literal pieces.145 if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {146 incomplete_lit.push_str(sym.as_str());147 continue;148 }149 let mut s = if incomplete_lit.is_empty() {150 sym.as_str()151 } else {152 incomplete_lit.push_str(sym.as_str());153 &incomplete_lit154 };155156 // If this is the last piece and was the only piece, that means157 // there are no placeholders and the entire format string is just a literal.158 //159 // In that case, we can just use `from_str`.160 if i + 1 == template.len() && bytecode.is_empty() {161 // Generate:162 // <core::fmt::Arguments>::from_str("meow")163 let from_str = self.ty_rel_lang_path_desugared_expr(164 lang_items.FormatArguments,165 sym::from_str,166 );167 let sym =168 if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) };169 let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym)));170 let from_str = self.alloc_expr(171 Expr::Call { callee: from_str, args: Box::new([s]) },172 syntax_ptr,173 );174 return if !fmt.arguments.arguments.is_empty() {175 // With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments`176 // to be non-empty when reaching this code path.177 self.alloc_expr(178 Expr::Block {179 id: None,180 statements: fmt181 .arguments182 .arguments183 .iter()184 .map(|arg| Statement::Expr {185 expr: arg.expr,186 has_semi: true,187 })188 .collect(),189 tail: Some(from_str),190 label: None,191 },192 syntax_ptr,193 )194 } else {195 from_str196 };197 }198199 // 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 }211212 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);218219 let position = match &p.argument.index {220 &Ok(it) => it,221 Err(_) => usize::MAX,222 };223 let position = argmap224 .insert_full((position, ArgumentType::Format(p.format_trait)))225 .0 as u64;226227 // 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 u32237 | ((o.sign == Some(FormatSign::Plus)) as u32) << 21238 | ((o.sign == Some(FormatSign::Minus)) as u32) << 22239 | (o.alternate as u32) << 23240 | (o.zero_pad as u32) << 24241 | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25242 | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26243 | (o.width.is_some() as u32) << 27244 | (o.precision.is_some() as u32) << 28245 | 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 }274275 assert!(incomplete_lit.is_empty());276277 // Zero terminator.278 bytecode.push(0);279280 // 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 }285286 let arguments = &fmt.arguments.arguments[..];287288 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 = arguments310 .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 };327328 // 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 = argmap336 .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 self.make_argument(arg, ty)344 })345 .collect();346 let args =347 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));348 let args_binding =349 self.alloc_binding(args_name, BindingAnnotation::Unannotated, HygieneId::ROOT);350 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });351 self.add_definition_to_binding(args_binding, args_pat);352 // FIXME: Make this a `super let` when we have this statement.353 let let_statement_2 = Statement::Let {354 pat: args_pat,355 type_ref: None,356 initializer: Some(args),357 else_branch: None,358 };359 (360 vec![let_statement_1, let_statement_2],361 self.alloc_expr_desugared(Expr::Path(args_path)),362 )363 };364365 // Generate:366 // unsafe {367 // <core::fmt::Arguments>::new(b"…", &args)368 // }369 let template = self370 .alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice())));371 let call = {372 let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new);373 let args = self.alloc_expr_desugared(Expr::Ref {374 expr: args,375 rawness: Rawness::Ref,376 mutability: Mutability::Shared,377 });378 self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) })379 };380 let call = self.alloc_expr(381 Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) },382 syntax_ptr,383 );384385 // We collect the unused expressions here so that we still infer them instead of386 // dropping them out of the expression tree. We cannot store them in the `Unsafe`387 // block because then unsafe blocks within them will get a false "unused unsafe"388 // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).389 statements390 .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));391392 if !statements.is_empty() {393 // Generate:394 // {395 // super let …396 // super let …397 // <core::fmt::Arguments>::new(…)398 // }399 self.alloc_expr(400 Expr::Block {401 id: None,402 statements: statements.into_boxed_slice(),403 tail: Some(call),404 label: None,405 },406 syntax_ptr,407 )408 } else {409 call410 }411 }412413 /// Get the value for a `width` or `precision` field.414 ///415 /// Returns the value and whether it is indirect (an indexed argument) or not.416 fn make_count_after_1_93_0(417 &self,418 count: &FormatCount,419 argmap: &mut FxIndexSet<(usize, ArgumentType)>,420 ) -> (bool, u16) {421 match count {422 FormatCount::Literal(n) => (false, *n),423 FormatCount::Argument(arg) => {424 let index = match &arg.index {425 &Ok(it) => it,426 Err(_) => usize::MAX,427 };428 (true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16)429 }430 }431 }432433 fn collect_format_args_before_1_93_0_impl(434 &mut self,435 syntax_ptr: AstPtr<ast::Expr>,436 fmt: FormatArgs,437 ) -> ExprId {438 // Create a list of all _unique_ (argument, format trait) combinations.439 // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]440 let mut argmap = FxIndexSet::default();441 for piece in fmt.template.iter() {442 let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };443 if let Ok(index) = placeholder.argument.index {444 argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));445 }446 }447448 let lit_pieces = fmt449 .template450 .iter()451 .enumerate()452 .filter_map(|(i, piece)| {453 match piece {454 FormatArgsPiece::Literal(s) => {455 Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))))456 }457 &FormatArgsPiece::Placeholder(_) => {458 // Inject empty string before placeholders when not already preceded by a literal piece.459 if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))460 {461 Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(462 Symbol::empty(),463 ))))464 } else {465 None466 }467 }468 }469 })470 .collect();471 let lit_pieces =472 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));473 let lit_pieces = self.alloc_expr_desugared(Expr::Ref {474 expr: lit_pieces,475 rawness: Rawness::Ref,476 mutability: Mutability::Shared,477 });478 let format_options = {479 // Generate:480 // &[format_spec_0, format_spec_1, format_spec_2]481 let elements = fmt482 .template483 .iter()484 .filter_map(|piece| {485 let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };486 Some(self.make_format_spec(placeholder, &mut argmap))487 })488 .collect();489 let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));490 self.alloc_expr_desugared(Expr::Ref {491 expr: array,492 rawness: Rawness::Ref,493 mutability: Mutability::Shared,494 })495 };496497 // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists498 // but `format_unsafe_arg` does not499 let lang_items = self.lang_items();500 let fmt_args = lang_items.FormatArguments;501 let fmt_unsafe_arg = lang_items.FormatUnsafeArg;502 let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none();503504 if use_format_args_since_1_89_0 {505 self.collect_format_args_after_1_89_0_impl(506 syntax_ptr,507 fmt,508 argmap,509 lit_pieces,510 format_options,511 )512 } else {513 self.collect_format_args_before_1_89_0_impl(514 syntax_ptr,515 fmt,516 argmap,517 lit_pieces,518 format_options,519 )520 }521 }522523 /// `format_args!` expansion implementation for rustc versions < `1.89.0`524 fn collect_format_args_before_1_89_0_impl(525 &mut self,526 syntax_ptr: AstPtr<ast::Expr>,527 fmt: FormatArgs,528 argmap: FxIndexSet<(usize, ArgumentType)>,529 lit_pieces: ExprId,530 format_options: ExprId,531 ) -> ExprId {532 let arguments = &*fmt.arguments.arguments;533534 let args = if arguments.is_empty() {535 let expr = self536 .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));537 self.alloc_expr_desugared(Expr::Ref {538 expr,539 rawness: Rawness::Ref,540 mutability: Mutability::Shared,541 })542 } else {543 // Generate:544 // &match (&arg0, &arg1, &…) {545 // args => [546 // <core::fmt::Argument>::new_display(args.0),547 // <core::fmt::Argument>::new_lower_hex(args.1),548 // <core::fmt::Argument>::new_debug(args.0),549 // …550 // ]551 // }552 let args = argmap553 .iter()554 .map(|&(arg_index, ty)| {555 let arg = self.alloc_expr_desugared(Expr::Ref {556 expr: arguments[arg_index].expr,557 rawness: Rawness::Ref,558 mutability: Mutability::Shared,559 });560 self.make_argument(arg, ty)561 })562 .collect();563 let array =564 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));565 self.alloc_expr_desugared(Expr::Ref {566 expr: array,567 rawness: Rawness::Ref,568 mutability: Mutability::Shared,569 })570 };571572 // Generate:573 // <core::fmt::Arguments>::new_v1_formatted(574 // lit_pieces,575 // args,576 // format_options,577 // unsafe { ::core::fmt::UnsafeArg::new() }578 // )579580 let lang_items = self.lang_items();581 let new_v1_formatted =582 self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new_v1_formatted);583 let unsafe_arg_new =584 self.ty_rel_lang_path_desugared_expr(lang_items.FormatUnsafeArg, sym::new);585 let unsafe_arg_new =586 self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });587 let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {588 id: None,589 statements: Box::new([]),590 tail: Some(unsafe_arg_new),591 });592 if !fmt.orphans.is_empty() {593 unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {594 id: None,595 // We collect the unused expressions here so that we still infer them instead of596 // dropping them out of the expression tree. We cannot store them in the `Unsafe`597 // block because then unsafe blocks within them will get a false "unused unsafe"598 // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).599 statements: fmt600 .orphans601 .into_iter()602 .map(|expr| Statement::Expr { expr, has_semi: true })603 .collect(),604 tail: Some(unsafe_arg_new),605 label: None,606 });607 }608609 self.alloc_expr(610 Expr::Call {611 callee: new_v1_formatted,612 args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),613 },614 syntax_ptr,615 )616 }617618 /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,619 /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)620 fn collect_format_args_after_1_89_0_impl(621 &mut self,622 syntax_ptr: AstPtr<ast::Expr>,623 fmt: FormatArgs,624 argmap: FxIndexSet<(usize, ArgumentType)>,625 lit_pieces: ExprId,626 format_options: ExprId,627 ) -> ExprId {628 let arguments = &*fmt.arguments.arguments;629630 let (let_stmts, args) = if arguments.is_empty() {631 (632 // Generate:633 // []634 vec![],635 self.alloc_expr_desugared(Expr::Array(Array::ElementList {636 elements: Box::default(),637 })),638 )639 } else if argmap.len() == 1 && arguments.len() == 1 {640 // Only one argument, so we don't need to make the `args` tuple.641 //642 // Generate:643 // super let args = [<core::fmt::Arguments>::new_display(&arg)];644 let args = argmap645 .iter()646 .map(|&(arg_index, ty)| {647 let ref_arg = self.alloc_expr_desugared(Expr::Ref {648 expr: arguments[arg_index].expr,649 rawness: Rawness::Ref,650 mutability: Mutability::Shared,651 });652 self.make_argument(ref_arg, ty)653 })654 .collect();655 let args =656 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));657 let args_name = self.generate_new_name();658 let args_binding = self.alloc_binding(659 args_name.clone(),660 BindingAnnotation::Unannotated,661 HygieneId::ROOT,662 );663 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });664 self.add_definition_to_binding(args_binding, args_pat);665 // TODO: We don't have `super let` yet.666 let let_stmt = Statement::Let {667 pat: args_pat,668 type_ref: None,669 initializer: Some(args),670 else_branch: None,671 };672 (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into())))673 } else {674 // Generate:675 // super let args = (&arg0, &arg1, &...);676 let args_name = self.generate_new_name();677 let args_binding = self.alloc_binding(678 args_name.clone(),679 BindingAnnotation::Unannotated,680 HygieneId::ROOT,681 );682 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });683 self.add_definition_to_binding(args_binding, args_pat);684 let elements = arguments685 .iter()686 .map(|arg| {687 self.alloc_expr_desugared(Expr::Ref {688 expr: arg.expr,689 rawness: Rawness::Ref,690 mutability: Mutability::Shared,691 })692 })693 .collect();694 let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });695 // TODO: We don't have `super let` yet696 let let_stmt1 = Statement::Let {697 pat: args_pat,698 type_ref: None,699 initializer: Some(args_tuple),700 else_branch: None,701 };702703 // Generate:704 // super let args = [705 // <core::fmt::Argument>::new_display(args.0),706 // <core::fmt::Argument>::new_lower_hex(args.1),707 // <core::fmt::Argument>::new_debug(args.0),708 // …709 // ];710 let args = argmap711 .iter()712 .map(|&(arg_index, ty)| {713 let args_ident_expr =714 self.alloc_expr_desugared(Expr::Path(args_name.clone().into()));715 let arg = self.alloc_expr_desugared(Expr::Field {716 expr: args_ident_expr,717 name: Name::new_tuple_field(arg_index),718 });719 self.make_argument(arg, ty)720 })721 .collect();722 let array =723 self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));724 let args_binding = self.alloc_binding(725 args_name.clone(),726 BindingAnnotation::Unannotated,727 HygieneId::ROOT,728 );729 let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });730 self.add_definition_to_binding(args_binding, args_pat);731 let let_stmt2 = Statement::Let {732 pat: args_pat,733 type_ref: None,734 initializer: Some(array),735 else_branch: None,736 };737 (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into())))738 };739740 // Generate:741 // &args742 let args = self.alloc_expr_desugared(Expr::Ref {743 expr: args,744 rawness: Rawness::Ref,745 mutability: Mutability::Shared,746 });747748 let call_block = {749 // Generate:750 // unsafe {751 // <core::fmt::Arguments>::new_v1_formatted(752 // lit_pieces,753 // args,754 // format_options,755 // )756 // }757758 let new_v1_formatted = self.ty_rel_lang_path_desugared_expr(759 self.lang_items().FormatArguments,760 sym::new_v1_formatted,761 );762 let args = [lit_pieces, args, format_options];763 let call = self764 .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });765766 Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) }767 };768769 if !let_stmts.is_empty() {770 // Generate:771 // {772 // super let …773 // super let …774 // <core::fmt::Arguments>::new_…(…)775 // }776 let call = self.alloc_expr_desugared(call_block);777 self.alloc_expr(778 Expr::Block {779 id: None,780 statements: let_stmts.into(),781 tail: Some(call),782 label: None,783 },784 syntax_ptr,785 )786 } else {787 self.alloc_expr(call_block, syntax_ptr)788 }789 }790791 /// Generate a hir expression for a format_args placeholder specification.792 ///793 /// Generates794 ///795 /// ```text796 /// <core::fmt::rt::Placeholder::new(797 /// …usize, // position798 /// '…', // fill799 /// <core::fmt::rt::Alignment>::…, // alignment800 /// …u32, // flags801 /// <core::fmt::rt::Count::…>, // width802 /// <core::fmt::rt::Count::…>, // precision803 /// )804 /// ```805 fn make_format_spec(806 &mut self,807 placeholder: &FormatPlaceholder,808 argmap: &mut FxIndexSet<(usize, ArgumentType)>,809 ) -> ExprId {810 let lang_items = self.lang_items();811 let position = match placeholder.argument.index {812 Ok(arg_index) => {813 let (i, _) =814 argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));815 self.alloc_expr_desugared(Expr::Literal(Literal::Uint(816 i as u128,817 Some(BuiltinUint::Usize),818 )))819 }820 Err(_) => self.missing_expr(),821 };822 let &FormatOptions {823 ref width,824 ref precision,825 alignment,826 fill,827 sign,828 alternate,829 zero_pad,830 debug_hex,831 } = &placeholder.format_options;832833 let precision_expr = self.make_count_before_1_93_0(precision, argmap);834 let width_expr = self.make_count_before_1_93_0(width, argmap);835836 if self.krate.workspace_data(self.db).is_atleast_187() {837 // These need to match the constants in library/core/src/fmt/rt.rs.838 let align = match alignment {839 Some(FormatAlignment::Left) => 0,840 Some(FormatAlignment::Right) => 1,841 Some(FormatAlignment::Center) => 2,842 None => 3,843 };844 // This needs to match `Flag` in library/core/src/fmt/rt.rs.845 let flags = fill.unwrap_or(' ') as u32846 | ((sign == Some(FormatSign::Plus)) as u32) << 21847 | ((sign == Some(FormatSign::Minus)) as u32) << 22848 | (alternate as u32) << 23849 | (zero_pad as u32) << 24850 | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25851 | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26852 | (width.is_some() as u32) << 27853 | (precision.is_some() as u32) << 28854 | align << 29855 | 1 << 31; // Highest bit always set.856 let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(857 flags as u128,858 Some(BuiltinUint::U32),859 )));860861 let position =862 RecordLitField { name: Name::new_symbol_root(sym::position), expr: position };863 let flags = RecordLitField { name: Name::new_symbol_root(sym::flags), expr: flags };864 let precision = RecordLitField {865 name: Name::new_symbol_root(sym::precision),866 expr: precision_expr,867 };868 let width =869 RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr };870 self.alloc_expr_desugared(Expr::RecordLit {871 path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),872 fields: Box::new([position, flags, precision, width]),873 spread: RecordSpread::None,874 })875 } else {876 let format_placeholder_new =877 self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new);878 // This needs to match `Flag` in library/core/src/fmt/rt.rs.879 let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)880 | (((sign == Some(FormatSign::Minus)) as u32) << 1)881 | ((alternate as u32) << 2)882 | ((zero_pad as u32) << 3)883 | (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4)884 | (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5);885 let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(886 flags as u128,887 Some(BuiltinUint::U32),888 )));889 let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));890 let align = self.ty_rel_lang_path_desugared_expr(891 lang_items.FormatAlignment,892 match alignment {893 Some(FormatAlignment::Left) => sym::Left,894 Some(FormatAlignment::Right) => sym::Right,895 Some(FormatAlignment::Center) => sym::Center,896 None => sym::Unknown,897 },898 );899 self.alloc_expr_desugared(Expr::Call {900 callee: format_placeholder_new,901 args: Box::new([position, fill, align, flags, precision_expr, width_expr]),902 })903 }904 }905906 /// Generate a hir expression for a format_args Count.907 ///908 /// Generates:909 ///910 /// ```text911 /// <core::fmt::rt::Count>::Is(…)912 /// ```913 ///914 /// or915 ///916 /// ```text917 /// <core::fmt::rt::Count>::Param(…)918 /// ```919 ///920 /// or921 ///922 /// ```text923 /// <core::fmt::rt::Count>::Implied924 /// ```925 fn make_count_before_1_93_0(926 &mut self,927 count: &Option<FormatCount>,928 argmap: &mut FxIndexSet<(usize, ArgumentType)>,929 ) -> ExprId {930 let lang_items = self.lang_items();931 match count {932 Some(FormatCount::Literal(n)) => {933 let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(934 *n as u128,935 // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88936 None,937 )));938 let count_is =939 self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Is);940 self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })941 }942 Some(FormatCount::Argument(arg)) => {943 if let Ok(arg_index) = arg.index {944 let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));945946 let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(947 i as u128,948 Some(BuiltinUint::Usize),949 )));950 let count_param =951 self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Param);952 self.alloc_expr_desugared(Expr::Call {953 callee: count_param,954 args: Box::new([args]),955 })956 } else {957 // FIXME: This drops arg causing it to potentially not be resolved/type checked958 // when typing?959 self.missing_expr()960 }961 }962 None => match self.ty_rel_lang_path(lang_items.FormatCount, sym::Implied) {963 Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),964 None => self.missing_expr(),965 },966 }967 }968969 /// Generate a hir expression representing an argument to a format_args invocation.970 ///971 /// Generates:972 ///973 /// ```text974 /// <core::fmt::Argument>::new_…(arg)975 /// ```976 fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {977 use ArgumentType::*;978 use FormatTrait::*;979980 let new_fn = self.ty_rel_lang_path_desugared_expr(981 self.lang_items().FormatArgument,982 match ty {983 Format(Display) => sym::new_display,984 Format(Debug) => sym::new_debug,985 Format(LowerExp) => sym::new_lower_exp,986 Format(UpperExp) => sym::new_upper_exp,987 Format(Octal) => sym::new_octal,988 Format(Pointer) => sym::new_pointer,989 Format(Binary) => sym::new_binary,990 Format(LowerHex) => sym::new_lower_hex,991 Format(UpperHex) => sym::new_upper_hex,992 Usize => sym::from_usize,993 },994 );995 self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })996 }997998 fn ty_rel_lang_path_desugared_expr(999 &mut self,1000 lang: Option<impl Into<LangItemTarget>>,1001 relative_name: Symbol,1002 ) -> ExprId {1003 self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name))1004 }1005}10061007#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]1008enum ArgumentType {1009 Format(FormatTrait),1010 Usize,1011}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.