1
- use rustc_ast::token::{self, Delimiter, IdentIsRaw};
1
+ use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind };
2
2
use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
3
3
use rustc_ast::{LitIntType, LitKind};
4
4
use rustc_ast_pretty::pprust;
5
5
use rustc_errors::{Applicability, PResult};
6
6
use rustc_macros::{Decodable, Encodable};
7
7
use rustc_session::parse::ParseSess;
8
8
use rustc_span::symbol::Ident;
9
- use rustc_span::Span;
9
+ use rustc_span::{ Span, Symbol} ;
10
10
11
11
pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
12
+ pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
12
13
13
14
/// A meta-variable expression, for expansions based on properties of meta-variables.
14
15
#[derive(Debug, PartialEq, Encodable, Decodable)]
@@ -51,11 +52,26 @@ impl MetaVarExpr {
51
52
let mut result = Vec::new();
52
53
loop {
53
54
let is_var = try_eat_dollar(&mut iter);
54
- let element_ident = parse_ident (&mut iter, psess, outer_span)?;
55
+ let token = parse_token (&mut iter, psess, outer_span)?;
55
56
let element = if is_var {
56
- MetaVarExprConcatElem::Var(element_ident)
57
+ MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
58
+ } else if let TokenKind::Literal(Lit {
59
+ kind: token::LitKind::Str,
60
+ symbol,
61
+ suffix: None,
62
+ }) = token.kind
63
+ {
64
+ MetaVarExprConcatElem::Literal(symbol)
57
65
} else {
58
- MetaVarExprConcatElem::Ident(element_ident)
66
+ match parse_ident_from_token(psess, token) {
67
+ Err(err) => {
68
+ err.cancel();
69
+ return Err(psess
70
+ .dcx()
71
+ .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR));
72
+ }
73
+ Ok(elem) => MetaVarExprConcatElem::Ident(elem),
74
+ }
59
75
};
60
76
result.push(element);
61
77
if iter.look_ahead(0).is_none() {
@@ -105,11 +121,13 @@ impl MetaVarExpr {
105
121
106
122
#[derive(Debug, Decodable, Encodable, PartialEq)]
107
123
pub(crate) enum MetaVarExprConcatElem {
108
- /// There is NO preceding dollar sign, which means that this identifier should be interpreted
109
- /// as a literal.
124
+ /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be
125
+ /// interpreted as a literal.
110
126
Ident(Ident),
111
- /// There is a preceding dollar sign, which means that this identifier should be expanded
112
- /// and interpreted as a variable.
127
+ /// For example, a number or a string.
128
+ Literal(Symbol),
129
+ /// Identifier WITH a preceding dollar sign, which means that this identifier should be
130
+ /// expanded and interpreted as a variable.
113
131
Var(Ident),
114
132
}
115
133
@@ -158,7 +176,7 @@ fn parse_depth<'psess>(
158
176
span: Span,
159
177
) -> PResult<'psess, usize> {
160
178
let Some(tt) = iter.next() else { return Ok(0) };
161
- let TokenTree::Token(token:: Token { kind: token:: TokenKind::Literal(lit), .. }, _) = tt else {
179
+ let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else {
162
180
return Err(psess
163
181
.dcx()
164
182
.struct_span_err(span, "meta-variable expression depth must be a literal"));
@@ -180,12 +198,14 @@ fn parse_ident<'psess>(
180
198
psess: &'psess ParseSess,
181
199
fallback_span: Span,
182
200
) -> PResult<'psess, Ident> {
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
- };
201
+ let token = parse_token(iter, psess, fallback_span)?;
202
+ parse_ident_from_token(psess, token)
203
+ }
204
+
205
+ fn parse_ident_from_token<'psess>(
206
+ psess: &'psess ParseSess,
207
+ token: &Token,
208
+ ) -> PResult<'psess, Ident> {
189
209
if let Some((elem, is_raw)) = token.ident() {
190
210
if let IdentIsRaw::Yes = is_raw {
191
211
return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
@@ -205,10 +225,24 @@ fn parse_ident<'psess>(
205
225
Err(err)
206
226
}
207
227
228
+ fn parse_token<'psess, 't>(
229
+ iter: &mut RefTokenTreeCursor<'t>,
230
+ psess: &'psess ParseSess,
231
+ fallback_span: Span,
232
+ ) -> PResult<'psess, &'t Token> {
233
+ let Some(tt) = iter.next() else {
234
+ return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR));
235
+ };
236
+ let TokenTree::Token(token, _) = tt else {
237
+ return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR));
238
+ };
239
+ Ok(token)
240
+ }
241
+
208
242
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
209
243
/// iterator is not modified and the result is `false`.
210
244
fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
211
- if let Some(TokenTree::Token(token:: Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
245
+ if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
212
246
let _ = iter.next();
213
247
return true;
214
248
}
@@ -218,8 +252,7 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
218
252
/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
219
253
/// iterator is not modified and the result is `false`.
220
254
fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
221
- if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
222
- {
255
+ if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
223
256
let _ = iter.next();
224
257
return true;
225
258
}
@@ -232,8 +265,7 @@ fn eat_dollar<'psess>(
232
265
psess: &'psess ParseSess,
233
266
span: Span,
234
267
) -> PResult<'psess, ()> {
235
- if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
236
- {
268
+ if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
237
269
let _ = iter.next();
238
270
return Ok(());
239
271
}
0 commit comments