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