1use rustc_ast::token::{self, Delimiter, IdentIsRaw, 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 {
MetaVarExpr::Concat(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Concat",
&__self_0),
MetaVarExpr::Count(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f, "Count",
__self_0, &__self_1),
MetaVarExpr::Ignore(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Ignore",
&__self_0),
MetaVarExpr::Index(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Index",
&__self_0),
MetaVarExpr::Len(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Len",
&__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for MetaVarExpr {
#[inline]
fn eq(&self, other: &MetaVarExpr) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(MetaVarExpr::Concat(__self_0), MetaVarExpr::Concat(__arg1_0))
=> __self_0 == __arg1_0,
(MetaVarExpr::Count(__self_0, __self_1),
MetaVarExpr::Count(__arg1_0, __arg1_1)) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1,
(MetaVarExpr::Ignore(__self_0), MetaVarExpr::Ignore(__arg1_0))
=> __self_0 == __arg1_0,
(MetaVarExpr::Index(__self_0), MetaVarExpr::Index(__arg1_0))
=> __self_0 == __arg1_0,
(MetaVarExpr::Len(__self_0), MetaVarExpr::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::Concat(ref __binding_0) => { 0usize }
MetaVarExpr::Count(ref __binding_0, ref __binding_1) => {
1usize
}
MetaVarExpr::Ignore(ref __binding_0) => { 2usize }
MetaVarExpr::Index(ref __binding_0) => { 3usize }
MetaVarExpr::Len(ref __binding_0) => { 4usize }
};
::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
match *self {
MetaVarExpr::Concat(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::Concat(::rustc_serialize::Decodable::decode(__decoder))
}
1usize => {
MetaVarExpr::Count(::rustc_serialize::Decodable::decode(__decoder),
::rustc_serialize::Decodable::decode(__decoder))
}
2usize => {
MetaVarExpr::Ignore(::rustc_serialize::Decodable::decode(__decoder))
}
3usize => {
MetaVarExpr::Index(::rustc_serialize::Decodable::decode(__decoder))
}
4usize => {
MetaVarExpr::Len(::rustc_serialize::Decodable::decode(__decoder))
}
n => {
::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `MetaVarExpr`, expected 0..5, actual {0}",
n));
}
}
}
}
};Decodable)]
17pub(crate) enum MetaVarExpr {
18 Concat(Box<[MetaVarExprConcatElem]>),
20
21 Count(Ident, usize),
23
24 Ignore(Ident),
26
27 Index(usize),
30
31 Len(usize),
34}
35
36impl MetaVarExpr {
37 pub(crate) fn parse<'psess>(
39 input: &TokenStream,
40 outer_span: Span,
41 psess: &'psess ParseSess,
42 ) -> PResult<'psess, MetaVarExpr> {
43 let mut iter = input.iter();
44 let ident = parse_ident(&mut iter, psess, outer_span)?;
45 let next = iter.next();
46 let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else {
47 let (unexpected_span, insert_span) = match next {
50 Some(TokenTree::Delimited(..)) => (None, None),
51 Some(tt) => (Some(tt.span()), None),
52 None => (None, Some(ident.span.shrink_to_hi())),
53 };
54 let err = diagnostics::MveMissingParen {
55 ident_span: ident.span,
56 unexpected_span,
57 insert_span,
58 };
59 return Err(psess.dcx().create_err(err));
60 };
61
62 if iter.peek().is_some() {
64 let span = iter_span(&iter).expect("checked is_some above");
65 let err = diagnostics::MveExtraTokens {
66 span,
67 ident_span: ident.span,
68 extra_count: iter.count(),
69 ..Default::default()
70 };
71 return Err(psess.dcx().create_err(err));
72 }
73
74 let mut iter = args.iter();
75 let rslt = match ident.name {
76 sym::concat => parse_concat(&mut iter, psess, outer_span, ident.span)?,
77 sym::count => parse_count(&mut iter, psess, ident.span)?,
78 sym::ignore => {
79 eat_dollar(&mut iter, psess, ident.span)?;
80 MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?)
81 }
82 sym::index => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?),
83 sym::len => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
84 _ => {
85 let err = diagnostics::MveUnrecognizedExpr {
86 span: ident.span,
87 valid_expr_list: "`count`, `ignore`, `index`, `len`, and `concat`",
88 };
89 return Err(psess.dcx().create_err(err));
90 }
91 };
92 check_trailing_tokens(&mut iter, psess, ident)?;
93 Ok(rslt)
94 }
95
96 pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A {
97 match self {
98 MetaVarExpr::Concat(elems) => {
99 for elem in elems {
100 if let MetaVarExprConcatElem::Var(ident) = elem {
101 aux = cb(aux, ident)
102 }
103 }
104 aux
105 }
106 MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => cb(aux, ident),
107 MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => aux,
108 }
109 }
110}
111
112fn check_trailing_tokens<'psess>(
115 iter: &mut TokenStreamIter<'_>,
116 psess: &'psess ParseSess,
117 ident: Ident,
118) -> PResult<'psess, ()> {
119 if iter.peek().is_none() {
120 return Ok(());
122 }
123
124 let (min_or_exact_args, max_args) = match ident.name {
126 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"),
127 sym::ignore => (1, None),
128 sym::count => (1, Some(2)),
130 sym::index | sym::len => (0, Some(1)),
132 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}`)"),
133 };
134
135 let err = diagnostics::MveExtraTokens {
136 span: iter_span(iter).expect("checked is_none above"),
137 ident_span: ident.span,
138 extra_count: iter.count(),
139
140 exact_args_note: if max_args.is_some() { None } else { Some(()) },
141 range_args_note: if max_args.is_some() { Some(()) } else { None },
142 min_or_exact_args,
143 max_args: max_args.unwrap_or_default(),
144 name: ident.to_string(),
145 };
146 Err(psess.dcx().create_err(err))
147}
148
149fn iter_span(iter: &TokenStreamIter<'_>) -> Option<Span> {
151 let mut iter = iter.clone(); let first_sp = iter.next()?.span();
153 let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp);
154 let span = first_sp.with_hi(last_sp.hi());
155 Some(span)
156}
157
158#[derive(#[automatically_derived]
impl ::core::fmt::Debug for MetaVarExprConcatElem {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
MetaVarExprConcatElem::Ident(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Ident",
&__self_0),
MetaVarExprConcatElem::Literal(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Literal", &__self_0),
MetaVarExprConcatElem::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::cmp::PartialEq for MetaVarExprConcatElem {
#[inline]
fn eq(&self, other: &MetaVarExprConcatElem) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(MetaVarExprConcatElem::Ident(__self_0),
MetaVarExprConcatElem::Ident(__arg1_0)) =>
__self_0 == __arg1_0,
(MetaVarExprConcatElem::Literal(__self_0),
MetaVarExprConcatElem::Literal(__arg1_0)) =>
__self_0 == __arg1_0,
(MetaVarExprConcatElem::Var(__self_0),
MetaVarExprConcatElem::Var(__arg1_0)) =>
__self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq)]
161pub(crate) enum MetaVarExprConcatElem {
162 Ident(Ident),
165 Literal(Symbol),
167 Var(Ident),
170}
171
172fn parse_concat<'psess>(
174 iter: &mut TokenStreamIter<'_>,
175 psess: &'psess ParseSess,
176 outer_span: Span,
177 expr_ident_span: Span,
178) -> PResult<'psess, MetaVarExpr> {
179 let mut result = Vec::new();
180 loop {
181 let is_var = try_eat_dollar(iter);
182 let token = parse_token(iter, psess, outer_span)?;
183 let element = if is_var {
184 MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
185 } else if let TokenKind::Literal(Lit { kind: token::LitKind::Str, symbol, suffix: None }) =
186 token.kind
187 {
188 MetaVarExprConcatElem::Literal(symbol)
189 } else {
190 match parse_ident_from_token(psess, token) {
191 Err(err) => {
192 err.cancel();
193 return Err(psess
194 .dcx()
195 .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR));
196 }
197 Ok(elem) => MetaVarExprConcatElem::Ident(elem),
198 }
199 };
200 result.push(element);
201 if iter.peek().is_none() {
202 break;
203 }
204 if !try_eat_comma(iter) {
205 return Err(psess.dcx().struct_span_err(outer_span, "expected comma"));
206 }
207 }
208 if result.len() < 2 {
209 return Err(psess
210 .dcx()
211 .struct_span_err(expr_ident_span, "`concat` must have at least two elements"));
212 }
213 Ok(MetaVarExpr::Concat(result.into()))
214}
215
216fn parse_count<'psess>(
218 iter: &mut TokenStreamIter<'_>,
219 psess: &'psess ParseSess,
220 span: Span,
221) -> PResult<'psess, MetaVarExpr> {
222 eat_dollar(iter, psess, span)?;
223 let ident = parse_ident(iter, psess, span)?;
224 let depth = if try_eat_comma(iter) {
225 if iter.peek().is_none() {
226 return Err(psess.dcx().struct_span_err(
227 span,
228 "`count` followed by a comma must have an associated index indicating its depth",
229 ));
230 }
231 parse_depth(iter, psess, span)?
232 } else {
233 0
234 };
235 Ok(MetaVarExpr::Count(ident, depth))
236}
237
238fn parse_depth<'psess>(
240 iter: &mut TokenStreamIter<'_>,
241 psess: &'psess ParseSess,
242 span: Span,
243) -> PResult<'psess, usize> {
244 let Some(tt) = iter.next() else { return Ok(0) };
245 let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else {
246 return Err(psess
247 .dcx()
248 .struct_span_err(span, "meta-variable expression depth must be a literal"));
249 };
250 if let Ok(lit_kind) = LitKind::from_token_lit(*lit)
251 && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind
252 && let Ok(n_usize) = usize::try_from(n_u128.get())
253 {
254 Ok(n_usize)
255 } else {
256 let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
257 Err(psess.dcx().struct_span_err(span, msg))
258 }
259}
260
261fn parse_ident<'psess>(
263 iter: &mut TokenStreamIter<'_>,
264 psess: &'psess ParseSess,
265 fallback_span: Span,
266) -> PResult<'psess, Ident> {
267 let token = parse_token(iter, psess, fallback_span)?;
268 parse_ident_from_token(psess, token)
269}
270
271fn parse_ident_from_token<'psess>(
272 psess: &'psess ParseSess,
273 token: &Token,
274) -> PResult<'psess, Ident> {
275 if let Some((elem, is_raw)) = token.ident() {
276 if let IdentIsRaw::Yes = is_raw {
277 return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
278 }
279 return Ok(elem);
280 }
281 let token_str = pprust::token_to_string(token);
282 let mut err = psess
283 .dcx()
284 .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}`"));
285 err.span_suggestion(
286 token.span,
287 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try removing `{0}`", token_str))
})format!("try removing `{token_str}`"),
288 "",
289 Applicability::MaybeIncorrect,
290 );
291 Err(err)
292}
293
294fn parse_token<'psess, 't>(
295 iter: &mut TokenStreamIter<'t>,
296 psess: &'psess ParseSess,
297 fallback_span: Span,
298) -> PResult<'psess, &'t Token> {
299 let Some(tt) = iter.next() else {
300 return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR));
301 };
302 let TokenTree::Token(token, _) = tt else {
303 return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR));
304 };
305 Ok(token)
306}
307
308fn try_eat_comma(iter: &mut TokenStreamIter<'_>) -> bool {
311 if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.peek() {
312 let _ = iter.next();
313 return true;
314 }
315 false
316}
317
318fn try_eat_dollar(iter: &mut TokenStreamIter<'_>) -> bool {
321 if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.peek() {
322 let _ = iter.next();
323 return true;
324 }
325 false
326}
327
328fn eat_dollar<'psess>(
330 iter: &mut TokenStreamIter<'_>,
331 psess: &'psess ParseSess,
332 span: Span,
333) -> PResult<'psess, ()> {
334 if try_eat_dollar(iter) {
335 return Ok(());
336 }
337 Err(psess.dcx().struct_span_err(
338 span,
339 "meta-variables within meta-variable expressions must be referenced using a dollar sign",
340 ))
341}