@@ -7,15 +7,113 @@ use rustc_hir as hir;
77use rustc_span:: {
88 sym,
99 symbol:: { kw, Ident } ,
10- Span ,
10+ Span , Symbol ,
1111} ;
12+ use std:: borrow:: Cow ;
1213
1314impl < ' hir > LoweringContext < ' _ , ' hir > {
1415 pub ( crate ) fn lower_format_args ( & mut self , sp : Span , fmt : & FormatArgs ) -> hir:: ExprKind < ' hir > {
15- expand_format_args ( self , sp, fmt)
16+ let fmt = flatten_format_args ( fmt) ;
17+ expand_format_args ( self , sp, & fmt)
1618 }
1719}
1820
21+ /// Flattens nested `format_args!()` into one.
22+ ///
23+ /// Turns
24+ ///
25+ /// `format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3)`
26+ ///
27+ /// into
28+ ///
29+ /// `format_args!("a {} b{}! {}.", 1, 2, 3)`.
30+ fn flatten_format_args ( fmt : & FormatArgs ) -> Cow < ' _ , FormatArgs > {
31+ let mut fmt = Cow :: Borrowed ( fmt) ;
32+ let mut i = 0 ;
33+ while i < fmt. template . len ( ) {
34+ if let FormatArgsPiece :: Placeholder ( placeholder) = & fmt. template [ i]
35+ && let FormatTrait :: Display | FormatTrait :: Debug = & placeholder. format_trait
36+ && let Ok ( arg_index) = placeholder. argument . index
37+ && let arg = & fmt. arguments . all_args ( ) [ arg_index] . expr
38+ && let ExprKind :: FormatArgs ( _) = & arg. kind
39+ // Check that this argument is not used by any other placeholders.
40+ && fmt. template . iter ( ) . enumerate ( ) . all ( |( j, p) |
41+ i == j ||
42+ !matches ! ( p, FormatArgsPiece :: Placeholder ( placeholder)
43+ if placeholder. argument. index == Ok ( arg_index) )
44+ )
45+ {
46+ // Now we need to mutate the outer FormatArgs.
47+ // If this is the first time, this clones the outer FormatArgs.
48+ let fmt = fmt. to_mut ( ) ;
49+
50+ // Take the inner FormatArgs out of the outer arguments, and
51+ // replace it by the inner arguments. (We can't just put those at
52+ // the end, because we need to preserve the order of evaluation.)
53+
54+ let args = fmt. arguments . all_args_mut ( ) ;
55+ let remaining_args = args. split_off ( arg_index + 1 ) ;
56+ let old_arg_offset = args. len ( ) ;
57+ let fmt2 = args. pop ( ) . unwrap ( ) . expr . into_inner ( ) ; // The inner FormatArgs.
58+ let ExprKind :: FormatArgs ( fmt2) = fmt2. kind else { unreachable ! ( ) } ;
59+ let mut fmt2 = fmt2. into_inner ( ) ;
60+
61+ args. append ( fmt2. arguments . all_args_mut ( ) ) ;
62+ let new_arg_offset = args. len ( ) ;
63+ args. extend ( remaining_args) ;
64+
65+ // Correct the indexes that refer to the arguments after the newly inserted arguments.
66+ for piece in & mut fmt. template {
67+ if let FormatArgsPiece :: Placeholder ( placeholder) = piece
68+ && let Ok ( index) = & mut placeholder. argument . index
69+ && * index >= old_arg_offset
70+ {
71+ * index -= old_arg_offset;
72+ * index += new_arg_offset;
73+ }
74+ }
75+
76+ // Now merge the placeholders:
77+
78+ let mut rest = fmt. template . split_off ( i + 1 ) ;
79+ fmt. template . pop ( ) ; // remove the placeholder for the nested fmt args.
80+
81+ // Coalesce adjacent literals.
82+ if let Some ( FormatArgsPiece :: Literal ( s1) ) = fmt. template . last ( ) &&
83+ let Some ( FormatArgsPiece :: Literal ( s2) ) = fmt2. template . first_mut ( )
84+ {
85+ * s2 = Symbol :: intern ( & ( s1. as_str ( ) . to_owned ( ) + s2. as_str ( ) ) ) ;
86+ fmt. template . pop ( ) ;
87+ }
88+ if let Some ( FormatArgsPiece :: Literal ( s1) ) = fmt2. template . last ( ) &&
89+ let Some ( FormatArgsPiece :: Literal ( s2) ) = rest. first_mut ( )
90+ {
91+ * s2 = Symbol :: intern ( & ( s1. as_str ( ) . to_owned ( ) + s2. as_str ( ) ) ) ;
92+ fmt2. template . pop ( ) ;
93+ }
94+
95+ for piece in fmt2. template {
96+ match piece {
97+ FormatArgsPiece :: Literal ( s) => fmt. template . push ( FormatArgsPiece :: Literal ( s) ) ,
98+ FormatArgsPiece :: Placeholder ( mut p) => {
99+ // Correct the index to refer to the right place into the outer argument list.
100+ if let Ok ( n) = & mut p. argument . index {
101+ * n += arg_index;
102+ }
103+ fmt. template . push ( FormatArgsPiece :: Placeholder ( p) ) ;
104+ }
105+ }
106+ }
107+ fmt. template . extend ( rest) ;
108+
109+ // Don't increment `i` here, so we recurse into the newly added pieces.
110+ } else {
111+ i += 1 ;
112+ }
113+ }
114+ fmt
115+ }
116+
19117#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
20118enum ArgumentType {
21119 Format ( FormatTrait ) ,
0 commit comments