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 RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
13pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
14
15#[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)]
17pub(crate) enum MetaVarExpr {
18 ConcatIdent(Box<[MetaVarExprConcatElem]>),
20
21 ConcatStr(Box<[MetaVarExprConcatElem]>),
23
24 Count(Ident, usize),
26
27 Ignore(Ident),
29
30 Index(usize),
33
34 Len(usize),
37}
38
39impl MetaVarExpr {
40 pub(crate) fn parse<'psess>(
42 input: &TokenStream,
43 outer_span: Span,
44 psess: &'psess ParseSess,
45 ) -> PResult<'psess, MetaVarExpr> {
46 let mut iter = input.iter();
47 let ident = parse_ident(&mut iter, psess, outer_span)?;
48 let next = iter.next();
49 let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else {
50 let (unexpected_span, insert_span) = match next {
53 Some(TokenTree::Delimited(..)) => (None, None),
54 Some(tt) => (Some(tt.span()), None),
55 None => (None, Some(ident.span.shrink_to_hi())),
56 };
57 let err = diagnostics::MveMissingParen {
58 ident_span: ident.span,
59 unexpected_span,
60 insert_span,
61 };
62 return Err(psess.dcx().create_err(err));
63 };
64
65 if iter.peek().is_some() {
67 let span = iter_span(&iter).expect("checked is_some above");
68 let err = diagnostics::MveExtraTokens {
69 span,
70 ident_span: ident.span,
71 extra_count: iter.count(),
72 ..Default::default()
73 };
74 return Err(psess.dcx().create_err(err));
75 }
76
77 let mut iter = args.iter();
78 let rslt = match ident.name {
79 sym::concat => {
80 MetaVarExpr::ConcatIdent(parse_concat(&mut iter, psess, outer_span, ident.span)?)
81 }
82 sym::concat_str => {
83 MetaVarExpr::ConcatStr(parse_concat(&mut iter, psess, outer_span, ident.span)?)
84 }
85 sym::count => parse_count(&mut iter, psess, ident.span)?,
86 sym::ignore => {
87 eat_dollar(&mut iter, psess, ident.span)?;
88 MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?)
89 }
90 sym::index => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?),
91 sym::len => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
92 _ => {
93 let err = diagnostics::MveUnrecognizedExpr {
94 span: ident.span,
95 valid_expr_list: "`count`, `ignore`, `index`, `len`, and `concat`",
96 };
97 return Err(psess.dcx().create_err(err));
98 }
99 };
100 check_trailing_tokens(&mut iter, psess, ident)?;
101 Ok(rslt)
102 }
103
104 pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A {
105 match self {
106 MetaVarExpr::ConcatIdent(elems) | MetaVarExpr::ConcatStr(elems) => {
107 for elem in elems {
108 if let MetaVarExprConcatElem::Var(ident) = elem {
109 aux = cb(aux, ident)
110 }
111 }
112 aux
113 }
114 MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => cb(aux, ident),
115 MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => aux,
116 }
117 }
118}
119
120fn check_trailing_tokens<'psess>(
123 iter: &mut TokenStreamIter<'_>,
124 psess: &'psess ParseSess,
125 ident: Ident,
126) -> PResult<'psess, ()> {
127 if iter.peek().is_none() {
128 return Ok(());
130 }
131
132 let (min_or_exact_args, max_args) = match ident.name {
134 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"),
135 sym::ignore => (1, None),
136 sym::count => (1, Some(2)),
138 sym::index | sym::len => (0, Some(1)),
140 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}`)"),
141 };
142
143 let err = diagnostics::MveExtraTokens {
144 span: iter_span(iter).expect("checked is_none above"),
145 ident_span: ident.span,
146 extra_count: iter.count(),
147
148 exact_args_note: if max_args.is_some() { None } else { Some(()) },
149 range_args_note: if max_args.is_some() { Some(()) } else { None },
150 min_or_exact_args,
151 max_args: max_args.unwrap_or_default(),
152 name: ident.to_string(),
153 };
154 Err(psess.dcx().create_err(err))
155}
156
157fn iter_span(iter: &TokenStreamIter<'_>) -> Option<Span> {
159 let mut iter = iter.clone(); let first_sp = iter.next()?.span();
161 let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp);
162 let span = first_sp.with_hi(last_sp.hi());
163 Some(span)
164}
165
166#[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)]
169pub(crate) enum MetaVarExprConcatElem {
170 Ident(Ident),
173 Literal(Symbol),
175 Var(Ident),
178}
179
180fn parse_concat<'psess>(
182 iter: &mut TokenStreamIter<'_>,
183 psess: &'psess ParseSess,
184 outer_span: Span,
185 expr_ident_span: Span,
186) -> PResult<'psess, Box<[MetaVarExprConcatElem]>> {
187 let mut result = Vec::new();
188 loop {
189 let is_var = try_eat_dollar(iter);
190 let token = parse_token(iter, psess, outer_span)?;
191 let element = if is_var {
192 MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
193 } else if let TokenKind::Literal(Lit { kind: token::LitKind::Str, symbol, suffix: None }) =
194 token.kind
195 {
196 MetaVarExprConcatElem::Literal(symbol)
197 } else {
198 match parse_ident_from_token(psess, token) {
199 Err(err) => {
200 err.cancel();
201 return Err(psess
202 .dcx()
203 .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR));
204 }
205 Ok(elem) => MetaVarExprConcatElem::Ident(elem),
206 }
207 };
208 result.push(element);
209 if iter.peek().is_none() {
210 break;
211 }
212 if !try_eat_comma(iter) {
213 return Err(psess.dcx().struct_span_err(outer_span, "expected comma"));
214 }
215 }
216 if result.len() < 2 {
217 return Err(psess
218 .dcx()
219 .struct_span_err(expr_ident_span, "`concat` must have at least two elements"));
220 }
221 Ok(result.into())
222}
223
224fn parse_count<'psess>(
226 iter: &mut TokenStreamIter<'_>,
227 psess: &'psess ParseSess,
228 span: Span,
229) -> PResult<'psess, MetaVarExpr> {
230 eat_dollar(iter, psess, span)?;
231 let ident = parse_ident(iter, psess, span)?;
232 let depth = if try_eat_comma(iter) {
233 if iter.peek().is_none() {
234 return Err(psess.dcx().struct_span_err(
235 span,
236 "`count` followed by a comma must have an associated index indicating its depth",
237 ));
238 }
239 parse_depth(iter, psess, span)?
240 } else {
241 0
242 };
243 Ok(MetaVarExpr::Count(ident, depth))
244}
245
246fn parse_depth<'psess>(
248 iter: &mut TokenStreamIter<'_>,
249 psess: &'psess ParseSess,
250 span: Span,
251) -> PResult<'psess, usize> {
252 let Some(tt) = iter.next() else { return Ok(0) };
253 let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else {
254 return Err(psess
255 .dcx()
256 .struct_span_err(span, "meta-variable expression depth must be a literal"));
257 };
258 if let Ok(lit_kind) = LitKind::from_token_lit(*lit)
259 && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind
260 && let Ok(n_usize) = usize::try_from(n_u128.get())
261 {
262 Ok(n_usize)
263 } else {
264 let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
265 Err(psess.dcx().struct_span_err(span, msg))
266 }
267}
268
269fn parse_ident<'psess>(
271 iter: &mut TokenStreamIter<'_>,
272 psess: &'psess ParseSess,
273 fallback_span: Span,
274) -> PResult<'psess, Ident> {
275 let token = parse_token(iter, psess, fallback_span)?;
276 parse_ident_from_token(psess, token)
277}
278
279fn parse_ident_from_token<'psess>(
280 psess: &'psess ParseSess,
281 token: &Token,
282) -> PResult<'psess, Ident> {
283 if let Some((elem, kind)) = token.ident() {
284 if let IdentKind::Raw = kind {
285 return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
286 }
287 return Ok(elem);
288 }
289 let token_str = pprust::token_to_string(token);
290 let mut err = psess
291 .dcx()
292 .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}`"));
293 err.span_suggestion_short(
294 token.span,
295 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try removing `{0}`", token_str))
})format!("try removing `{token_str}`"),
296 "",
297 Applicability::MaybeIncorrect,
298 );
299 Err(err)
300}
301
302fn parse_token<'psess, 't>(
303 iter: &mut TokenStreamIter<'t>,
304 psess: &'psess ParseSess,
305 fallback_span: Span,
306) -> PResult<'psess, &'t Token> {
307 let Some(tt) = iter.next() else {
308 return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR));
309 };
310 let TokenTree::Token(token, _) = tt else {
311 return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR));
312 };
313 Ok(token)
314}
315
316fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool {
319 if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() {
320 let _ = iter.next();
321 return true;
322 }
323 false
324}
325
326fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool {
329 if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() {
330 let _ = iter.next();
331 return true;
332 }
333 false
334}
335
336fn eat_dollar<'psess>(
338 iter: &mut TokenStreamIter<'_>,
339 psess: &'psess ParseSess,
340 span: Span,
341) -> PResult<'psess, ()> {
342 if try_eat_dollar(iter) {
343 return Ok(());
344 }
345 Err(psess.dcx().struct_span_err(
346 span,
347 "meta-variables within meta-variable expressions must be referenced using a dollar sign",
348 ))
349}