@@ -7,15 +7,113 @@ use rustc_hir as hir;
7
7
use rustc_span:: {
8
8
sym,
9
9
symbol:: { kw, Ident } ,
10
- Span ,
10
+ Span , Symbol ,
11
11
} ;
12
+ use std:: borrow:: Cow ;
12
13
13
14
impl < ' hir > LoweringContext < ' _ , ' hir > {
14
15
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)
16
18
}
17
19
}
18
20
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
+
19
117
#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
20
118
enum ArgumentType {
21
119
Format ( FormatTrait ) ,
0 commit comments