1 use rustc_ast
::token
::{self, Delimiter}
;
2 use rustc_ast
::tokenstream
::{CursorRef, TokenStream, TokenTree}
;
3 use rustc_ast
::{LitIntType, LitKind}
;
4 use rustc_ast_pretty
::pprust
;
5 use rustc_errors
::{Applicability, PResult}
;
6 use rustc_session
::parse
::ParseSess
;
7 use rustc_span
::symbol
::Ident
;
10 /// A meta-variable expression, for expansions based on properties of meta-variables.
11 #[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
12 pub(crate) enum MetaVarExpr
{
13 /// The number of repetitions of an identifier, optionally limited to a number
14 /// of outer-most repetition depths. If the depth limit is `None` then the depth is unlimited.
15 Count(Ident
, Option
<usize>),
17 /// Ignore a meta-variable for repetition without expansion.
20 /// The index of the repetition at a particular depth, where 0 is the inner-most
21 /// repetition. The `usize` is the depth.
24 /// The length of the repetition at a particular depth, where 0 is the inner-most
25 /// repetition. The `usize` is the depth.
30 /// Attempt to parse a meta-variable expression from a token stream.
31 pub(crate) fn parse
<'sess
>(
34 sess
: &'sess ParseSess
,
35 ) -> PResult
<'sess
, MetaVarExpr
> {
36 let mut tts
= input
.trees();
37 let ident
= parse_ident(&mut tts
, sess
, outer_span
)?
;
38 let Some(TokenTree
::Delimited(_
, Delimiter
::Parenthesis
, args
)) = tts
.next() else {
39 let msg
= "meta-variable expression parameter must be wrapped in parentheses";
40 return Err(sess
.span_diagnostic
.struct_span_err(ident
.span
, msg
));
42 check_trailing_token(&mut tts
, sess
)?
;
43 let mut iter
= args
.trees();
44 let rslt
= match &*ident
.as_str() {
45 "count" => parse_count(&mut iter
, sess
, ident
.span
)?
,
46 "ignore" => MetaVarExpr
::Ignore(parse_ident(&mut iter
, sess
, ident
.span
)?
),
47 "index" => MetaVarExpr
::Index(parse_depth(&mut iter
, sess
, ident
.span
)?
),
48 "length" => MetaVarExpr
::Length(parse_depth(&mut iter
, sess
, ident
.span
)?
),
50 let err_msg
= "unrecognized meta-variable expression";
51 let mut err
= sess
.span_diagnostic
.struct_span_err(ident
.span
, err_msg
);
54 "supported expressions are count, ignore, index and length",
56 Applicability
::MachineApplicable
,
61 check_trailing_token(&mut iter
, sess
)?
;
65 pub(crate) fn ident(&self) -> Option
<Ident
> {
67 MetaVarExpr
::Count(ident
, _
) | MetaVarExpr
::Ignore(ident
) => Some(ident
),
68 MetaVarExpr
::Index(..) | MetaVarExpr
::Length(..) => None
,
73 // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
74 fn check_trailing_token
<'sess
>(
75 iter
: &mut CursorRef
<'_
>,
76 sess
: &'sess ParseSess
,
77 ) -> PResult
<'sess
, ()> {
78 if let Some(tt
) = iter
.next() {
81 .struct_span_err(tt
.span(), &format
!("unexpected token: {}", pprust
::tt_to_string(tt
)));
82 diag
.span_note(tt
.span(), "meta-variable expression must not have trailing tokens");
89 /// Parse a meta-variable `count` expression: `count(ident[, depth])`
90 fn parse_count
<'sess
>(
91 iter
: &mut CursorRef
<'_
>,
92 sess
: &'sess ParseSess
,
94 ) -> PResult
<'sess
, MetaVarExpr
> {
95 let ident
= parse_ident(iter
, sess
, span
)?
;
96 let depth
= if try_eat_comma(iter
) { Some(parse_depth(iter, sess, span)?) }
else { None }
;
97 Ok(MetaVarExpr
::Count(ident
, depth
))
100 /// Parses the depth used by index(depth) and length(depth).
101 fn parse_depth
<'sess
>(
102 iter
: &mut CursorRef
<'_
>,
103 sess
: &'sess ParseSess
,
105 ) -> PResult
<'sess
, usize> {
106 let Some(tt
) = iter
.next() else { return Ok(0) }
;
107 let TokenTree
::Token(token
::Token
{
108 kind
: token
::TokenKind
::Literal(lit
), ..
110 return Err(sess
.span_diagnostic
.struct_span_err(
112 "meta-variable expression depth must be a literal"
115 if let Ok(lit_kind
) = LitKind
::from_lit_token(*lit
)
116 && let LitKind
::Int(n_u128
, LitIntType
::Unsuffixed
) = lit_kind
117 && let Ok(n_usize
) = usize::try_from(n_u128
)
122 let msg
= "only unsuffixes integer literals are supported in meta-variable expressions";
123 Err(sess
.span_diagnostic
.struct_span_err(span
, msg
))
127 /// Parses an generic ident
128 fn parse_ident
<'sess
>(
129 iter
: &mut CursorRef
<'_
>,
130 sess
: &'sess ParseSess
,
132 ) -> PResult
<'sess
, Ident
> {
133 if let Some(tt
) = iter
.next() && let TokenTree
::Token(token
) = tt
{
134 if let Some((elem
, false)) = token
.ident() {
137 let token_str
= pprust
::token_to_string(token
);
138 let mut err
= sess
.span_diagnostic
.struct_span_err(
140 &format
!("expected identifier, found `{}`", &token_str
)
144 &format
!("try removing `{}`", &token_str
),
146 Applicability
::MaybeIncorrect
,
150 Err(sess
.span_diagnostic
.struct_span_err(span
, "expected identifier"))
153 /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
154 /// iterator is not modified and the result is `false`.
155 fn try_eat_comma(iter
: &mut CursorRef
<'_
>) -> bool
{
156 if let Some(TokenTree
::Token(token
::Token { kind: token::Comma, .. }
)) = iter
.look_ahead(0) {