@@ -9,8 +9,11 @@ use rustc_span::symbol::Ident;
9
9
use rustc_span:: Span ;
10
10
11
11
/// A meta-variable expression, for expansions based on properties of meta-variables.
12
- #[ derive( Debug , Clone , PartialEq , Encodable , Decodable ) ]
12
+ #[ derive( Debug , PartialEq , Encodable , Decodable ) ]
13
13
pub ( crate ) enum MetaVarExpr {
14
+ /// Unification of two or more identifiers.
15
+ Concat ( Box < [ MetaVarExprConcatElem ] > ) ,
16
+
14
17
/// The number of repetitions of an identifier.
15
18
Count ( Ident , usize ) ,
16
19
@@ -42,6 +45,31 @@ impl MetaVarExpr {
42
45
check_trailing_token ( & mut tts, psess) ?;
43
46
let mut iter = args. trees ( ) ;
44
47
let rslt = match ident. as_str ( ) {
48
+ "concat" => {
49
+ let mut result = Vec :: new ( ) ;
50
+ loop {
51
+ let is_var = try_eat_dollar ( & mut iter) ;
52
+ let element_ident = parse_ident ( & mut iter, psess, outer_span) ?;
53
+ let element = if is_var {
54
+ MetaVarExprConcatElem :: Var ( element_ident)
55
+ } else {
56
+ MetaVarExprConcatElem :: Ident ( element_ident)
57
+ } ;
58
+ result. push ( element) ;
59
+ if iter. look_ahead ( 0 ) . is_none ( ) {
60
+ break ;
61
+ }
62
+ if !try_eat_comma ( & mut iter) {
63
+ return Err ( psess. dcx . struct_span_err ( outer_span, "expected comma" ) ) ;
64
+ }
65
+ }
66
+ if result. len ( ) < 2 {
67
+ return Err ( psess
68
+ . dcx
69
+ . struct_span_err ( ident. span , "`concat` must have at least two elements" ) ) ;
70
+ }
71
+ MetaVarExpr :: Concat ( result. into ( ) )
72
+ }
45
73
"count" => parse_count ( & mut iter, psess, ident. span ) ?,
46
74
"ignore" => {
47
75
eat_dollar ( & mut iter, psess, ident. span ) ?;
@@ -68,11 +96,21 @@ impl MetaVarExpr {
68
96
pub ( crate ) fn ident ( & self ) -> Option < Ident > {
69
97
match * self {
70
98
MetaVarExpr :: Count ( ident, _) | MetaVarExpr :: Ignore ( ident) => Some ( ident) ,
71
- MetaVarExpr :: Index ( ..) | MetaVarExpr :: Len ( ..) => None ,
99
+ MetaVarExpr :: Concat { .. } | MetaVarExpr :: Index ( ..) | MetaVarExpr :: Len ( ..) => None ,
72
100
}
73
101
}
74
102
}
75
103
104
+ #[ derive( Debug , Decodable , Encodable , PartialEq ) ]
105
+ pub ( crate ) enum MetaVarExprConcatElem {
106
+ /// There is NO preceding dollar sign, which means that this identifier should be interpreted
107
+ /// as a literal.
108
+ Ident ( Ident ) ,
109
+ /// There is a preceding dollar sign, which means that this identifier should be expanded
110
+ /// and interpreted as a variable.
111
+ Var ( Ident ) ,
112
+ }
113
+
76
114
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
77
115
fn check_trailing_token < ' psess > (
78
116
iter : & mut RefTokenTreeCursor < ' _ > ,
@@ -138,26 +176,27 @@ fn parse_depth<'psess>(
138
176
fn parse_ident < ' psess > (
139
177
iter : & mut RefTokenTreeCursor < ' _ > ,
140
178
psess : & ' psess ParseSess ,
141
- span : Span ,
179
+ fallback_span : Span ,
142
180
) -> PResult < ' psess , Ident > {
143
- if let Some ( tt) = iter. next ( )
144
- && let TokenTree :: Token ( token, _) = tt
145
- {
146
- if let Some ( ( elem, IdentIsRaw :: No ) ) = token. ident ( ) {
147
- return Ok ( elem) ;
148
- }
149
- let token_str = pprust:: token_to_string ( token) ;
150
- let mut err =
151
- psess. dcx . struct_span_err ( span, format ! ( "expected identifier, found `{}`" , & token_str) ) ;
152
- err. span_suggestion (
153
- token. span ,
154
- format ! ( "try removing `{}`" , & token_str) ,
155
- "" ,
156
- Applicability :: MaybeIncorrect ,
157
- ) ;
158
- return Err ( err) ;
181
+ let Some ( tt) = iter. next ( ) else {
182
+ return Err ( psess. dcx . struct_span_err ( fallback_span, "expected identifier" ) ) ;
183
+ } ;
184
+ let TokenTree :: Token ( token, _) = tt else {
185
+ return Err ( psess. dcx . struct_span_err ( tt. span ( ) , "expected identifier" ) ) ;
186
+ } ;
187
+ if let Some ( ( elem, IdentIsRaw :: No ) ) = token. ident ( ) {
188
+ return Ok ( elem) ;
159
189
}
160
- Err ( psess. dcx . struct_span_err ( span, "expected identifier" ) )
190
+ let token_str = pprust:: token_to_string ( token) ;
191
+ let mut err =
192
+ psess. dcx . struct_span_err ( token. span , format ! ( "expected identifier, found `{token_str}`" ) ) ;
193
+ err. span_suggestion (
194
+ token. span ,
195
+ format ! ( "try removing `{token_str}`" ) ,
196
+ "" ,
197
+ Applicability :: MaybeIncorrect ,
198
+ ) ;
199
+ Err ( err)
161
200
}
162
201
163
202
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
@@ -170,6 +209,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
170
209
false
171
210
}
172
211
212
+ /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
213
+ /// iterator is not modified and the result is `false`.
214
+ fn try_eat_dollar ( iter : & mut RefTokenTreeCursor < ' _ > ) -> bool {
215
+ if let Some ( TokenTree :: Token ( token:: Token { kind : token:: Dollar , .. } , _) ) = iter. look_ahead ( 0 )
216
+ {
217
+ let _ = iter. next ( ) ;
218
+ return true ;
219
+ }
220
+ false
221
+ }
222
+
173
223
/// Expects that the next item is a dollar sign.
174
224
fn eat_dollar < ' psess > (
175
225
iter : & mut RefTokenTreeCursor < ' _ > ,
0 commit comments