rustc_expand/mbe/transcribe.rs
1use std::mem;
2use std::sync::Arc;
3
4use rustc_ast::mut_visit::{self, MutVisitor};
5use rustc_ast::token::{
6 self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Nonterminal, Token,
7 TokenKind,
8};
9use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
10use rustc_ast::{ExprKind, StmtKind, TyKind};
11use rustc_data_structures::fx::FxHashMap;
12use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize};
13use rustc_parse::lexer::nfc_normalize;
14use rustc_parse::parser::ParseNtResult;
15use rustc_session::parse::{ParseSess, SymbolGallery};
16use rustc_span::hygiene::{LocalExpnId, Transparency};
17use rustc_span::{
18 Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
19};
20use smallvec::{SmallVec, smallvec};
21
22use crate::errors::{
23 CountRepetitionMisplaced, MetaVarExprUnrecognizedVar, MetaVarsDifSeqMatchers, MustRepeatOnce,
24 NoSyntaxVarsExprRepeat, VarStillRepeating,
25};
26use crate::mbe::macro_parser::NamedMatch;
27use crate::mbe::macro_parser::NamedMatch::*;
28use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
29use crate::mbe::{self, KleeneOp, MetaVarExpr};
30
31// A Marker adds the given mark to the syntax context.
32struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
33
34impl MutVisitor for Marker {
35 const VISIT_TOKENS: bool = true;
36
37 fn visit_span(&mut self, span: &mut Span) {
38 // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
39 // by itself. All tokens in a macro body typically have the same syntactic context, unless
40 // it's some advanced case with macro-generated macros. So if we cache the marked version
41 // of that context once, we'll typically have a 100% cache hit rate after that.
42 let Marker(expn_id, transparency, ref mut cache) = *self;
43 *span = span.map_ctxt(|ctxt| {
44 *cache
45 .entry(ctxt)
46 .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
47 });
48 }
49}
50
51/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
52struct Frame<'a> {
53 tts: &'a [mbe::TokenTree],
54 idx: usize,
55 kind: FrameKind,
56}
57
58enum FrameKind {
59 Delimited { delim: Delimiter, span: DelimSpan, spacing: DelimSpacing },
60 Sequence { sep: Option<Token>, kleene_op: KleeneOp },
61}
62
63impl<'a> Frame<'a> {
64 fn new_delimited(src: &'a mbe::Delimited, span: DelimSpan, spacing: DelimSpacing) -> Frame<'a> {
65 Frame {
66 tts: &src.tts,
67 idx: 0,
68 kind: FrameKind::Delimited { delim: src.delim, span, spacing },
69 }
70 }
71
72 fn new_sequence(
73 src: &'a mbe::SequenceRepetition,
74 sep: Option<Token>,
75 kleene_op: KleeneOp,
76 ) -> Frame<'a> {
77 Frame { tts: &src.tts, idx: 0, kind: FrameKind::Sequence { sep, kleene_op } }
78 }
79}
80
81impl<'a> Iterator for Frame<'a> {
82 type Item = &'a mbe::TokenTree;
83
84 fn next(&mut self) -> Option<&'a mbe::TokenTree> {
85 let res = self.tts.get(self.idx);
86 self.idx += 1;
87 res
88 }
89}
90
91/// This can do Macro-By-Example transcription.
92/// - `interp` is a map of meta-variables to the tokens (non-terminals) they matched in the
93/// invocation. We are assuming we already know there is a match.
94/// - `src` is the RHS of the MBE, that is, the "example" we are filling in.
95///
96/// For example,
97///
98/// ```rust
99/// macro_rules! foo {
100/// ($id:ident) => { println!("{}", stringify!($id)); }
101/// }
102///
103/// foo!(bar);
104/// ```
105///
106/// `interp` would contain `$id => bar` and `src` would contain `println!("{}", stringify!($id));`.
107///
108/// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`.
109///
110/// Along the way, we do some additional error checking.
111pub(super) fn transcribe<'a>(
112 psess: &'a ParseSess,
113 interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
114 src: &mbe::Delimited,
115 src_span: DelimSpan,
116 transparency: Transparency,
117 expand_id: LocalExpnId,
118) -> PResult<'a, TokenStream> {
119 // Nothing for us to transcribe...
120 if src.tts.is_empty() {
121 return Ok(TokenStream::default());
122 }
123
124 // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
125 // we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
126 // choice of spacing values doesn't matter.
127 let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new_delimited(
128 src,
129 src_span,
130 DelimSpacing::new(Spacing::Alone, Spacing::Alone)
131 )];
132
133 // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
134 // `repeats` keeps track of where we are in matching at each level, with the last element being
135 // the most deeply nested sequence. This is used as a stack.
136 let mut repeats: Vec<(usize, usize)> = Vec::new();
137
138 // `result` contains resulting token stream from the TokenTree we just finished processing. At
139 // the end, this will contain the full result of transcription, but at arbitrary points during
140 // `transcribe`, `result` will contain subsets of the final result.
141 //
142 // Specifically, as we descend into each TokenTree, we will push the existing results onto the
143 // `result_stack` and clear `results`. We will then produce the results of transcribing the
144 // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
145 // `result_stack` and append `results` too it to produce the new `results` up to that point.
146 //
147 // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
148 // again, and we are done transcribing.
149 let mut result: Vec<TokenTree> = Vec::new();
150 let mut result_stack = Vec::new();
151 let mut marker = Marker(expand_id, transparency, Default::default());
152
153 let dcx = psess.dcx();
154 loop {
155 // Look at the last frame on the stack.
156 // If it still has a TokenTree we have not looked at yet, use that tree.
157 let Some(tree) = stack.last_mut().unwrap().next() else {
158 // This else-case never produces a value for `tree` (it `continue`s or `return`s).
159
160 // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
161 // go back to the beginning of the sequence.
162 let frame = stack.last_mut().unwrap();
163 if let FrameKind::Sequence { sep, .. } = &frame.kind {
164 let (repeat_idx, repeat_len) = repeats.last_mut().unwrap();
165 *repeat_idx += 1;
166 if repeat_idx < repeat_len {
167 frame.idx = 0;
168 if let Some(sep) = sep {
169 result.push(TokenTree::Token(sep.clone(), Spacing::Alone));
170 }
171 continue;
172 }
173 }
174
175 // We are done with the top of the stack. Pop it. Depending on what it was, we do
176 // different things. Note that the outermost item must be the delimited, wrapped RHS
177 // that was passed in originally to `transcribe`.
178 match stack.pop().unwrap().kind {
179 // Done with a sequence. Pop from repeats.
180 FrameKind::Sequence { .. } => {
181 repeats.pop();
182 }
183
184 // We are done processing a Delimited. If this is the top-level delimited, we are
185 // done. Otherwise, we unwind the result_stack to append what we have produced to
186 // any previous results.
187 FrameKind::Delimited { delim, span, mut spacing, .. } => {
188 // Hack to force-insert a space after `]` in certain case.
189 // See discussion of the `hex-literal` crate in #114571.
190 if delim == Delimiter::Bracket {
191 spacing.close = Spacing::Alone;
192 }
193 if result_stack.is_empty() {
194 // No results left to compute! We are back at the top-level.
195 return Ok(TokenStream::new(result));
196 }
197
198 // Step back into the parent Delimited.
199 let tree = TokenTree::Delimited(span, spacing, delim, TokenStream::new(result));
200 result = result_stack.pop().unwrap();
201 result.push(tree);
202 }
203 }
204 continue;
205 };
206
207 // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
208 // `tree` contains the next `TokenTree` to be processed.
209 match tree {
210 // We are descending into a sequence. We first make sure that the matchers in the RHS
211 // and the matches in `interp` have the same shape. Otherwise, either the caller or the
212 // macro writer has made a mistake.
213 seq @ mbe::TokenTree::Sequence(_, seq_rep) => {
214 match lockstep_iter_size(seq, interp, &repeats) {
215 LockstepIterSize::Unconstrained => {
216 return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
217 }
218
219 LockstepIterSize::Contradiction(msg) => {
220 // FIXME: this really ought to be caught at macro definition time... It
221 // happens when two meta-variables are used in the same repetition in a
222 // sequence, but they come from different sequence matchers and repeat
223 // different amounts.
224 return Err(
225 dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg })
226 );
227 }
228
229 LockstepIterSize::Constraint(len, _) => {
230 // We do this to avoid an extra clone above. We know that this is a
231 // sequence already.
232 let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
233
234 // Is the repetition empty?
235 if len == 0 {
236 if seq.kleene.op == KleeneOp::OneOrMore {
237 // FIXME: this really ought to be caught at macro definition
238 // time... It happens when the Kleene operator in the matcher and
239 // the body for the same meta-variable do not match.
240 return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
241 }
242 } else {
243 // 0 is the initial counter (we have done 0 repetitions so far). `len`
244 // is the total number of repetitions we should generate.
245 repeats.push((0, len));
246
247 // The first time we encounter the sequence we push it to the stack. It
248 // then gets reused (see the beginning of the loop) until we are done
249 // repeating.
250 stack.push(Frame::new_sequence(
251 seq_rep,
252 seq.separator.clone(),
253 seq.kleene.op,
254 ));
255 }
256 }
257 }
258 }
259
260 // Replace the meta-var with the matched token tree from the invocation.
261 &mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
262 // Find the matched nonterminal from the macro invocation, and use it to replace
263 // the meta-var.
264 //
265 // We use `Spacing::Alone` everywhere here, because that's the conservative choice
266 // and spacing of declarative macros is tricky. E.g. in this macro:
267 // ```
268 // macro_rules! idents {
269 // ($($a:ident,)*) => { stringify!($($a)*) }
270 // }
271 // ```
272 // `$a` has no whitespace after it and will be marked `JointHidden`. If you then
273 // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
274 // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
275 // producing "xyz", which is bad because it effectively merges tokens.
276 // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
277 // some of the unnecessary whitespace.
278 let ident = MacroRulesNormalizedIdent::new(original_ident);
279 if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
280 // We wrap the tokens in invisible delimiters, unless they are already wrapped
281 // in invisible delimiters with the same `MetaVarKind`. Because some proc
282 // macros can't handle multiple layers of invisible delimiters of the same
283 // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
284 let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
285 if stream.len() == 1 {
286 let tree = stream.iter().next().unwrap();
287 if let TokenTree::Delimited(_, _, delim, inner) = tree
288 && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
289 && mv_kind == *mvk
290 {
291 stream = inner.clone();
292 }
293 }
294
295 // Emit as a token stream within `Delimiter::Invisible` to maintain
296 // parsing priorities.
297 marker.visit_span(&mut sp);
298 with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
299 // Both the open delim and close delim get the same span, which covers the
300 // `$foo` in the decl macro RHS.
301 TokenTree::Delimited(
302 DelimSpan::from_single(sp),
303 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
304 Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
305 stream,
306 )
307 };
308 let tt = match cur_matched {
309 MatchedSingle(ParseNtResult::Tt(tt)) => {
310 // `tt`s are emitted into the output stream directly as "raw tokens",
311 // without wrapping them into groups.
312 maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker)
313 }
314 MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
315 marker.visit_span(&mut sp);
316 with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
317 let kind = token::NtIdent(*ident, *is_raw);
318 TokenTree::token_alone(kind, sp)
319 }
320 MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
321 marker.visit_span(&mut sp);
322 with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
323 let kind = token::NtLifetime(*ident, *is_raw);
324 TokenTree::token_alone(kind, sp)
325 }
326 MatchedSingle(ParseNtResult::Item(item)) => {
327 mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
328 }
329 MatchedSingle(ParseNtResult::Stmt(stmt)) => {
330 let stream = if let StmtKind::Empty = stmt.kind {
331 // FIXME: Properly collect tokens for empty statements.
332 TokenStream::token_alone(token::Semi, stmt.span)
333 } else {
334 TokenStream::from_ast(stmt)
335 };
336 mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
337 }
338 MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => mk_delimited(
339 pat.span,
340 MetaVarKind::Pat(*pat_kind),
341 TokenStream::from_ast(pat),
342 ),
343 MatchedSingle(ParseNtResult::Ty(ty)) => {
344 let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
345 mk_delimited(
346 ty.span,
347 MetaVarKind::Ty { is_path },
348 TokenStream::from_ast(ty),
349 )
350 }
351 MatchedSingle(ParseNtResult::Meta(attr_item)) => {
352 let has_meta_form = attr_item.meta_kind().is_some();
353 mk_delimited(
354 attr_item.span(),
355 MetaVarKind::Meta { has_meta_form },
356 TokenStream::from_ast(attr_item),
357 )
358 }
359 MatchedSingle(ParseNtResult::Path(path)) => {
360 mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
361 }
362 MatchedSingle(ParseNtResult::Vis(vis)) => {
363 mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
364 }
365 MatchedSingle(ParseNtResult::Nt(nt)) => {
366 // Other variables are emitted into the output stream as groups with
367 // `Delimiter::Invisible` to maintain parsing priorities.
368 // `Interpolated` is currently used for such groups in rustc parser.
369 marker.visit_span(&mut sp);
370 let use_span = nt.use_span();
371 with_metavar_spans(|mspans| mspans.insert(use_span, sp));
372 TokenTree::token_alone(token::Interpolated(Arc::clone(nt)), sp)
373 }
374 MatchedSeq(..) => {
375 // We were unable to descend far enough. This is an error.
376 return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
377 }
378 };
379 result.push(tt)
380 } else {
381 // If we aren't able to match the meta-var, we push it back into the result but
382 // with modified syntax context. (I believe this supports nested macros).
383 marker.visit_span(&mut sp);
384 marker.visit_ident(&mut original_ident);
385 result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
386 result.push(TokenTree::Token(
387 Token::from_ast_ident(original_ident),
388 Spacing::Alone,
389 ));
390 }
391 }
392
393 // Replace meta-variable expressions with the result of their expansion.
394 mbe::TokenTree::MetaVarExpr(sp, expr) => {
395 transcribe_metavar_expr(
396 dcx,
397 expr,
398 interp,
399 &mut marker,
400 &repeats,
401 &mut result,
402 sp,
403 &psess.symbol_gallery,
404 )?;
405 }
406
407 // If we are entering a new delimiter, we push its contents to the `stack` to be
408 // processed, and we push all of the currently produced results to the `result_stack`.
409 // We will produce all of the results of the inside of the `Delimited` and then we will
410 // jump back out of the Delimited, pop the result_stack and add the new results back to
411 // the previous results (from outside the Delimited).
412 &mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => {
413 mut_visit::visit_delim_span(&mut marker, &mut span);
414 stack.push(Frame::new_delimited(delimited, span, *spacing));
415 result_stack.push(mem::take(&mut result));
416 }
417
418 // Nothing much to do here. Just push the token to the result, being careful to
419 // preserve syntax context.
420 mbe::TokenTree::Token(token) => {
421 let mut token = token.clone();
422 mut_visit::visit_token(&mut marker, &mut token);
423 let tt = TokenTree::Token(token, Spacing::Alone);
424 result.push(tt);
425 }
426
427 // There should be no meta-var declarations in the invocation of a macro.
428 mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl`"),
429 }
430 }
431}
432
433/// Store the metavariable span for this original span into a side table.
434/// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517).
435/// An optimal encoding for inlined spans will need to be selected to minimize regressions.
436/// The side table approach is relatively good, but not perfect due to collisions.
437/// In particular, collisions happen when token is passed as an argument through several macro
438/// calls, like in recursive macros.
439/// The old heuristic below is used to improve spans in case of collisions, but diagnostics are
440/// still degraded sometimes in those cases.
441///
442/// The old heuristic:
443///
444/// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
445/// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
446/// case however, and there's no place for keeping a second span. So we try to give the single
447/// produced span a location that would be most useful in practice (the hygiene part of the span
448/// must not be changed).
449///
450/// Different locations are useful for different purposes:
451/// - The original location is useful when we need to report a diagnostic for the original token in
452/// isolation, without combining it with any surrounding tokens. This case occurs, but it is not
453/// very common in practice.
454/// - The metavariable location is useful when we need to somehow combine the token span with spans
455/// of its surrounding tokens. This is the most common way to use token spans.
456///
457/// So this function replaces the original location with the metavariable location in all cases
458/// except these two:
459/// - The metavariable is an element of undelimited sequence `$($tt)*`.
460/// These are typically used for passing larger amounts of code, and tokens in that code usually
461/// combine with each other and not with tokens outside of the sequence.
462/// - The metavariable span comes from a different crate, then we prefer the more local span.
463fn maybe_use_metavar_location(
464 psess: &ParseSess,
465 stack: &[Frame<'_>],
466 mut metavar_span: Span,
467 orig_tt: &TokenTree,
468 marker: &mut Marker,
469) -> TokenTree {
470 let undelimited_seq = matches!(
471 stack.last(),
472 Some(Frame {
473 tts: [_],
474 kind: FrameKind::Sequence {
475 sep: None,
476 kleene_op: KleeneOp::ZeroOrMore | KleeneOp::OneOrMore,
477 ..
478 },
479 ..
480 })
481 );
482 if undelimited_seq {
483 // Do not record metavar spans for tokens from undelimited sequences, for perf reasons.
484 return orig_tt.clone();
485 }
486
487 marker.visit_span(&mut metavar_span);
488 let no_collision = match orig_tt {
489 TokenTree::Token(token, ..) => {
490 with_metavar_spans(|mspans| mspans.insert(token.span, metavar_span))
491 }
492 TokenTree::Delimited(dspan, ..) => with_metavar_spans(|mspans| {
493 mspans.insert(dspan.open, metavar_span)
494 && mspans.insert(dspan.close, metavar_span)
495 && mspans.insert(dspan.entire(), metavar_span)
496 }),
497 };
498 if no_collision || psess.source_map().is_imported(metavar_span) {
499 return orig_tt.clone();
500 }
501
502 // Setting metavar spans for the heuristic spans gives better opportunities for combining them
503 // with neighboring spans even despite their different syntactic contexts.
504 match orig_tt {
505 TokenTree::Token(Token { kind, span }, spacing) => {
506 let span = metavar_span.with_ctxt(span.ctxt());
507 with_metavar_spans(|mspans| mspans.insert(span, metavar_span));
508 TokenTree::Token(Token { kind: kind.clone(), span }, *spacing)
509 }
510 TokenTree::Delimited(dspan, dspacing, delimiter, tts) => {
511 let open = metavar_span.with_ctxt(dspan.open.ctxt());
512 let close = metavar_span.with_ctxt(dspan.close.ctxt());
513 with_metavar_spans(|mspans| {
514 mspans.insert(open, metavar_span) && mspans.insert(close, metavar_span)
515 });
516 let dspan = DelimSpan::from_pair(open, close);
517 TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone())
518 }
519 }
520}
521
522/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
523/// the set of matches `interpolations`.
524///
525/// See the definition of `repeats` in the `transcribe` function. `repeats` is used to descend
526/// into the right place in nested matchers. If we attempt to descend too far, the macro writer has
527/// made a mistake, and we return `None`.
528fn lookup_cur_matched<'a>(
529 ident: MacroRulesNormalizedIdent,
530 interpolations: &'a FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
531 repeats: &[(usize, usize)],
532) -> Option<&'a NamedMatch> {
533 interpolations.get(&ident).map(|mut matched| {
534 for &(idx, _) in repeats {
535 match matched {
536 MatchedSingle(_) => break,
537 MatchedSeq(ads) => matched = ads.get(idx).unwrap(),
538 }
539 }
540
541 matched
542 })
543}
544
545/// An accumulator over a TokenTree to be used with `fold`. During transcription, we need to make
546/// sure that the size of each sequence and all of its nested sequences are the same as the sizes
547/// of all the matched (nested) sequences in the macro invocation. If they don't match, somebody
548/// has made a mistake (either the macro writer or caller).
549#[derive(Clone)]
550enum LockstepIterSize {
551 /// No constraints on length of matcher. This is true for any TokenTree variants except a
552 /// `MetaVar` with an actual `MatchedSeq` (as opposed to a `MatchedNonterminal`).
553 Unconstrained,
554
555 /// A `MetaVar` with an actual `MatchedSeq`. The length of the match and the name of the
556 /// meta-var are returned.
557 Constraint(usize, MacroRulesNormalizedIdent),
558
559 /// Two `Constraint`s on the same sequence had different lengths. This is an error.
560 Contradiction(String),
561}
562
563impl LockstepIterSize {
564 /// Find incompatibilities in matcher/invocation sizes.
565 /// - `Unconstrained` is compatible with everything.
566 /// - `Contradiction` is incompatible with everything.
567 /// - `Constraint(len)` is only compatible with other constraints of the same length.
568 fn with(self, other: LockstepIterSize) -> LockstepIterSize {
569 match self {
570 LockstepIterSize::Unconstrained => other,
571 LockstepIterSize::Contradiction(_) => self,
572 LockstepIterSize::Constraint(l_len, l_id) => match other {
573 LockstepIterSize::Unconstrained => self,
574 LockstepIterSize::Contradiction(_) => other,
575 LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
576 LockstepIterSize::Constraint(r_len, r_id) => {
577 let msg = format!(
578 "meta-variable `{}` repeats {} time{}, but `{}` repeats {} time{}",
579 l_id,
580 l_len,
581 pluralize!(l_len),
582 r_id,
583 r_len,
584 pluralize!(r_len),
585 );
586 LockstepIterSize::Contradiction(msg)
587 }
588 },
589 }
590 }
591}
592
593/// Given a `tree`, make sure that all sequences have the same length as the matches for the
594/// appropriate meta-vars in `interpolations`.
595///
596/// Note that if `repeats` does not match the exact correct depth of a meta-var,
597/// `lookup_cur_matched` will return `None`, which is why this still works even in the presence of
598/// multiple nested matcher sequences.
599///
600/// Example: `$($($x $y)+*);+` -- we need to make sure that `x` and `y` repeat the same amount as
601/// each other at the given depth when the macro was invoked. If they don't it might mean they were
602/// declared at depths which weren't equal or there was a compiler bug. For example, if we have 3 repetitions of
603/// the outer sequence and 4 repetitions of the inner sequence for `x`, we should have the same for
604/// `y`; otherwise, we can't transcribe them both at the given depth.
605fn lockstep_iter_size(
606 tree: &mbe::TokenTree,
607 interpolations: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
608 repeats: &[(usize, usize)],
609) -> LockstepIterSize {
610 use mbe::TokenTree;
611 match tree {
612 TokenTree::Delimited(.., delimited) => {
613 delimited.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
614 size.with(lockstep_iter_size(tt, interpolations, repeats))
615 })
616 }
617 TokenTree::Sequence(_, seq) => {
618 seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
619 size.with(lockstep_iter_size(tt, interpolations, repeats))
620 })
621 }
622 TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
623 let name = MacroRulesNormalizedIdent::new(*name);
624 match lookup_cur_matched(name, interpolations, repeats) {
625 Some(matched) => match matched {
626 MatchedSingle(_) => LockstepIterSize::Unconstrained,
627 MatchedSeq(ads) => LockstepIterSize::Constraint(ads.len(), name),
628 },
629 _ => LockstepIterSize::Unconstrained,
630 }
631 }
632 TokenTree::MetaVarExpr(_, expr) => {
633 expr.for_each_metavar(LockstepIterSize::Unconstrained, |lis, ident| {
634 lis.with(lockstep_iter_size(
635 &TokenTree::MetaVar(ident.span, *ident),
636 interpolations,
637 repeats,
638 ))
639 })
640 }
641 TokenTree::Token(..) => LockstepIterSize::Unconstrained,
642 }
643}
644
645/// Used solely by the `count` meta-variable expression, counts the outermost repetitions at a
646/// given optional nested depth.
647///
648/// For example, a macro parameter of `$( { $( $foo:ident ),* } )*` called with `{ a, b } { c }`:
649///
650/// * `[ $( ${count(foo)} ),* ]` will return [2, 1] with a, b = 2 and c = 1
651/// * `[ $( ${count(foo, 0)} ),* ]` will be the same as `[ $( ${count(foo)} ),* ]`
652/// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is
653/// declared inside a single repetition and the index `1` implies two nested repetitions.
654fn count_repetitions<'a>(
655 dcx: DiagCtxtHandle<'a>,
656 depth_user: usize,
657 mut matched: &NamedMatch,
658 repeats: &[(usize, usize)],
659 sp: &DelimSpan,
660) -> PResult<'a, usize> {
661 // Recursively count the number of matches in `matched` at given depth
662 // (or at the top-level of `matched` if no depth is given).
663 fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> {
664 match matched {
665 MatchedSingle(_) => Ok(1),
666 MatchedSeq(named_matches) => {
667 if depth_curr == depth_max {
668 Ok(named_matches.len())
669 } else {
670 named_matches.iter().map(|elem| count(depth_curr + 1, depth_max, elem)).sum()
671 }
672 }
673 }
674 }
675
676 /// Maximum depth
677 fn depth(counter: usize, matched: &NamedMatch) -> usize {
678 match matched {
679 MatchedSingle(_) => counter,
680 MatchedSeq(named_matches) => {
681 let rslt = counter + 1;
682 if let Some(elem) = named_matches.first() { depth(rslt, elem) } else { rslt }
683 }
684 }
685 }
686
687 let depth_max = depth(0, matched)
688 .checked_sub(1)
689 .and_then(|el| el.checked_sub(repeats.len()))
690 .unwrap_or_default();
691 if depth_user > depth_max {
692 return Err(out_of_bounds_err(dcx, depth_max + 1, sp.entire(), "count"));
693 }
694
695 // `repeats` records all of the nested levels at which we are currently
696 // matching meta-variables. The meta-var-expr `count($x)` only counts
697 // matches that occur in this "subtree" of the `NamedMatch` where we
698 // are currently transcribing, so we need to descend to that subtree
699 // before we start counting. `matched` contains the various levels of the
700 // tree as we descend, and its final value is the subtree we are currently at.
701 for &(idx, _) in repeats {
702 if let MatchedSeq(ads) = matched {
703 matched = &ads[idx];
704 }
705 }
706
707 if let MatchedSingle(_) = matched {
708 return Err(dcx.create_err(CountRepetitionMisplaced { span: sp.entire() }));
709 }
710
711 count(depth_user, depth_max, matched)
712}
713
714/// Returns a `NamedMatch` item declared on the LHS given an arbitrary [Ident]
715fn matched_from_ident<'ctx, 'interp, 'rslt>(
716 dcx: DiagCtxtHandle<'ctx>,
717 ident: Ident,
718 interp: &'interp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
719) -> PResult<'ctx, &'rslt NamedMatch>
720where
721 'interp: 'rslt,
722{
723 let span = ident.span;
724 let key = MacroRulesNormalizedIdent::new(ident);
725 interp.get(&key).ok_or_else(|| dcx.create_err(MetaVarExprUnrecognizedVar { span, key }))
726}
727
728/// Used by meta-variable expressions when an user input is out of the actual declared bounds. For
729/// example, index(999999) in an repetition of only three elements.
730fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &str) -> Diag<'a> {
731 let msg = if max == 0 {
732 format!(
733 "meta-variable expression `{ty}` with depth parameter \
734 must be called inside of a macro repetition"
735 )
736 } else {
737 format!(
738 "depth parameter of meta-variable expression `{ty}` \
739 must be less than {max}"
740 )
741 };
742 dcx.struct_span_err(span, msg)
743}
744
745fn transcribe_metavar_expr<'a>(
746 dcx: DiagCtxtHandle<'a>,
747 expr: &MetaVarExpr,
748 interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
749 marker: &mut Marker,
750 repeats: &[(usize, usize)],
751 result: &mut Vec<TokenTree>,
752 sp: &DelimSpan,
753 symbol_gallery: &SymbolGallery,
754) -> PResult<'a, ()> {
755 let mut visited_span = || {
756 let mut span = sp.entire();
757 marker.visit_span(&mut span);
758 span
759 };
760 match *expr {
761 MetaVarExpr::Concat(ref elements) => {
762 let mut concatenated = String::new();
763 for element in elements.into_iter() {
764 let symbol = match element {
765 MetaVarExprConcatElem::Ident(elem) => elem.name,
766 MetaVarExprConcatElem::Literal(elem) => *elem,
767 MetaVarExprConcatElem::Var(ident) => {
768 match matched_from_ident(dcx, *ident, interp)? {
769 NamedMatch::MatchedSeq(named_matches) => {
770 let Some((curr_idx, _)) = repeats.last() else {
771 return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
772 };
773 match &named_matches[*curr_idx] {
774 // FIXME(c410-f3r) Nested repetitions are unimplemented
775 MatchedSeq(_) => unimplemented!(),
776 MatchedSingle(pnr) => {
777 extract_symbol_from_pnr(dcx, pnr, ident.span)?
778 }
779 }
780 }
781 NamedMatch::MatchedSingle(pnr) => {
782 extract_symbol_from_pnr(dcx, pnr, ident.span)?
783 }
784 }
785 }
786 };
787 concatenated.push_str(symbol.as_str());
788 }
789 let symbol = nfc_normalize(&concatenated);
790 let concatenated_span = visited_span();
791 if !rustc_lexer::is_ident(symbol.as_str()) {
792 return Err(dcx.struct_span_err(
793 concatenated_span,
794 "`${concat(..)}` is not generating a valid identifier",
795 ));
796 }
797 symbol_gallery.insert(symbol, concatenated_span);
798 // The current implementation marks the span as coming from the macro regardless of
799 // contexts of the concatenated identifiers but this behavior may change in the
800 // future.
801 result.push(TokenTree::Token(
802 Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
803 Spacing::Alone,
804 ));
805 }
806 MetaVarExpr::Count(original_ident, depth) => {
807 let matched = matched_from_ident(dcx, original_ident, interp)?;
808 let count = count_repetitions(dcx, depth, matched, repeats, sp)?;
809 let tt = TokenTree::token_alone(
810 TokenKind::lit(token::Integer, sym::integer(count), None),
811 visited_span(),
812 );
813 result.push(tt);
814 }
815 MetaVarExpr::Ignore(original_ident) => {
816 // Used to ensure that `original_ident` is present in the LHS
817 let _ = matched_from_ident(dcx, original_ident, interp)?;
818 }
819 MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) {
820 Some((index, _)) => {
821 result.push(TokenTree::token_alone(
822 TokenKind::lit(token::Integer, sym::integer(*index), None),
823 visited_span(),
824 ));
825 }
826 None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "index")),
827 },
828 MetaVarExpr::Len(depth) => match repeats.iter().nth_back(depth) {
829 Some((_, length)) => {
830 result.push(TokenTree::token_alone(
831 TokenKind::lit(token::Integer, sym::integer(*length), None),
832 visited_span(),
833 ));
834 }
835 None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "len")),
836 },
837 }
838 Ok(())
839}
840
841/// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
842fn extract_symbol_from_pnr<'a>(
843 dcx: DiagCtxtHandle<'a>,
844 pnr: &ParseNtResult,
845 span_err: Span,
846) -> PResult<'a, Symbol> {
847 match pnr {
848 ParseNtResult::Ident(nt_ident, is_raw) => {
849 if let IdentIsRaw::Yes = is_raw {
850 Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR))
851 } else {
852 Ok(nt_ident.name)
853 }
854 }
855 ParseNtResult::Tt(TokenTree::Token(
856 Token { kind: TokenKind::Ident(symbol, is_raw), .. },
857 _,
858 )) => {
859 if let IdentIsRaw::Yes = is_raw {
860 Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR))
861 } else {
862 Ok(*symbol)
863 }
864 }
865 ParseNtResult::Tt(TokenTree::Token(
866 Token {
867 kind: TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }),
868 ..
869 },
870 _,
871 )) => Ok(*symbol),
872 ParseNtResult::Nt(nt)
873 if let Nonterminal::NtLiteral(expr) = &**nt
874 && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) =
875 &expr.kind =>
876 {
877 Ok(*symbol)
878 }
879 _ => Err(dcx
880 .struct_err(
881 "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`",
882 )
883 .with_note("currently only string literals are supported")
884 .with_span(span_err)),
885 }
886}