rustc_expand/mbe/
macro_check.rs

1//! Checks that meta-variables in macro definition are correctly declared and used.
2//!
3//! # What is checked
4//!
5//! ## Meta-variables must not be bound twice
6//!
7//! ```compile_fail
8//! macro_rules! foo { ($x:tt $x:tt) => { $x }; }
9//! ```
10//!
11//! This check is sound (no false-negative) and complete (no false-positive).
12//!
13//! ## Meta-variables must not be free
14//!
15//! ```
16//! macro_rules! foo { () => { $x }; }
17//! ```
18//!
19//! This check is also done at macro instantiation but only if the branch is taken.
20//!
21//! ## Meta-variables must repeat at least as many times as their binder
22//!
23//! ```
24//! macro_rules! foo { ($($x:tt)*) => { $x }; }
25//! ```
26//!
27//! This check is also done at macro instantiation but only if the branch is taken.
28//!
29//! ## Meta-variables must repeat with the same Kleene operators as their binder
30//!
31//! ```
32//! macro_rules! foo { ($($x:tt)+) => { $($x)* }; }
33//! ```
34//!
35//! This check is not done at macro instantiation.
36//!
37//! # Disclaimer
38//!
39//! In the presence of nested macros (a macro defined in a macro), those checks may have false
40//! positives and false negatives. We try to detect those cases by recognizing potential macro
41//! definitions in RHSes, but nested macros may be hidden through the use of particular values of
42//! meta-variables.
43//!
44//! ## Examples of false positive
45//!
46//! False positives can come from cases where we don't recognize a nested macro, because it depends
47//! on particular values of meta-variables. In the following example, we think both instances of
48//! `$x` are free, which is a correct statement if `$name` is anything but `macro_rules`. But when
49//! `$name` is `macro_rules`, like in the instantiation below, then `$x:tt` is actually a binder of
50//! the nested macro and `$x` is bound to it.
51//!
52//! ```
53//! macro_rules! foo { ($name:ident) => { $name! bar { ($x:tt) => { $x }; } }; }
54//! foo!(macro_rules);
55//! ```
56//!
57//! False positives can also come from cases where we think there is a nested macro while there
58//! isn't. In the following example, we think `$x` is free, which is incorrect because `bar` is not
59//! a nested macro since it is not evaluated as code by `stringify!`.
60//!
61//! ```
62//! macro_rules! foo { () => { stringify!(macro_rules! bar { () => { $x }; }) }; }
63//! ```
64//!
65//! ## Examples of false negative
66//!
67//! False negatives can come from cases where we don't recognize a meta-variable, because it depends
68//! on particular values of meta-variables. In the following examples, we don't see that if `$d` is
69//! instantiated with `$` then `$d z` becomes `$z` in the nested macro definition and is thus a free
70//! meta-variable. Note however, that if `foo` is instantiated, then we would check the definition
71//! of `bar` and would see the issue.
72//!
73//! ```
74//! macro_rules! foo { ($d:tt) => { macro_rules! bar { ($y:tt) => { $d z }; } }; }
75//! ```
76//!
77//! # How it is checked
78//!
79//! There are 3 main functions: `check_binders`, `check_occurrences`, and `check_nested_macro`. They
80//! all need some kind of environment.
81//!
82//! ## Environments
83//!
84//! Environments are used to pass information.
85//!
86//! ### From LHS to RHS
87//!
88//! When checking a LHS with `check_binders`, we produce (and use) an environment for binders,
89//! namely `Binders`. This is a mapping from binder name to information about that binder: the span
90//! of the binder for error messages and the stack of Kleene operators under which it was bound in
91//! the LHS.
92//!
93//! This environment is used by both the LHS and RHS. The LHS uses it to detect duplicate binders.
94//! The RHS uses it to detect the other errors.
95//!
96//! ### From outer macro to inner macro
97//!
98//! When checking the RHS of an outer macro and we detect a nested macro definition, we push the
99//! current state, namely `MacroState`, to an environment of nested macro definitions. Each state
100//! stores the LHS binders when entering the macro definition as well as the stack of Kleene
101//! operators under which the inner macro is defined in the RHS.
102//!
103//! This environment is a stack representing the nesting of macro definitions. As such, the stack of
104//! Kleene operators under which a meta-variable is repeating is the concatenation of the stacks
105//! stored when entering a macro definition starting from the state in which the meta-variable is
106//! bound.
107
108use std::iter;
109
110use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind};
111use rustc_ast::{DUMMY_NODE_ID, NodeId};
112use rustc_data_structures::fx::FxHashMap;
113use rustc_errors::MultiSpan;
114use rustc_lint_defs::BuiltinLintDiag;
115use rustc_session::lint::builtin::META_VARIABLE_MISUSE;
116use rustc_session::parse::ParseSess;
117use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
118use smallvec::SmallVec;
119
120use crate::errors;
121use crate::mbe::{KleeneToken, TokenTree};
122
123/// Stack represented as linked list.
124///
125/// Those are used for environments because they grow incrementally and are not mutable.
126enum Stack<'a, T> {
127    /// Empty stack.
128    Empty,
129    /// A non-empty stack.
130    Push {
131        /// The top element.
132        top: T,
133        /// The previous elements.
134        prev: &'a Stack<'a, T>,
135    },
136}
137
138impl<'a, T> Stack<'a, T> {
139    /// Returns whether a stack is empty.
140    fn is_empty(&self) -> bool {
141        matches!(*self, Stack::Empty)
142    }
143
144    /// Returns a new stack with an element of top.
145    fn push(&'a self, top: T) -> Stack<'a, T> {
146        Stack::Push { top, prev: self }
147    }
148}
149
150impl<'a, T> Iterator for &'a Stack<'a, T> {
151    type Item = &'a T;
152
153    // Iterates from top to bottom of the stack.
154    fn next(&mut self) -> Option<&'a T> {
155        match self {
156            Stack::Empty => None,
157            Stack::Push { top, prev } => {
158                *self = prev;
159                Some(top)
160            }
161        }
162    }
163}
164
165impl From<&Stack<'_, KleeneToken>> for SmallVec<[KleeneToken; 1]> {
166    fn from(ops: &Stack<'_, KleeneToken>) -> SmallVec<[KleeneToken; 1]> {
167        let mut ops: SmallVec<[KleeneToken; 1]> = ops.cloned().collect();
168        // The stack is innermost on top. We want outermost first.
169        ops.reverse();
170        ops
171    }
172}
173
174/// Information attached to a meta-variable binder in LHS.
175struct BinderInfo {
176    /// The span of the meta-variable in LHS.
177    span: Span,
178    /// The stack of Kleene operators (outermost first).
179    ops: SmallVec<[KleeneToken; 1]>,
180}
181
182/// An environment of meta-variables to their binder information.
183type Binders = FxHashMap<MacroRulesNormalizedIdent, BinderInfo>;
184
185/// The state at which we entered a macro definition in the RHS of another macro definition.
186struct MacroState<'a> {
187    /// The binders of the branch where we entered the macro definition.
188    binders: &'a Binders,
189    /// The stack of Kleene operators (outermost first) where we entered the macro definition.
190    ops: SmallVec<[KleeneToken; 1]>,
191}
192
193/// Checks that meta-variables are used correctly in a macro definition.
194///
195/// Arguments:
196/// - `psess` is used to emit diagnostics and lints
197/// - `node_id` is used to emit lints
198/// - `span` is used when no spans are available
199/// - `lhses` and `rhses` should have the same length and represent the macro definition
200pub(super) fn check_meta_variables(
201    psess: &ParseSess,
202    node_id: NodeId,
203    span: Span,
204    lhses: &[TokenTree],
205    rhses: &[TokenTree],
206) -> Result<(), ErrorGuaranteed> {
207    if lhses.len() != rhses.len() {
208        psess.dcx().span_bug(span, "length mismatch between LHSes and RHSes")
209    }
210    let mut guar = None;
211    for (lhs, rhs) in iter::zip(lhses, rhses) {
212        let mut binders = Binders::default();
213        check_binders(psess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar);
214        check_occurrences(psess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut guar);
215    }
216    guar.map_or(Ok(()), Err)
217}
218
219/// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and
220/// sets `valid` to false in case of errors.
221///
222/// Arguments:
223/// - `psess` is used to emit diagnostics and lints
224/// - `node_id` is used to emit lints
225/// - `lhs` is checked as part of a LHS
226/// - `macros` is the stack of possible outer macros
227/// - `binders` contains the binders of the LHS
228/// - `ops` is the stack of Kleene operators from the LHS
229/// - `guar` is set in case of errors
230fn check_binders(
231    psess: &ParseSess,
232    node_id: NodeId,
233    lhs: &TokenTree,
234    macros: &Stack<'_, MacroState<'_>>,
235    binders: &mut Binders,
236    ops: &Stack<'_, KleeneToken>,
237    guar: &mut Option<ErrorGuaranteed>,
238) {
239    match *lhs {
240        TokenTree::Token(..) => {}
241        // This can only happen when checking a nested macro because this LHS is then in the RHS of
242        // the outer macro. See ui/macros/macro-of-higher-order.rs where $y:$fragment in the
243        // LHS of the nested macro (and RHS of the outer macro) is parsed as MetaVar(y) Colon
244        // MetaVar(fragment) and not as MetaVarDecl(y, fragment).
245        TokenTree::MetaVar(span, name) => {
246            if macros.is_empty() {
247                psess.dcx().span_bug(span, "unexpected MetaVar in lhs");
248            }
249            let name = MacroRulesNormalizedIdent::new(name);
250            // There are 3 possibilities:
251            if let Some(prev_info) = binders.get(&name) {
252                // 1. The meta-variable is already bound in the current LHS: This is an error.
253                let mut span = MultiSpan::from_span(span);
254                span.push_span_label(prev_info.span, "previous declaration");
255                buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding);
256            } else if get_binder_info(macros, binders, name).is_none() {
257                // 2. The meta-variable is free: This is a binder.
258                binders.insert(name, BinderInfo { span, ops: ops.into() });
259            } else {
260                // 3. The meta-variable is bound: This is an occurrence.
261                check_occurrences(psess, node_id, lhs, macros, binders, ops, guar);
262            }
263        }
264        // Similarly, this can only happen when checking a toplevel macro.
265        TokenTree::MetaVarDecl { span, name, .. } => {
266            if !macros.is_empty() {
267                psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
268            }
269            let name = MacroRulesNormalizedIdent::new(name);
270            if let Some(prev_info) = get_binder_info(macros, binders, name) {
271                // Duplicate binders at the top-level macro definition are errors. The lint is only
272                // for nested macro definitions.
273                *guar = Some(
274                    psess
275                        .dcx()
276                        .emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span }),
277                );
278            } else {
279                binders.insert(name, BinderInfo { span, ops: ops.into() });
280            }
281        }
282        // `MetaVarExpr` can not appear in the LHS of a macro arm
283        TokenTree::MetaVarExpr(..) => {}
284        TokenTree::Delimited(.., ref del) => {
285            for tt in &del.tts {
286                check_binders(psess, node_id, tt, macros, binders, ops, guar);
287            }
288        }
289        TokenTree::Sequence(_, ref seq) => {
290            let ops = ops.push(seq.kleene);
291            for tt in &seq.tts {
292                check_binders(psess, node_id, tt, macros, binders, &ops, guar);
293            }
294        }
295    }
296}
297
298/// Returns the binder information of a meta-variable.
299///
300/// Arguments:
301/// - `macros` is the stack of possible outer macros
302/// - `binders` contains the current binders
303/// - `name` is the name of the meta-variable we are looking for
304fn get_binder_info<'a>(
305    mut macros: &'a Stack<'a, MacroState<'a>>,
306    binders: &'a Binders,
307    name: MacroRulesNormalizedIdent,
308) -> Option<&'a BinderInfo> {
309    binders.get(&name).or_else(|| macros.find_map(|state| state.binders.get(&name)))
310}
311
312/// Checks `rhs` as part of the RHS of a macro definition and sets `valid` to false in case of
313/// errors.
314///
315/// Arguments:
316/// - `psess` is used to emit diagnostics and lints
317/// - `node_id` is used to emit lints
318/// - `rhs` is checked as part of a RHS
319/// - `macros` is the stack of possible outer macros
320/// - `binders` contains the binders of the associated LHS
321/// - `ops` is the stack of Kleene operators from the RHS
322/// - `guar` is set in case of errors
323fn check_occurrences(
324    psess: &ParseSess,
325    node_id: NodeId,
326    rhs: &TokenTree,
327    macros: &Stack<'_, MacroState<'_>>,
328    binders: &Binders,
329    ops: &Stack<'_, KleeneToken>,
330    guar: &mut Option<ErrorGuaranteed>,
331) {
332    match *rhs {
333        TokenTree::Token(..) => {}
334        TokenTree::MetaVarDecl { span, .. } => {
335            psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs")
336        }
337        TokenTree::MetaVar(span, name) => {
338            let name = MacroRulesNormalizedIdent::new(name);
339            check_ops_is_prefix(psess, node_id, macros, binders, ops, span, name);
340        }
341        TokenTree::MetaVarExpr(dl, ref mve) => {
342            mve.for_each_metavar((), |_, ident| {
343                let name = MacroRulesNormalizedIdent::new(*ident);
344                check_ops_is_prefix(psess, node_id, macros, binders, ops, dl.entire(), name);
345            });
346        }
347        TokenTree::Delimited(.., ref del) => {
348            check_nested_occurrences(psess, node_id, &del.tts, macros, binders, ops, guar);
349        }
350        TokenTree::Sequence(_, ref seq) => {
351            let ops = ops.push(seq.kleene);
352            check_nested_occurrences(psess, node_id, &seq.tts, macros, binders, &ops, guar);
353        }
354    }
355}
356
357/// Represents the processed prefix of a nested macro.
358#[derive(Clone, Copy, PartialEq, Eq)]
359enum NestedMacroState {
360    /// Nothing that matches a nested macro definition was processed yet.
361    Empty,
362    /// The token `macro_rules` was processed.
363    MacroRules,
364    /// The tokens `macro_rules!` were processed.
365    MacroRulesNot,
366    /// The tokens `macro_rules!` followed by a name were processed. The name may be either directly
367    /// an identifier or a meta-variable (that hopefully would be instantiated by an identifier).
368    MacroRulesNotName,
369    /// The keyword `macro` was processed.
370    Macro,
371    /// The keyword `macro` followed by a name was processed.
372    MacroName,
373    /// The keyword `macro` followed by a name and a token delimited by parentheses was processed.
374    MacroNameParen,
375}
376
377/// Checks `tts` as part of the RHS of a macro definition, tries to recognize nested macro
378/// definitions, and sets `valid` to false in case of errors.
379///
380/// Arguments:
381/// - `psess` is used to emit diagnostics and lints
382/// - `node_id` is used to emit lints
383/// - `tts` is checked as part of a RHS and may contain macro definitions
384/// - `macros` is the stack of possible outer macros
385/// - `binders` contains the binders of the associated LHS
386/// - `ops` is the stack of Kleene operators from the RHS
387/// - `guar` is set in case of errors
388fn check_nested_occurrences(
389    psess: &ParseSess,
390    node_id: NodeId,
391    tts: &[TokenTree],
392    macros: &Stack<'_, MacroState<'_>>,
393    binders: &Binders,
394    ops: &Stack<'_, KleeneToken>,
395    guar: &mut Option<ErrorGuaranteed>,
396) {
397    let mut state = NestedMacroState::Empty;
398    let nested_macros = macros.push(MacroState { binders, ops: ops.into() });
399    let mut nested_binders = Binders::default();
400    for tt in tts {
401        match (state, tt) {
402            (
403                NestedMacroState::Empty,
404                &TokenTree::Token(Token { kind: TokenKind::Ident(name, IdentIsRaw::No), .. }),
405            ) => {
406                if name == kw::MacroRules {
407                    state = NestedMacroState::MacroRules;
408                } else if name == kw::Macro {
409                    state = NestedMacroState::Macro;
410                }
411            }
412            (
413                NestedMacroState::MacroRules,
414                &TokenTree::Token(Token { kind: TokenKind::Bang, .. }),
415            ) => {
416                state = NestedMacroState::MacroRulesNot;
417            }
418            (
419                NestedMacroState::MacroRulesNot,
420                &TokenTree::Token(Token { kind: TokenKind::Ident(..), .. }),
421            ) => {
422                state = NestedMacroState::MacroRulesNotName;
423            }
424            (NestedMacroState::MacroRulesNot, &TokenTree::MetaVar(..)) => {
425                state = NestedMacroState::MacroRulesNotName;
426                // We check that the meta-variable is correctly used.
427                check_occurrences(psess, node_id, tt, macros, binders, ops, guar);
428            }
429            (NestedMacroState::MacroRulesNotName, TokenTree::Delimited(.., del))
430            | (NestedMacroState::MacroName, TokenTree::Delimited(.., del))
431                if del.delim == Delimiter::Brace =>
432            {
433                let macro_rules = state == NestedMacroState::MacroRulesNotName;
434                state = NestedMacroState::Empty;
435                let rest =
436                    check_nested_macro(psess, node_id, macro_rules, &del.tts, &nested_macros, guar);
437                // If we did not check the whole macro definition, then check the rest as if outside
438                // the macro definition.
439                check_nested_occurrences(
440                    psess,
441                    node_id,
442                    &del.tts[rest..],
443                    macros,
444                    binders,
445                    ops,
446                    guar,
447                );
448            }
449            (
450                NestedMacroState::Macro,
451                &TokenTree::Token(Token { kind: TokenKind::Ident(..), .. }),
452            ) => {
453                state = NestedMacroState::MacroName;
454            }
455            (NestedMacroState::Macro, &TokenTree::MetaVar(..)) => {
456                state = NestedMacroState::MacroName;
457                // We check that the meta-variable is correctly used.
458                check_occurrences(psess, node_id, tt, macros, binders, ops, guar);
459            }
460            (NestedMacroState::MacroName, TokenTree::Delimited(.., del))
461                if del.delim == Delimiter::Parenthesis =>
462            {
463                state = NestedMacroState::MacroNameParen;
464                nested_binders = Binders::default();
465                check_binders(
466                    psess,
467                    node_id,
468                    tt,
469                    &nested_macros,
470                    &mut nested_binders,
471                    &Stack::Empty,
472                    guar,
473                );
474            }
475            (NestedMacroState::MacroNameParen, TokenTree::Delimited(.., del))
476                if del.delim == Delimiter::Brace =>
477            {
478                state = NestedMacroState::Empty;
479                check_occurrences(
480                    psess,
481                    node_id,
482                    tt,
483                    &nested_macros,
484                    &nested_binders,
485                    &Stack::Empty,
486                    guar,
487                );
488            }
489            (_, tt) => {
490                state = NestedMacroState::Empty;
491                check_occurrences(psess, node_id, tt, macros, binders, ops, guar);
492            }
493        }
494    }
495}
496
497/// Checks the body of nested macro, returns where the check stopped, and sets `valid` to false in
498/// case of errors.
499///
500/// The token trees are checked as long as they look like a list of (LHS) => {RHS} token trees. This
501/// check is a best-effort to detect a macro definition. It returns the position in `tts` where we
502/// stopped checking because we detected we were not in a macro definition anymore.
503///
504/// Arguments:
505/// - `psess` is used to emit diagnostics and lints
506/// - `node_id` is used to emit lints
507/// - `macro_rules` specifies whether the macro is `macro_rules`
508/// - `tts` is checked as a list of (LHS) => {RHS}
509/// - `macros` is the stack of outer macros
510/// - `guar` is set in case of errors
511fn check_nested_macro(
512    psess: &ParseSess,
513    node_id: NodeId,
514    macro_rules: bool,
515    tts: &[TokenTree],
516    macros: &Stack<'_, MacroState<'_>>,
517    guar: &mut Option<ErrorGuaranteed>,
518) -> usize {
519    let n = tts.len();
520    let mut i = 0;
521    let separator = if macro_rules { TokenKind::Semi } else { TokenKind::Comma };
522    loop {
523        // We expect 3 token trees: `(LHS) => {RHS}`. The separator is checked after.
524        if i + 2 >= n
525            || !tts[i].is_delimited()
526            || !tts[i + 1].is_token(&TokenKind::FatArrow)
527            || !tts[i + 2].is_delimited()
528        {
529            break;
530        }
531        let lhs = &tts[i];
532        let rhs = &tts[i + 2];
533        let mut binders = Binders::default();
534        check_binders(psess, node_id, lhs, macros, &mut binders, &Stack::Empty, guar);
535        check_occurrences(psess, node_id, rhs, macros, &binders, &Stack::Empty, guar);
536        // Since the last semicolon is optional for `macro_rules` macros and decl_macro are not terminated,
537        // we increment our checked position by how many token trees we already checked (the 3
538        // above) before checking for the separator.
539        i += 3;
540        if i == n || !tts[i].is_token(&separator) {
541            break;
542        }
543        // We increment our checked position for the semicolon.
544        i += 1;
545    }
546    i
547}
548
549/// Checks that a meta-variable occurrence is valid.
550///
551/// Arguments:
552/// - `psess` is used to emit diagnostics and lints
553/// - `node_id` is used to emit lints
554/// - `macros` is the stack of possible outer macros
555/// - `binders` contains the binders of the associated LHS
556/// - `ops` is the stack of Kleene operators from the RHS
557/// - `span` is the span of the meta-variable to check
558/// - `name` is the name of the meta-variable to check
559fn check_ops_is_prefix(
560    psess: &ParseSess,
561    node_id: NodeId,
562    macros: &Stack<'_, MacroState<'_>>,
563    binders: &Binders,
564    ops: &Stack<'_, KleeneToken>,
565    span: Span,
566    name: MacroRulesNormalizedIdent,
567) {
568    let macros = macros.push(MacroState { binders, ops: ops.into() });
569    // Accumulates the stacks the operators of each state until (and including when) the
570    // meta-variable is found. The innermost stack is first.
571    let mut acc: SmallVec<[&SmallVec<[KleeneToken; 1]>; 1]> = SmallVec::new();
572    for state in &macros {
573        acc.push(&state.ops);
574        if let Some(binder) = state.binders.get(&name) {
575            // This variable concatenates the stack of operators from the RHS of the LHS where the
576            // meta-variable was defined to where it is used (in possibly nested macros). The
577            // outermost operator is first.
578            let mut occurrence_ops: SmallVec<[KleeneToken; 2]> = SmallVec::new();
579            // We need to iterate from the end to start with outermost stack.
580            for ops in acc.iter().rev() {
581                occurrence_ops.extend_from_slice(ops);
582            }
583            ops_is_prefix(psess, node_id, span, name, &binder.ops, &occurrence_ops);
584            return;
585        }
586    }
587    buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name));
588}
589
590/// Returns whether `binder_ops` is a prefix of `occurrence_ops`.
591///
592/// The stack of Kleene operators of a meta-variable occurrence just needs to have the stack of
593/// Kleene operators of its binder as a prefix.
594///
595/// Consider $i in the following example:
596/// ```ignore (illustrative)
597/// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* }
598/// ```
599/// It occurs under the Kleene stack ["*", "+"] and is bound under ["*"] only.
600///
601/// Arguments:
602/// - `psess` is used to emit diagnostics and lints
603/// - `node_id` is used to emit lints
604/// - `span` is the span of the meta-variable being check
605/// - `name` is the name of the meta-variable being check
606/// - `binder_ops` is the stack of Kleene operators for the binder
607/// - `occurrence_ops` is the stack of Kleene operators for the occurrence
608fn ops_is_prefix(
609    psess: &ParseSess,
610    node_id: NodeId,
611    span: Span,
612    name: MacroRulesNormalizedIdent,
613    binder_ops: &[KleeneToken],
614    occurrence_ops: &[KleeneToken],
615) {
616    for (i, binder) in binder_ops.iter().enumerate() {
617        if i >= occurrence_ops.len() {
618            let mut span = MultiSpan::from_span(span);
619            span.push_span_label(binder.span, "expected repetition");
620            buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name));
621            return;
622        }
623        let occurrence = &occurrence_ops[i];
624        if occurrence.op != binder.op {
625            let mut span = MultiSpan::from_span(span);
626            span.push_span_label(binder.span, "expected repetition");
627            span.push_span_label(occurrence.span, "conflicting repetition");
628            buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator);
629            return;
630        }
631    }
632}
633
634fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) {
635    // Macros loaded from other crates have dummy node ids.
636    if node_id != DUMMY_NODE_ID {
637        psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag);
638    }
639}