@@ -2815,6 +2815,44 @@ impl ExprCollector<'_> {
2815
2815
mutability : Mutability :: Shared ,
2816
2816
} )
2817
2817
} ;
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 {
2818
2856
let arguments = & * fmt. arguments . arguments ;
2819
2857
2820
2858
let args = if arguments. is_empty ( ) {
@@ -2902,19 +2940,189 @@ impl ExprCollector<'_> {
2902
2940
} ) ;
2903
2941
}
2904
2942
2905
- let idx = self . alloc_expr (
2943
+ self . alloc_expr (
2906
2944
Expr :: Call {
2907
2945
callee : new_v1_formatted,
2908
2946
args : Box :: new ( [ lit_pieces, args, format_options, unsafe_arg_new] ) ,
2909
2947
} ,
2910
2948
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
+ }
2918
3126
}
2919
3127
2920
3128
/// Generate a hir expression for a format_args placeholder specification.
0 commit comments