]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_expand/src/mbe/metavar_expr.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_expand / src / mbe / metavar_expr.rs
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;
8 use rustc_span::Span;
9
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>),
16
17 /// Ignore a meta-variable for repetition without expansion.
18 Ignore(Ident),
19
20 /// The index of the repetition at a particular depth, where 0 is the inner-most
21 /// repetition. The `usize` is the depth.
22 Index(usize),
23
24 /// The length of the repetition at a particular depth, where 0 is the inner-most
25 /// repetition. The `usize` is the depth.
26 Length(usize),
27 }
28
29 impl MetaVarExpr {
30 /// Attempt to parse a meta-variable expression from a token stream.
31 pub(crate) fn parse<'sess>(
32 input: &TokenStream,
33 outer_span: Span,
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));
41 };
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)?),
49 _ => {
50 let err_msg = "unrecognized meta-variable expression";
51 let mut err = sess.span_diagnostic.struct_span_err(ident.span, err_msg);
52 err.span_suggestion(
53 ident.span,
54 "supported expressions are count, ignore, index and length",
55 "",
56 Applicability::MachineApplicable,
57 );
58 return Err(err);
59 }
60 };
61 check_trailing_token(&mut iter, sess)?;
62 Ok(rslt)
63 }
64
65 pub(crate) fn ident(&self) -> Option<Ident> {
66 match *self {
67 MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident),
68 MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None,
69 }
70 }
71 }
72
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() {
79 let mut diag = sess
80 .span_diagnostic
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");
83 Err(diag)
84 } else {
85 Ok(())
86 }
87 }
88
89 /// Parse a meta-variable `count` expression: `count(ident[, depth])`
90 fn parse_count<'sess>(
91 iter: &mut CursorRef<'_>,
92 sess: &'sess ParseSess,
93 span: Span,
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))
98 }
99
100 /// Parses the depth used by index(depth) and length(depth).
101 fn parse_depth<'sess>(
102 iter: &mut CursorRef<'_>,
103 sess: &'sess ParseSess,
104 span: Span,
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), ..
109 }) = tt else {
110 return Err(sess.span_diagnostic.struct_span_err(
111 span,
112 "meta-variable expression depth must be a literal"
113 ));
114 };
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)
118 {
119 Ok(n_usize)
120 }
121 else {
122 let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
123 Err(sess.span_diagnostic.struct_span_err(span, msg))
124 }
125 }
126
127 /// Parses an generic ident
128 fn parse_ident<'sess>(
129 iter: &mut CursorRef<'_>,
130 sess: &'sess ParseSess,
131 span: Span,
132 ) -> PResult<'sess, Ident> {
133 if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt {
134 if let Some((elem, false)) = token.ident() {
135 return Ok(elem);
136 }
137 let token_str = pprust::token_to_string(token);
138 let mut err = sess.span_diagnostic.struct_span_err(
139 span,
140 &format!("expected identifier, found `{}`", &token_str)
141 );
142 err.span_suggestion(
143 token.span,
144 &format!("try removing `{}`", &token_str),
145 "",
146 Applicability::MaybeIncorrect,
147 );
148 return Err(err);
149 }
150 Err(sess.span_diagnostic.struct_span_err(span, "expected identifier"))
151 }
152
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) {
157 let _ = iter.next();
158 return true;
159 }
160 false
161 }