From c0bf494d24ad7fe8aed76320286b105cf90be4c0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Dec 2025 18:42:22 +0200 Subject: [PATCH] Support the new lowering of format_args!() --- .../crates/hir-def/src/expr_store/lower.rs | 32 +- .../src/expr_store/lower/format_args.rs | 461 +++++++++++++++--- .../hir-def/src/expr_store/tests/body.rs | 84 +++- .../crates/test-utils/src/minicore.rs | 51 +- 4 files changed, 523 insertions(+), 105 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 3448b734ff10..0b25812f261b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -432,6 +432,8 @@ pub struct ExprCollector<'db> { awaitable_context: Option, krate: base_db::Crate, + + name_generator_index: usize, } #[derive(Clone, Debug)] @@ -537,9 +539,16 @@ impl<'db> ExprCollector<'db> { current_block_legacy_macro_defs_count: FxHashMap::default(), outer_impl_trait: false, krate, + name_generator_index: 0, } } + fn generate_new_name(&mut self) -> Name { + let index = self.name_generator_index; + self.name_generator_index += 1; + Name::generate_new_name(index) + } + #[inline] pub(crate) fn lang_items(&self) -> &'db LangItems { self.lang_items.get_or_init(|| crate::lang_item::lang_items(self.db, self.def_map.krate())) @@ -1638,9 +1647,8 @@ impl<'db> ExprCollector<'db> { /// and save the `` to use it as a break target for desugaring of the `?` operator. fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput); - let label = self.alloc_label_desugared(Label { - name: Name::generate_new_name(self.store.labels.len()), - }); + let label = self.generate_new_name(); + let label = self.alloc_label_desugared(Label { name: label }); let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); @@ -1768,7 +1776,7 @@ impl<'db> ExprCollector<'db> { this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; - let iter_name = Name::generate_new_name(self.store.exprs.len()); + let iter_name = self.generate_new_name(); let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); let iter_expr_mut = self.alloc_expr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, @@ -1829,7 +1837,7 @@ impl<'db> ExprCollector<'db> { let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); - let continue_name = Name::generate_new_name(self.store.bindings.len()); + let continue_name = self.generate_new_name(); let continue_binding = self.alloc_binding( continue_name.clone(), BindingAnnotation::Unannotated, @@ -1847,7 +1855,7 @@ impl<'db> ExprCollector<'db> { guard: None, expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr), }; - let break_name = Name::generate_new_name(self.store.bindings.len()); + let break_name = self.generate_new_name(); let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated, HygieneId::ROOT); let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); @@ -2628,9 +2636,17 @@ impl<'db> ExprCollector<'db> { fn ty_rel_lang_path( &self, lang: Option>, - relative_name: Name, + relative_name: Symbol, ) -> Option { - Some(Path::LangItem(lang?.into(), Some(relative_name))) + Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name)))) + } + + fn ty_rel_lang_path_expr( + &self, + lang: Option>, + relative_name: Symbol, + ) -> Expr { + self.ty_rel_lang_path(lang, relative_name).map_or(Expr::Missing, Expr::Path) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index aa7bf0e4e244..7efc9a956c1c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -19,6 +19,7 @@ use crate::{ FormatPlaceholder, FormatSign, FormatTrait, }, }, + lang_item::LangItemTarget, type_ref::{Mutability, Rawness}, }; @@ -94,6 +95,347 @@ impl<'db> ExprCollector<'db> { ), }; + let idx = if self.lang_items().FormatCount.is_none() { + self.collect_format_args_after_1_93_0_impl(syntax_ptr, fmt) + } else { + self.collect_format_args_before_1_93_0_impl(syntax_ptr, fmt) + }; + + self.store + .template_map + .get_or_insert_with(Default::default) + .format_args_to_captures + .insert(idx, (hygiene, mappings)); + idx + } + + fn collect_format_args_after_1_93_0_impl( + &mut self, + syntax_ptr: AstPtr, + fmt: FormatArgs, + ) -> ExprId { + let lang_items = self.lang_items(); + + // Create a list of all _unique_ (argument, format trait) combinations. + // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] + // + // We use usize::MAX for arguments that don't exist, because that can never be a valid index + // into the arguments array. + let mut argmap = FxIndexSet::default(); + + let mut incomplete_lit = String::new(); + + let mut implicit_arg_index = 0; + + let mut bytecode = Vec::new(); + + let template = if fmt.template.is_empty() { + // Treat empty templates as a single literal piece (with an empty string), + // so we produce `from_str("")` for those. + &[FormatArgsPiece::Literal(sym::__empty)][..] + } else { + &fmt.template[..] + }; + + // See library/core/src/fmt/mod.rs for the format string encoding format. + + for (i, piece) in template.iter().enumerate() { + match piece { + FormatArgsPiece::Literal(sym) => { + // Coalesce adjacent literal pieces. + if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) { + incomplete_lit.push_str(sym.as_str()); + continue; + } + let mut s = if incomplete_lit.is_empty() { + sym.as_str() + } else { + incomplete_lit.push_str(sym.as_str()); + &incomplete_lit + }; + + // If this is the last piece and was the only piece, that means + // there are no placeholders and the entire format string is just a literal. + // + // In that case, we can just use `from_str`. + if i + 1 == template.len() && bytecode.is_empty() { + // Generate: + // ::from_str("meow") + let from_str = self.ty_rel_lang_path_desugared_expr( + lang_items.FormatArguments, + sym::from_str, + ); + let sym = + if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) }; + let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym))); + let from_str = self.alloc_expr( + Expr::Call { callee: from_str, args: Box::new([s]) }, + syntax_ptr, + ); + return if !fmt.arguments.arguments.is_empty() { + // With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments` + // to be non-empty when reaching this code path. + self.alloc_expr( + Expr::Block { + id: None, + statements: fmt + .arguments + .arguments + .iter() + .map(|arg| Statement::Expr { + expr: arg.expr, + has_semi: true, + }) + .collect(), + tail: Some(from_str), + label: None, + }, + syntax_ptr, + ) + } else { + from_str + }; + } + + // Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries. + while !s.is_empty() { + let len = s.floor_char_boundary(usize::from(u16::MAX)); + if len < 0x80 { + bytecode.push(len as u8); + } else { + bytecode.push(0x80); + bytecode.extend_from_slice(&(len as u16).to_le_bytes()); + } + bytecode.extend(&s.as_bytes()[..len]); + s = &s[len..]; + } + + incomplete_lit.clear(); + } + FormatArgsPiece::Placeholder(p) => { + // Push the start byte and remember its index so we can set the option bits later. + let i = bytecode.len(); + bytecode.push(0xC0); + + let position = match &p.argument.index { + &Ok(it) => it, + Err(_) => usize::MAX, + }; + let position = argmap + .insert_full((position, ArgumentType::Format(p.format_trait))) + .0 as u64; + + // This needs to match the constants in library/core/src/fmt/mod.rs. + let o = &p.format_options; + let align = match o.alignment { + Some(FormatAlignment::Left) => 0, + Some(FormatAlignment::Right) => 1, + Some(FormatAlignment::Center) => 2, + None => 3, + }; + let default_flags = 0x6000_0020; + let flags: u32 = o.fill.unwrap_or(' ') as u32 + | ((o.sign == Some(FormatSign::Plus)) as u32) << 21 + | ((o.sign == Some(FormatSign::Minus)) as u32) << 22 + | (o.alternate as u32) << 23 + | (o.zero_pad as u32) << 24 + | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 + | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 + | (o.width.is_some() as u32) << 27 + | (o.precision.is_some() as u32) << 28 + | align << 29; + if flags != default_flags { + bytecode[i] |= 1; + bytecode.extend_from_slice(&flags.to_le_bytes()); + if let Some(val) = &o.width { + let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap); + // Only encode if nonzero; zero is the default. + if indirect || val != 0 { + bytecode[i] |= 1 << 1 | (indirect as u8) << 4; + bytecode.extend_from_slice(&val.to_le_bytes()); + } + } + if let Some(val) = &o.precision { + let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap); + // Only encode if nonzero; zero is the default. + if indirect || val != 0 { + bytecode[i] |= 1 << 2 | (indirect as u8) << 5; + bytecode.extend_from_slice(&val.to_le_bytes()); + } + } + } + if implicit_arg_index != position { + bytecode[i] |= 1 << 3; + bytecode.extend_from_slice(&(position as u16).to_le_bytes()); + } + implicit_arg_index = position + 1; + } + } + } + + assert!(incomplete_lit.is_empty()); + + // Zero terminator. + bytecode.push(0); + + // Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before. + if argmap.len() > u16::MAX as usize { + // FIXME: Emit an error. + // ctx.dcx().span_err(macsp, "too many format arguments"); + } + + let arguments = &fmt.arguments.arguments[..]; + + let (mut statements, args) = if arguments.is_empty() { + // Generate: + // [] + ( + Vec::new(), + self.alloc_expr_desugared(Expr::Array(Array::ElementList { + elements: Box::new([]), + })), + ) + } else { + // Generate: + // super let args = (&arg0, &arg1, &…); + let args_name = self.generate_new_name(); + let args_path = Path::from(args_name.clone()); + let args_binding = self.alloc_binding( + args_name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); + let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); + self.add_definition_to_binding(args_binding, args_pat); + let elements = arguments + .iter() + .map(|arg| { + self.alloc_expr_desugared(Expr::Ref { + expr: arg.expr, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }) + }) + .collect(); + let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements }); + // FIXME: Make this a `super let` when we have this statement. + let let_statement_1 = Statement::Let { + pat: args_pat, + type_ref: None, + initializer: Some(args_tuple), + else_branch: None, + }; + + // Generate: + // super let args = [ + // ::new_display(args.0), + // ::new_lower_hex(args.1), + // ::new_debug(args.0), + // … + // ]; + let args = argmap + .iter() + .map(|&(arg_index, ty)| { + let args_ident_expr = self.alloc_expr_desugared(Expr::Path(args_path.clone())); + let arg = self.alloc_expr_desugared(Expr::Field { + expr: args_ident_expr, + name: Name::new_tuple_field(arg_index), + }); + self.make_argument(arg, ty) + }) + .collect(); + let args = + self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args })); + let args_binding = + self.alloc_binding(args_name, BindingAnnotation::Unannotated, HygieneId::ROOT); + let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None }); + self.add_definition_to_binding(args_binding, args_pat); + // FIXME: Make this a `super let` when we have this statement. + let let_statement_2 = Statement::Let { + pat: args_pat, + type_ref: None, + initializer: Some(args), + else_branch: None, + }; + ( + vec![let_statement_1, let_statement_2], + self.alloc_expr_desugared(Expr::Path(args_path)), + ) + }; + + // Generate: + // unsafe { + // ::new(b"…", &args) + // } + let template = self + .alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice()))); + let call = { + let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new); + let args = self.alloc_expr_desugared(Expr::Ref { + expr: args, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }); + self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) }) + }; + let call = self.alloc_expr( + Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) }, + syntax_ptr, + ); + + // We collect the unused expressions here so that we still infer them instead of + // dropping them out of the expression tree. We cannot store them in the `Unsafe` + // block because then unsafe blocks within them will get a false "unused unsafe" + // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't). + statements + .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true })); + + if !statements.is_empty() { + // Generate: + // { + // super let … + // super let … + // ::new(…) + // } + self.alloc_expr( + Expr::Block { + id: None, + statements: statements.into_boxed_slice(), + tail: Some(call), + label: None, + }, + syntax_ptr, + ) + } else { + call + } + } + + /// Get the value for a `width` or `precision` field. + /// + /// Returns the value and whether it is indirect (an indexed argument) or not. + fn make_count_after_1_93_0( + &self, + count: &FormatCount, + argmap: &mut FxIndexSet<(usize, ArgumentType)>, + ) -> (bool, u16) { + match count { + FormatCount::Literal(n) => (false, *n), + FormatCount::Argument(arg) => { + let index = match &arg.index { + &Ok(it) => it, + Err(_) => usize::MAX, + }; + (true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16) + } + } + } + + fn collect_format_args_before_1_93_0_impl( + &mut self, + syntax_ptr: AstPtr, + fmt: FormatArgs, + ) -> ExprId { // Create a list of all _unique_ (argument, format trait) combinations. // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] let mut argmap = FxIndexSet::default(); @@ -160,8 +502,14 @@ impl<'db> ExprCollector<'db> { let fmt_unsafe_arg = lang_items.FormatUnsafeArg; let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none(); - let idx = if use_format_args_since_1_89_0 { - self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options) + if use_format_args_since_1_89_0 { + self.collect_format_args_after_1_89_0_impl( + syntax_ptr, + fmt, + argmap, + lit_pieces, + format_options, + ) } else { self.collect_format_args_before_1_89_0_impl( syntax_ptr, @@ -170,14 +518,7 @@ impl<'db> ExprCollector<'db> { lit_pieces, format_options, ) - }; - - self.store - .template_map - .get_or_insert_with(Default::default) - .format_args_to_captures - .insert(idx, (hygiene, mappings)); - idx + } } /// `format_args!` expansion implementation for rustc versions < `1.89.0` @@ -238,17 +579,10 @@ impl<'db> ExprCollector<'db> { // ) let lang_items = self.lang_items(); - let new_v1_formatted = self.ty_rel_lang_path( - lang_items.FormatArguments, - Name::new_symbol_root(sym::new_v1_formatted), - ); - let unsafe_arg_new = - self.ty_rel_lang_path(lang_items.FormatUnsafeArg, Name::new_symbol_root(sym::new)); let new_v1_formatted = - self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path)); - + self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new_v1_formatted); let unsafe_arg_new = - self.alloc_expr_desugared(unsafe_arg_new.map_or(Expr::Missing, Expr::Path)); + self.ty_rel_lang_path_desugared_expr(lang_items.FormatUnsafeArg, sym::new); let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() }); let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { @@ -284,7 +618,7 @@ impl<'db> ExprCollector<'db> { /// `format_args!` expansion implementation for rustc versions >= `1.89.0`, /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748) - fn collect_format_args_impl( + fn collect_format_args_after_1_89_0_impl( &mut self, syntax_ptr: AstPtr, fmt: FormatArgs, @@ -422,12 +756,10 @@ impl<'db> ExprCollector<'db> { // ) // } - let new_v1_formatted = self.ty_rel_lang_path( + let new_v1_formatted = self.ty_rel_lang_path_desugared_expr( self.lang_items().FormatArguments, - Name::new_symbol_root(sym::new_v1_formatted), + sym::new_v1_formatted, ); - let new_v1_formatted = - self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path)); let args = [lit_pieces, args, format_options]; let call = self .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() }); @@ -499,8 +831,8 @@ impl<'db> ExprCollector<'db> { debug_hex, } = &placeholder.format_options; - let precision_expr = self.make_count(precision, argmap); - let width_expr = self.make_count(width, argmap); + let precision_expr = self.make_count_before_1_93_0(precision, argmap); + let width_expr = self.make_count_before_1_93_0(width, argmap); if self.krate.workspace_data(self.db).is_atleast_187() { // These need to match the constants in library/core/src/fmt/rt.rs. @@ -542,16 +874,8 @@ impl<'db> ExprCollector<'db> { spread: None, }) } else { - let format_placeholder_new = { - let format_placeholder_new = self.ty_rel_lang_path( - lang_items.FormatPlaceholder, - Name::new_symbol_root(sym::new), - ); - match format_placeholder_new { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; + let format_placeholder_new = + self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new); // This needs to match `Flag` in library/core/src/fmt/rt.rs. let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) | (((sign == Some(FormatSign::Minus)) as u32) << 1) @@ -564,21 +888,15 @@ impl<'db> ExprCollector<'db> { Some(BuiltinUint::U32), ))); let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' ')))); - let align = { - let align = self.ty_rel_lang_path( - lang_items.FormatAlignment, - match alignment { - Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left), - Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right), - Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center), - None => Name::new_symbol_root(sym::Unknown), - }, - ); - match align { - Some(path) => self.alloc_expr_desugared(Expr::Path(path)), - None => self.missing_expr(), - } - }; + let align = self.ty_rel_lang_path_desugared_expr( + lang_items.FormatAlignment, + match alignment { + Some(FormatAlignment::Left) => sym::Left, + Some(FormatAlignment::Right) => sym::Right, + Some(FormatAlignment::Center) => sym::Center, + None => sym::Unknown, + }, + ); self.alloc_expr_desugared(Expr::Call { callee: format_placeholder_new, args: Box::new([position, fill, align, flags, precision_expr, width_expr]), @@ -605,7 +923,7 @@ impl<'db> ExprCollector<'db> { /// ```text /// ::Implied /// ``` - fn make_count( + fn make_count_before_1_93_0( &mut self, count: &Option, argmap: &mut FxIndexSet<(usize, ArgumentType)>, @@ -618,12 +936,8 @@ impl<'db> ExprCollector<'db> { // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88 None, ))); - let count_is = match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Is)) - { - Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), - None => self.missing_expr(), - }; + let count_is = + self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Is); self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) }) } Some(FormatCount::Argument(arg)) => { @@ -634,12 +948,8 @@ impl<'db> ExprCollector<'db> { i as u128, Some(BuiltinUint::Usize), ))); - let count_param = match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Param)) - { - Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), - None => self.missing_expr(), - }; + let count_param = + self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Param); self.alloc_expr_desugared(Expr::Call { callee: count_param, args: Box::new([args]), @@ -650,9 +960,7 @@ impl<'db> ExprCollector<'db> { self.missing_expr() } } - None => match self - .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Implied)) - { + None => match self.ty_rel_lang_path(lang_items.FormatCount, sym::Implied) { Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), }, @@ -670,9 +978,9 @@ impl<'db> ExprCollector<'db> { use ArgumentType::*; use FormatTrait::*; - let new_fn = match self.ty_rel_lang_path( + let new_fn = self.ty_rel_lang_path_desugared_expr( self.lang_items().FormatArgument, - Name::new_symbol_root(match ty { + match ty { Format(Display) => sym::new_display, Format(Debug) => sym::new_debug, Format(LowerExp) => sym::new_lower_exp, @@ -683,13 +991,18 @@ impl<'db> ExprCollector<'db> { Format(LowerHex) => sym::new_lower_hex, Format(UpperHex) => sym::new_upper_hex, Usize => sym::from_usize, - }), - ) { - Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), - None => self.missing_expr(), - }; + }, + ); self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) }) } + + fn ty_rel_lang_path_desugared_expr( + &mut self, + lang: Option>, + relative_name: Symbol, + ) -> ExprId { + self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name)) + } } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index 22ade4387582..7e48ca8f4558 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs @@ -161,9 +161,9 @@ fn main() { match builtin#lang(into_iter)( 0..10, ) { - mut 11 => loop { + mut 0 => loop { match builtin#lang(next)( - &mut 11, + &mut 0, ) { builtin#lang(None) => break, builtin#lang(Some)(ident) => { @@ -261,10 +261,10 @@ fn main() { } #[test] -fn desugar_builtin_format_args() { +fn desugar_builtin_format_args_before_1_93_0() { let (db, body, def) = lower( r#" -//- minicore: fmt +//- minicore: fmt_before_1_93_0 fn main() { let are = "are"; let count = 10; @@ -343,6 +343,59 @@ fn main() { .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) } +#[test] +fn desugar_builtin_format_args() { + let (db, body, def) = lower( + r#" +//- minicore: fmt +fn main() { + let are = "are"; + let count = 10; + builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!"); + builtin#format_args("hello world"); + builtin#format_args("hello world", orphan = ()); +} +"#, + ); + + expect![[r#" + fn main() { + let are = "are"; + let count = 10; + { + let 0 = (&"fancy", &(), &"!", &count, &are, ); + let 0 = [ + builtin#lang(Argument::new_display)( + 0.3, + ), builtin#lang(Argument::new_display)( + 0.0, + ), builtin#lang(Argument::new_debug)( + 0.4, + ), builtin#lang(Argument::new_display)( + 0.2, + ), + ]; + (); + unsafe { + builtin#lang(Arguments::new)( + "\x07\x1bhello \xc3 \x00\x00i\x02\x00\x01 \xc0\r friends, we \xc0\x01 \xc8\x01\x00\xc8\x03\x00\x00", + &0, + ) + } + }; + builtin#lang(Arguments::from_str)( + "hello world", + ); + { + (); + builtin#lang(Arguments::from_str)( + "hello world", + ) + }; + }"#]] + .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) +} + #[test] fn test_macro_hygiene() { let (db, body, def) = lower( @@ -382,27 +435,16 @@ impl SsrError { fn main() { _ = ra_test_fixture::error::SsrError::new( { - let args = [ + let 0 = (&node.text(), ); + let 0 = [ builtin#lang(Argument::new_display)( - &node.text(), + 0.0, ), ]; unsafe { - builtin#lang(Arguments::new_v1_formatted)( - &[ - "Failed to resolve path `", "`", - ], - &args, - &[ - builtin#lang(Placeholder::new)( - 0usize, - ' ', - builtin#lang(Alignment::Unknown), - 0u32, - builtin#lang(Count::Implied), - builtin#lang(Count::Implied), - ), - ], + builtin#lang(Arguments::new)( + "\x18Failed to resolve path `\xc0\x01`\x00", + &0, ) } }, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 0fe17e3075de..b7c09391ec37 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -34,7 +34,8 @@ //! eq: sized //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive -//! fmt_before_1_89_0: fmt +//! fmt_before_1_93_0: fmt +//! fmt_before_1_89_0: fmt_before_1_93_0 //! fn: sized, tuple //! from: sized, result //! future: pin @@ -1259,6 +1260,7 @@ pub mod fmt { Unknown, } + // region:fmt_before_1_93_0 #[lang = "format_count"] pub enum Count { Is(usize), @@ -1288,6 +1290,7 @@ pub mod fmt { Placeholder { position, fill, align, flags, precision, width } } } + // endregion:fmt_before_1_93_0 // region:fmt_before_1_89_0 #[lang = "format_unsafe_arg"] @@ -1303,6 +1306,7 @@ pub mod fmt { // endregion:fmt_before_1_89_0 } + // region:fmt_before_1_93_0 #[derive(Copy, Clone)] #[lang = "format_arguments"] pub struct Arguments<'a> { @@ -1341,6 +1345,14 @@ pub mod fmt { } // endregion:!fmt_before_1_89_0 + pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { + Self::from_str(s) + } + + pub const fn from_str(s: &'static str) -> Arguments<'a> { + Arguments { pieces: &[s], fmt: None, args: &[] } + } + pub const fn as_str(&self) -> Option<&'static str> { match (self.pieces, self.args) { ([], []) => Some(""), @@ -1349,6 +1361,41 @@ pub mod fmt { } } } + // endregion:fmt_before_1_93_0 + + // region:!fmt_before_1_93_0 + #[lang = "format_arguments"] + #[derive(Copy, Clone)] + pub struct Arguments<'a> { + // This is a non-faithful representation of `core::fmt::Arguments`, because the real one + // is too complex for minicore. + message: Option<&'a str>, + } + + impl<'a> Arguments<'a> { + pub unsafe fn new( + _template: &'a [u8; N], + _args: &'a [rt::Argument<'a>; M], + ) -> Arguments<'a> { + Arguments { message: None } + } + + pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { + Arguments { message: Some(s) } + } + + pub const fn from_str(s: &'static str) -> Arguments<'a> { + Arguments { message: Some(s) } + } + + pub fn as_str(&self) -> Option<&'static str> { + match self.message { + Some(s) => unsafe { Some(&*(s as *const str)) }, + None => None, + } + } + } + // endregion:!fmt_before_1_93_0 // region:derive pub(crate) mod derive { @@ -1817,7 +1864,7 @@ mod panicking { #[lang = "panic"] pub const fn panic(expr: &'static str) -> ! { - panic_fmt(crate::fmt::Arguments::new_const(&[expr])) + panic_fmt(crate::fmt::Arguments::from_str(expr)) } } // endregion:panic