@@ -2815,6 +2815,44 @@ impl ExprCollector<'_> {
28152815 mutability : Mutability :: Shared ,
28162816 } )
28172817 } ;
2818+
2819+ // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
2820+ // but `format_unsafe_arg` does not
2821+ let fmt_args =
2822+ || crate :: lang_item:: lang_item ( self . db , self . module . krate ( ) , LangItem :: FormatArguments ) ;
2823+ let fmt_unsafe_arg =
2824+ || crate :: lang_item:: lang_item ( self . db , self . module . krate ( ) , LangItem :: FormatUnsafeArg ) ;
2825+ let use_format_args_since_1_89_0 = fmt_args ( ) . is_some ( ) && fmt_unsafe_arg ( ) . is_none ( ) ;
2826+
2827+ let idx = if use_format_args_since_1_89_0 {
2828+ self . collect_format_args_impl ( syntax_ptr, fmt, argmap, lit_pieces, format_options)
2829+ } else {
2830+ self . collect_format_args_before_1_89_0_impl (
2831+ syntax_ptr,
2832+ fmt,
2833+ argmap,
2834+ lit_pieces,
2835+ format_options,
2836+ )
2837+ } ;
2838+
2839+ self . source_map
2840+ . template_map
2841+ . get_or_insert_with ( Default :: default)
2842+ . format_args_to_captures
2843+ . insert ( idx, ( hygiene, mappings) ) ;
2844+ idx
2845+ }
2846+
2847+ /// `format_args!` expansion implementation for rustc versions < `1.89.0`
2848+ fn collect_format_args_before_1_89_0_impl (
2849+ & mut self ,
2850+ syntax_ptr : AstPtr < ast:: Expr > ,
2851+ fmt : FormatArgs ,
2852+ argmap : FxIndexSet < ( usize , ArgumentType ) > ,
2853+ lit_pieces : ExprId ,
2854+ format_options : ExprId ,
2855+ ) -> ExprId {
28182856 let arguments = & * fmt. arguments . arguments ;
28192857
28202858 let args = if arguments. is_empty ( ) {
@@ -2902,19 +2940,189 @@ impl ExprCollector<'_> {
29022940 } ) ;
29032941 }
29042942
2905- let idx = self . alloc_expr (
2943+ self . alloc_expr (
29062944 Expr :: Call {
29072945 callee : new_v1_formatted,
29082946 args : Box :: new ( [ lit_pieces, args, format_options, unsafe_arg_new] ) ,
29092947 } ,
29102948 syntax_ptr,
2911- ) ;
2912- self . source_map
2913- . template_map
2914- . get_or_insert_with ( Default :: default)
2915- . format_args_to_captures
2916- . insert ( idx, ( hygiene, mappings) ) ;
2917- idx
2949+ )
2950+ }
2951+
2952+ /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
2953+ /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
2954+ fn collect_format_args_impl (
2955+ & mut self ,
2956+ syntax_ptr : AstPtr < ast:: Expr > ,
2957+ fmt : FormatArgs ,
2958+ argmap : FxIndexSet < ( usize , ArgumentType ) > ,
2959+ lit_pieces : ExprId ,
2960+ format_options : ExprId ,
2961+ ) -> ExprId {
2962+ let arguments = & * fmt. arguments . arguments ;
2963+
2964+ let ( let_stmts, args) = if arguments. is_empty ( ) {
2965+ (
2966+ // Generate:
2967+ // []
2968+ vec ! [ ] ,
2969+ self . alloc_expr_desugared ( Expr :: Array ( Array :: ElementList {
2970+ elements : Box :: default ( ) ,
2971+ } ) ) ,
2972+ )
2973+ } else if argmap. len ( ) == 1 && arguments. len ( ) == 1 {
2974+ // Only one argument, so we don't need to make the `args` tuple.
2975+ //
2976+ // Generate:
2977+ // super let args = [<core::fmt::Arguments>::new_display(&arg)];
2978+ let args = argmap
2979+ . iter ( )
2980+ . map ( |& ( arg_index, ty) | {
2981+ let ref_arg = self . alloc_expr_desugared ( Expr :: Ref {
2982+ expr : arguments[ arg_index] . expr ,
2983+ rawness : Rawness :: Ref ,
2984+ mutability : Mutability :: Shared ,
2985+ } ) ;
2986+ self . make_argument ( ref_arg, ty)
2987+ } )
2988+ . collect ( ) ;
2989+ let args =
2990+ self . alloc_expr_desugared ( Expr :: Array ( Array :: ElementList { elements : args } ) ) ;
2991+ let args_name = Name :: new_symbol_root ( sym:: args) ;
2992+ let args_binding = self . alloc_binding (
2993+ args_name. clone ( ) ,
2994+ BindingAnnotation :: Unannotated ,
2995+ HygieneId :: ROOT ,
2996+ ) ;
2997+ let args_pat = self . alloc_pat_desugared ( Pat :: Bind { id : args_binding, subpat : None } ) ;
2998+ self . add_definition_to_binding ( args_binding, args_pat) ;
2999+ // TODO: We don't have `super let` yet.
3000+ let let_stmt = Statement :: Let {
3001+ pat : args_pat,
3002+ type_ref : None ,
3003+ initializer : Some ( args) ,
3004+ else_branch : None ,
3005+ } ;
3006+ ( vec ! [ let_stmt] , self . alloc_expr_desugared ( Expr :: Path ( args_name. into ( ) ) ) )
3007+ } else {
3008+ // Generate:
3009+ // super let args = (&arg0, &arg1, &...);
3010+ let args_name = Name :: new_symbol_root ( sym:: args) ;
3011+ let args_binding = self . alloc_binding (
3012+ args_name. clone ( ) ,
3013+ BindingAnnotation :: Unannotated ,
3014+ HygieneId :: ROOT ,
3015+ ) ;
3016+ let args_pat = self . alloc_pat_desugared ( Pat :: Bind { id : args_binding, subpat : None } ) ;
3017+ self . add_definition_to_binding ( args_binding, args_pat) ;
3018+ let elements = arguments
3019+ . iter ( )
3020+ . map ( |arg| {
3021+ self . alloc_expr_desugared ( Expr :: Ref {
3022+ expr : arg. expr ,
3023+ rawness : Rawness :: Ref ,
3024+ mutability : Mutability :: Shared ,
3025+ } )
3026+ } )
3027+ . collect ( ) ;
3028+ let args_tuple = self . alloc_expr_desugared ( Expr :: Tuple { exprs : elements } ) ;
3029+ // TODO: We don't have `super let` yet
3030+ let let_stmt1 = Statement :: Let {
3031+ pat : args_pat,
3032+ type_ref : None ,
3033+ initializer : Some ( args_tuple) ,
3034+ else_branch : None ,
3035+ } ;
3036+
3037+ // Generate:
3038+ // super let args = [
3039+ // <core::fmt::Argument>::new_display(args.0),
3040+ // <core::fmt::Argument>::new_lower_hex(args.1),
3041+ // <core::fmt::Argument>::new_debug(args.0),
3042+ // …
3043+ // ];
3044+ let args = argmap
3045+ . iter ( )
3046+ . map ( |& ( arg_index, ty) | {
3047+ let args_ident_expr =
3048+ self . alloc_expr_desugared ( Expr :: Path ( args_name. clone ( ) . into ( ) ) ) ;
3049+ let arg = self . alloc_expr_desugared ( Expr :: Field {
3050+ expr : args_ident_expr,
3051+ name : Name :: new_tuple_field ( arg_index) ,
3052+ } ) ;
3053+ self . make_argument ( arg, ty)
3054+ } )
3055+ . collect ( ) ;
3056+ let array =
3057+ self . alloc_expr_desugared ( Expr :: Array ( Array :: ElementList { elements : args } ) ) ;
3058+ let args_binding = self . alloc_binding (
3059+ args_name. clone ( ) ,
3060+ BindingAnnotation :: Unannotated ,
3061+ HygieneId :: ROOT ,
3062+ ) ;
3063+ let args_pat = self . alloc_pat_desugared ( Pat :: Bind { id : args_binding, subpat : None } ) ;
3064+ self . add_definition_to_binding ( args_binding, args_pat) ;
3065+ let let_stmt2 = Statement :: Let {
3066+ pat : args_pat,
3067+ type_ref : None ,
3068+ initializer : Some ( array) ,
3069+ else_branch : None ,
3070+ } ;
3071+ ( vec ! [ let_stmt1, let_stmt2] , self . alloc_expr_desugared ( Expr :: Path ( args_name. into ( ) ) ) )
3072+ } ;
3073+
3074+ // Generate:
3075+ // &args
3076+ let args = self . alloc_expr_desugared ( Expr :: Ref {
3077+ expr : args,
3078+ rawness : Rawness :: Ref ,
3079+ mutability : Mutability :: Shared ,
3080+ } ) ;
3081+
3082+ let call_block = {
3083+ // Generate:
3084+ // unsafe {
3085+ // <core::fmt::Arguments>::new_v1_formatted(
3086+ // lit_pieces,
3087+ // args,
3088+ // format_options,
3089+ // )
3090+ // }
3091+
3092+ let new_v1_formatted = LangItem :: FormatArguments . ty_rel_path (
3093+ self . db ,
3094+ self . module . krate ( ) ,
3095+ Name :: new_symbol_root ( sym:: new_v1_formatted) ,
3096+ ) ;
3097+ let new_v1_formatted =
3098+ self . alloc_expr_desugared ( new_v1_formatted. map_or ( Expr :: Missing , Expr :: Path ) ) ;
3099+ let args = [ lit_pieces, args, format_options] ;
3100+ let call = self
3101+ . alloc_expr_desugared ( Expr :: Call { callee : new_v1_formatted, args : args. into ( ) } ) ;
3102+
3103+ Expr :: Unsafe { id : None , statements : Box :: default ( ) , tail : Some ( call) }
3104+ } ;
3105+
3106+ if !let_stmts. is_empty ( ) {
3107+ // Generate:
3108+ // {
3109+ // super let …
3110+ // super let …
3111+ // <core::fmt::Arguments>::new_…(…)
3112+ // }
3113+ let call = self . alloc_expr_desugared ( call_block) ;
3114+ self . alloc_expr (
3115+ Expr :: Block {
3116+ id : None ,
3117+ statements : let_stmts. into ( ) ,
3118+ tail : Some ( call) ,
3119+ label : None ,
3120+ } ,
3121+ syntax_ptr,
3122+ )
3123+ } else {
3124+ self . alloc_expr ( call_block, syntax_ptr)
3125+ }
29183126 }
29193127
29203128 /// Generate a hir expression for a format_args placeholder specification.
0 commit comments