1use rustc_ast::token::{self, Delimiter, IdentKind, Lit, Token, TokenKind};
2use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree};
3use rustc_ast::{LitIntType, LitKind};
4use rustc_ast_pretty::pprust;
5use rustc_errors::{Applicability, PResult};
6use rustc_macros::{Decodable, Encodable};
7use rustc_session::parse::ParseSess;
8use rustc_span::{Ident, Span, Symbol, sym};
9
10use crate::diagnostics;
11
12pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
13
14#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaVarExpr {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::ConcatIdent(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConcatIdent", &__self_0),
Self::ConcatStr(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ConcatStr", &__self_0),
Self::Count(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f, "Count",
__self_0, &__self_1),
Self::Ignore(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Ignore",
&__self_0),
Self::Index(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Index",
&__self_0),
Self::Len(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Len",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MetaVarExpr { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MetaVarExpr {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::ConcatIdent(__self_0), Self::ConcatIdent(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::ConcatStr(__self_0), Self::ConcatStr(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Count(__self_0, __self_1),
Self::Count(__arg1_0, __arg1_1)) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1,
(Self::Ignore(__self_0), Self::Ignore(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Index(__self_0), Self::Index(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Len(__self_0), Self::Len(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for MetaVarExpr {
fn encode(&self, __encoder: &mut __E) {
let disc =
match *self {
MetaVarExpr::ConcatIdent(ref __binding_0) => { 0usize }
MetaVarExpr::ConcatStr(ref __binding_0) => { 1usize }
MetaVarExpr::Count(ref __binding_0, ref __binding_1) => {
2usize
}
MetaVarExpr::Ignore(ref __binding_0) => { 3usize }
MetaVarExpr::Index(ref __binding_0) => { 4usize }
MetaVarExpr::Len(ref __binding_0) => { 5usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
match *self {
MetaVarExpr::ConcatIdent(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExpr::ConcatStr(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExpr::Count(ref __binding_0, ref __binding_1) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
::rustc_serialize::Encodable::<__E>::encode(__binding_1,
__encoder);
}
MetaVarExpr::Ignore(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExpr::Index(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExpr::Len(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
}
}
}
};Encodable, const _: () =
{
impl<__D: ::rustc_span::SpanDecoder> ::rustc_serialize::Decodable<__D>
for MetaVarExpr {
fn decode(__decoder: &mut __D) -> Self {
match ::rustc_serialize::Decoder::read_u8(__decoder) as usize
{
0usize => {
MetaVarExpr::ConcatIdent(::rustc_serialize::Decodable::decode(__decoder))
}
1usize => {
MetaVarExpr::ConcatStr(::rustc_serialize::Decodable::decode(__decoder))
}
2usize => {
MetaVarExpr::Count(::rustc_serialize::Decodable::decode(__decoder),
::rustc_serialize::Decodable::decode(__decoder))
}
3usize => {
MetaVarExpr::Ignore(::rustc_serialize::Decodable::decode(__decoder))
}
4usize => {
MetaVarExpr::Index(::rustc_serialize::Decodable::decode(__decoder))
}
5usize => {
MetaVarExpr::Len(::rustc_serialize::Decodable::decode(__decoder))
}
n => {
::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `MetaVarExpr`, expected 0..6, actual {0}",
n));
}
}
}
}
};Decodable)]
16pub(crate) enum MetaVarExpr {
17 ConcatIdent(Box<[MetaVarExprConcatElem]>),
19
20 ConcatStr(Box<[MetaVarExprConcatElem]>),
22
23 Count(Ident, usize),
25
26 Ignore(Ident),
28
29 Index(usize),
32
33 Len(usize),
36}
37
38impl MetaVarExpr {
39 pub(crate) fn parse<'psess>(
41 input: &TokenStream,
42 outer_span: Span,
43 psess: &'psess ParseSess,
44 ) -> PResult<'psess, MetaVarExpr> {
45 let mut iter = input.iter();
46 let ident = parse_ident(&mut iter, psess, outer_span)?;
47 let next = iter.next();
48 let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else {
49 let (unexpected_span, insert_span) = match next {
52 Some(TokenTree::Delimited(..)) => (None, None),
53 Some(tt) => (Some(tt.span()), None),
54 None => (None, Some(ident.span.shrink_to_hi())),
55 };
56 let err = diagnostics::MveMissingParen {
57 ident_span: ident.span,
58 unexpected_span,
59 insert_span,
60 };
61 return Err(psess.dcx().create_err(err));
62 };
63
64 if iter.peek().is_some() {
66 let span = iter_span(&iter).expect("checked is_some above");
67 let err = diagnostics::MveExtraTokens {
68 span,
69 ident_span: ident.span,
70 extra_count: iter.count(),
71 ..Default::default()
72 };
73 return Err(psess.dcx().create_err(err));
74 }
75
76 let mut iter = args.iter();
77 let rslt = match ident.name {
78 sym::concat => {
79 MetaVarExpr::ConcatIdent(parse_concat(&mut iter, psess, outer_span, ident.span)?)
80 }
81 sym::concat_str => {
82 MetaVarExpr::ConcatStr(parse_concat(&mut iter, psess, outer_span, ident.span)?)
83 }
84 sym::count => parse_count(&mut iter, psess, ident.span)?,
85 sym::ignore => {
86 eat_dollar(&mut iter, psess, ident.span)?;
87 MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?)
88 }
89 sym::index => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?),
90 sym::len => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
91 _ => {
92 let err = diagnostics::MveUnrecognizedExpr {
93 span: ident.span,
94 valid_expr_list: "`count`, `ignore`, `index`, `len`, and `concat`",
95 };
96 return Err(psess.dcx().create_err(err));
97 }
98 };
99 check_trailing_tokens(&mut iter, psess, ident)?;
100 Ok(rslt)
101 }
102
103 pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A {
104 match self {
105 MetaVarExpr::ConcatIdent(elems) | MetaVarExpr::ConcatStr(elems) => {
106 for elem in elems {
107 if let MetaVarExprConcatElem::Var(ident) = elem {
108 aux = cb(aux, ident)
109 }
110 }
111 aux
112 }
113 MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => cb(aux, ident),
114 MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => aux,
115 }
116 }
117}
118
119fn check_trailing_tokens<'psess>(
122 iter: &mut TokenStreamIter<'_>,
123 psess: &'psess ParseSess,
124 ident: Ident,
125) -> PResult<'psess, ()> {
126 if iter.peek().is_none() {
127 return Ok(());
129 }
130
131 let (min_or_exact_args, max_args) = match ident.name {
133 sym::concat => {
::core::panicking::panic_fmt(format_args!("concat takes unlimited tokens but didn\'t eat them all"));
}panic!("concat takes unlimited tokens but didn't eat them all"),
134 sym::ignore => (1, None),
135 sym::count => (1, Some(2)),
137 sym::index | sym::len => (0, Some(1)),
139 other => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("unknown MVEs should be rejected earlier (got `{0}`)",
other)));
}unreachable!("unknown MVEs should be rejected earlier (got `{other}`)"),
140 };
141
142 let err = diagnostics::MveExtraTokens {
143 span: iter_span(iter).expect("checked is_none above"),
144 ident_span: ident.span,
145 extra_count: iter.count(),
146
147 exact_args_note: if max_args.is_some() { None } else { Some(()) },
148 range_args_note: if max_args.is_some() { Some(()) } else { None },
149 min_or_exact_args,
150 max_args: max_args.unwrap_or_default(),
151 name: ident.to_string(),
152 };
153 Err(psess.dcx().create_err(err))
154}
155
156fn iter_span(iter: &TokenStreamIter<'_>) -> Option<Span> {
158 let mut iter = iter.clone(); let first_sp = iter.next()?.span();
160 let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp);
161 let span = first_sp.with_hi(last_sp.hi());
162 Some(span)
163}
164
165#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaVarExprConcatElem {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Ident(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Ident",
&__self_0),
Self::Literal(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Literal", &__self_0),
Self::Var(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Var",
&__self_0),
}
}
}Debug, const _: () =
{
impl<__D: ::rustc_span::SpanDecoder> ::rustc_serialize::Decodable<__D>
for MetaVarExprConcatElem {
fn decode(__decoder: &mut __D) -> Self {
match ::rustc_serialize::Decoder::read_u8(__decoder) as usize
{
0usize => {
MetaVarExprConcatElem::Ident(::rustc_serialize::Decodable::decode(__decoder))
}
1usize => {
MetaVarExprConcatElem::Literal(::rustc_serialize::Decodable::decode(__decoder))
}
2usize => {
MetaVarExprConcatElem::Var(::rustc_serialize::Decodable::decode(__decoder))
}
n => {
::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `MetaVarExprConcatElem`, expected 0..3, actual {0}",
n));
}
}
}
}
};Decodable, const _: () =
{
impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
for MetaVarExprConcatElem {
fn encode(&self, __encoder: &mut __E) {
let disc =
match *self {
MetaVarExprConcatElem::Ident(ref __binding_0) => { 0usize }
MetaVarExprConcatElem::Literal(ref __binding_0) => {
1usize
}
MetaVarExprConcatElem::Var(ref __binding_0) => { 2usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
match *self {
MetaVarExprConcatElem::Ident(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExprConcatElem::Literal(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
MetaVarExprConcatElem::Var(ref __binding_0) => {
::rustc_serialize::Encodable::<__E>::encode(__binding_0,
__encoder);
}
}
}
}
};Encodable, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MetaVarExprConcatElem { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MetaVarExprConcatElem {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Ident(__self_0), Self::Ident(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Literal(__self_0), Self::Literal(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::Var(__self_0), Self::Var(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq)]
168pub(crate) enum MetaVarExprConcatElem {
169 Ident(Ident),
172 Literal(Symbol),
174 Var(Ident),
177}
178
179fn parse_concat<'psess>(
181 iter: &mut TokenStreamIter<'_>,
182 psess: &'psess ParseSess,
183 outer_span: Span,
184 expr_ident_span: Span,
185) -> PResult<'psess, Box<[MetaVarExprConcatElem]>> {
186 let mut result = Vec::new();
187 loop {
188 let is_var = try_eat_dollar(iter);
189 let token = parse_token(iter, psess, outer_span)?;
190 let element = if is_var {
191 MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
192 } else if let TokenKind::Literal(Lit { kind: token::LitKind::Str, symbol, suffix: None }) =
193 token.kind
194 {
195 MetaVarExprConcatElem::Literal(symbol)
196 } else {
197 match parse_ident_from_token(psess, token) {
198 Err(err) => {
199 err.cancel();
204 return Err(psess
205 .dcx()
206 .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR));
207 }
208 Ok(elem) => MetaVarExprConcatElem::Ident(elem),
209 }
210 };
211 result.push(element);
212 if iter.peek().is_none() {
213 break;
214 }
215 if !try_eat_comma(iter) {
216 return Err(psess.dcx().struct_span_err(outer_span, "expected comma"));
217 }
218 }
219 if result.len() < 2 {
220 return Err(psess
221 .dcx()
222 .struct_span_err(expr_ident_span, "`concat` must have at least two elements"));
223 }
224 Ok(result.into())
225}
226
227fn parse_count<'psess>(
229 iter: &mut TokenStreamIter<'_>,
230 psess: &'psess ParseSess,
231 span: Span,
232) -> PResult<'psess, MetaVarExpr> {
233 eat_dollar(iter, psess, span)?;
234 let ident = parse_ident(iter, psess, span)?;
235 let depth = if try_eat_comma(iter) {
236 if iter.peek().is_none() {
237 return Err(psess.dcx().struct_span_err(
238 span,
239 "`count` followed by a comma must have an associated index indicating its depth",
240 ));
241 }
242 parse_depth(iter, psess, span)?
243 } else {
244 0
245 };
246 Ok(MetaVarExpr::Count(ident, depth))
247}
248
249fn parse_depth<'psess>(
251 iter: &mut TokenStreamIter<'_>,
252 psess: &'psess ParseSess,
253 span: Span,
254) -> PResult<'psess, usize> {
255 let Some(tt) = iter.next() else { return Ok(0) };
256 let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else {
257 return Err(psess
258 .dcx()
259 .struct_span_err(span, "meta-variable expression depth must be a literal"));
260 };
261 if let Ok(lit_kind) = LitKind::from_token_lit(*lit)
262 && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind
263 && let Ok(n_usize) = usize::try_from(n_u128.get())
264 {
265 Ok(n_usize)
266 } else {
267 let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
268 Err(psess.dcx().struct_span_err(span, msg))
269 }
270}
271
272fn parse_ident<'psess>(
274 iter: &mut TokenStreamIter<'_>,
275 psess: &'psess ParseSess,
276 fallback_span: Span,
277) -> PResult<'psess, Ident> {
278 let token = parse_token(iter, psess, fallback_span)?;
279 parse_ident_from_token(psess, token)
280}
281
282fn parse_ident_from_token<'psess>(
283 psess: &'psess ParseSess,
284 token: &Token,
285) -> PResult<'psess, Ident> {
286 if let Some((elem, kind)) = token.ident() {
287 validate_ident_kind(psess.dcx(), kind, elem.span)?;
288 return Ok(elem);
289 }
290 let token_str = pprust::token_to_string(token);
291 let mut err = psess
292 .dcx()
293 .struct_span_err(token.span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected identifier, found `{0}`",
token_str))
})format!("expected identifier, found `{token_str}`"));
294 err.span_suggestion_short(
295 token.span,
296 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try removing `{0}`", token_str))
})format!("try removing `{token_str}`"),
297 "",
298 Applicability::MaybeIncorrect,
299 );
300 Err(err)
301}
302
303fn parse_token<'psess, 't>(
304 iter: &mut TokenStreamIter<'t>,
305 psess: &'psess ParseSess,
306 fallback_span: Span,
307) -> PResult<'psess, &'t Token> {
308 let Some(tt) = iter.next() else {
309 return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR));
310 };
311 let TokenTree::Token(token, _) = tt else {
312 return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR));
313 };
314 Ok(token)
315}
316
317fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool {
320 if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() {
321 let _ = iter.next();
322 return true;
323 }
324 false
325}
326
327fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool {
330 if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() {
331 let _ = iter.next();
332 return true;
333 }
334 false
335}
336
337fn eat_dollar<'psess>(
339 iter: &mut TokenStreamIter<'_>,
340 psess: &'psess ParseSess,
341 span: Span,
342) -> PResult<'psess, ()> {
343 if try_eat_dollar(iter) {
344 return Ok(());
345 }
346 Err(psess.dcx().struct_span_err(
347 span,
348 "meta-variables within meta-variable expressions must be referenced using a dollar sign",
349 ))
350}
351
352pub(crate) fn validate_ident_kind<'a>(
353 dcx: rustc_errors::DiagCtxtHandle<'a>,
354 kind: IdentKind,
355 span: Span,
356) -> PResult<'a, ()> {
357 let kind = match kind {
358 IdentKind::Normal => return Ok(()),
359 IdentKind::Raw => "raw identifiers",
360 IdentKind::ForcedKeyword => "forced keywords",
361 };
362 Err(dcx.struct_span_err(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`${{concat(..)}}` currently does not support {0}",
kind))
})format!("`${{concat(..)}}` currently does not support {kind}")))
363}