1 //! Checks that meta-variables in macro definition are correctly declared and used.
5 //! ## Meta-variables must not be bound twice
8 //! macro_rules! foo { ($x:tt $x:tt) => { $x }; }
11 //! This check is sound (no false-negative) and complete (no false-positive).
13 //! ## Meta-variables must not be free
16 //! macro_rules! foo { () => { $x }; }
19 //! This check is also done at macro instantiation but only if the branch is taken.
21 //! ## Meta-variables must repeat at least as many times as their binder
24 //! macro_rules! foo { ($($x:tt)*) => { $x }; }
27 //! This check is also done at macro instantiation but only if the branch is taken.
29 //! ## Meta-variables must repeat with the same Kleene operators as their binder
32 //! macro_rules! foo { ($($x:tt)+) => { $($x)* }; }
35 //! This check is not done at macro instantiation.
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
44 //! ## Examples of false positive
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.
53 //! macro_rules! foo { ($name:ident) => { $name! bar { ($x:tt) => { $x }; } }; }
54 //! foo!(macro_rules);
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!`.
62 //! macro_rules! foo { () => { stringify!(macro_rules! bar { () => { $x }; }) }; }
65 //! ## Examples of false negative
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.
74 //! macro_rules! foo { ($d:tt) => { macro_rules! bar { ($y:tt) => { $d z }; } }; }
77 //! # How it is checked
79 //! There are 3 main functions: `check_binders`, `check_occurrences`, and `check_nested_macro`. They
80 //! all need some kind of environment.
84 //! Environments are used to pass information.
86 //! ### From LHS to RHS
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
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.
96 //! ### From outer macro to inner macro
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.
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
107 use crate::mbe
::{KleeneToken, TokenTree}
;
109 use rustc_ast
::token
::{DelimToken, Token, TokenKind}
;
110 use rustc_ast
::{NodeId, DUMMY_NODE_ID}
;
111 use rustc_data_structures
::fx
::FxHashMap
;
112 use rustc_session
::lint
::builtin
::META_VARIABLE_MISUSE
;
113 use rustc_session
::parse
::ParseSess
;
114 use rustc_span
::symbol
::kw
;
115 use rustc_span
::{symbol::MacroRulesNormalizedIdent, MultiSpan, Span}
;
117 use smallvec
::SmallVec
;
121 /// Stack represented as linked list.
123 /// Those are used for environments because they grow incrementally and are not mutable.
127 /// A non-empty stack.
131 /// The previous elements.
132 prev
: &'a Stack
<'a
, T
>,
136 impl<'a
, T
> Stack
<'a
, T
> {
137 /// Returns whether a stack is empty.
138 fn is_empty(&self) -> bool
{
139 matches
!(*self, Stack
::Empty
)
142 /// Returns a new stack with an element of top.
143 fn push(&'a
self, top
: T
) -> Stack
<'a
, T
> {
144 Stack
::Push { top, prev: self }
148 impl<'a
, T
> Iterator
for &'a Stack
<'a
, T
> {
151 // Iterates from top to bottom of the stack.
152 fn next(&mut self) -> Option
<&'a T
> {
154 Stack
::Empty
=> None
,
155 Stack
::Push { ref top, ref prev }
=> {
163 impl From
<&Stack
<'_
, KleeneToken
>> for SmallVec
<[KleeneToken
; 1]> {
164 fn from(ops
: &Stack
<'_
, KleeneToken
>) -> SmallVec
<[KleeneToken
; 1]> {
165 let mut ops
: SmallVec
<[KleeneToken
; 1]> = ops
.cloned().collect();
166 // The stack is innermost on top. We want outermost first.
172 /// Information attached to a meta-variable binder in LHS.
174 /// The span of the meta-variable in LHS.
176 /// The stack of Kleene operators (outermost first).
177 ops
: SmallVec
<[KleeneToken
; 1]>,
180 /// An environment of meta-variables to their binder information.
181 type Binders
= FxHashMap
<MacroRulesNormalizedIdent
, BinderInfo
>;
183 /// The state at which we entered a macro definition in the RHS of another macro definition.
184 struct MacroState
<'a
> {
185 /// The binders of the branch where we entered the macro definition.
186 binders
: &'a Binders
,
187 /// The stack of Kleene operators (outermost first) where we entered the macro definition.
188 ops
: SmallVec
<[KleeneToken
; 1]>,
191 /// Checks that meta-variables are used correctly in a macro definition.
194 /// - `sess` is used to emit diagnostics and lints
195 /// - `node_id` is used to emit lints
196 /// - `span` is used when no spans are available
197 /// - `lhses` and `rhses` should have the same length and represent the macro definition
198 pub(super) fn check_meta_variables(
205 if lhses
.len() != rhses
.len() {
206 sess
.span_diagnostic
.span_bug(span
, "length mismatch between LHSes and RHSes")
208 let mut valid
= true;
209 for (lhs
, rhs
) in iter
::zip(lhses
, rhses
) {
210 let mut binders
= Binders
::default();
211 check_binders(sess
, node_id
, lhs
, &Stack
::Empty
, &mut binders
, &Stack
::Empty
, &mut valid
);
212 check_occurrences(sess
, node_id
, rhs
, &Stack
::Empty
, &binders
, &Stack
::Empty
, &mut valid
);
217 /// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and
218 /// sets `valid` to false in case of errors.
221 /// - `sess` is used to emit diagnostics and lints
222 /// - `node_id` is used to emit lints
223 /// - `lhs` is checked as part of a LHS
224 /// - `macros` is the stack of possible outer macros
225 /// - `binders` contains the binders of the LHS
226 /// - `ops` is the stack of Kleene operators from the LHS
227 /// - `valid` is set in case of errors
232 macros
: &Stack
<'_
, MacroState
<'_
>>,
233 binders
: &mut Binders
,
234 ops
: &Stack
<'_
, KleeneToken
>,
238 TokenTree
::Token(..) => {}
239 // This can only happen when checking a nested macro because this LHS is then in the RHS of
240 // the outer macro. See ui/macros/macro-of-higher-order.rs where $y:$fragment in the
241 // LHS of the nested macro (and RHS of the outer macro) is parsed as MetaVar(y) Colon
242 // MetaVar(fragment) and not as MetaVarDecl(y, fragment).
243 TokenTree
::MetaVar(span
, name
) => {
244 if macros
.is_empty() {
245 sess
.span_diagnostic
.span_bug(span
, "unexpected MetaVar in lhs");
247 let name
= MacroRulesNormalizedIdent
::new(name
);
248 // There are 3 possibilities:
249 if let Some(prev_info
) = binders
.get(&name
) {
250 // 1. The meta-variable is already bound in the current LHS: This is an error.
251 let mut span
= MultiSpan
::from_span(span
);
252 span
.push_span_label(prev_info
.span
, "previous declaration".into());
253 buffer_lint(sess
, span
, node_id
, "duplicate matcher binding");
254 } else if get_binder_info(macros
, binders
, name
).is_none() {
255 // 2. The meta-variable is free: This is a binder.
256 binders
.insert(name
, BinderInfo { span, ops: ops.into() }
);
258 // 3. The meta-variable is bound: This is an occurrence.
259 check_occurrences(sess
, node_id
, lhs
, macros
, binders
, ops
, valid
);
262 // Similarly, this can only happen when checking a toplevel macro.
263 TokenTree
::MetaVarDecl(span
, name
, _kind
) => {
264 if !macros
.is_empty() {
265 sess
.span_diagnostic
.span_bug(span
, "unexpected MetaVarDecl in nested lhs");
267 let name
= MacroRulesNormalizedIdent
::new(name
);
268 if let Some(prev_info
) = get_binder_info(macros
, binders
, name
) {
269 // Duplicate binders at the top-level macro definition are errors. The lint is only
270 // for nested macro definitions.
272 .struct_span_err(span
, "duplicate matcher binding")
273 .span_label(span
, "duplicate binding")
274 .span_label(prev_info
.span
, "previous binding")
278 binders
.insert(name
, BinderInfo { span, ops: ops.into() }
);
281 TokenTree
::Delimited(_
, ref del
) => {
283 check_binders(sess
, node_id
, tt
, macros
, binders
, ops
, valid
);
286 TokenTree
::Sequence(_
, ref seq
) => {
287 let ops
= ops
.push(seq
.kleene
);
289 check_binders(sess
, node_id
, tt
, macros
, binders
, &ops
, valid
);
295 /// Returns the binder information of a meta-variable.
298 /// - `macros` is the stack of possible outer macros
299 /// - `binders` contains the current binders
300 /// - `name` is the name of the meta-variable we are looking for
301 fn get_binder_info
<'a
>(
302 mut macros
: &'a Stack
<'a
, MacroState
<'a
>>,
303 binders
: &'a Binders
,
304 name
: MacroRulesNormalizedIdent
,
305 ) -> Option
<&'a BinderInfo
> {
306 binders
.get(&name
).or_else(|| macros
.find_map(|state
| state
.binders
.get(&name
)))
309 /// Checks `rhs` as part of the RHS of a macro definition and sets `valid` to false in case of
313 /// - `sess` is used to emit diagnostics and lints
314 /// - `node_id` is used to emit lints
315 /// - `rhs` is checked as part of a RHS
316 /// - `macros` is the stack of possible outer macros
317 /// - `binders` contains the binders of the associated LHS
318 /// - `ops` is the stack of Kleene operators from the RHS
319 /// - `valid` is set in case of errors
320 fn check_occurrences(
324 macros
: &Stack
<'_
, MacroState
<'_
>>,
326 ops
: &Stack
<'_
, KleeneToken
>,
330 TokenTree
::Token(..) => {}
331 TokenTree
::MetaVarDecl(span
, _name
, _kind
) => {
332 sess
.span_diagnostic
.span_bug(span
, "unexpected MetaVarDecl in rhs")
334 TokenTree
::MetaVar(span
, name
) => {
335 let name
= MacroRulesNormalizedIdent
::new(name
);
336 check_ops_is_prefix(sess
, node_id
, macros
, binders
, ops
, span
, name
);
338 TokenTree
::Delimited(_
, ref del
) => {
339 check_nested_occurrences(sess
, node_id
, &del
.tts
, macros
, binders
, ops
, valid
);
341 TokenTree
::Sequence(_
, ref seq
) => {
342 let ops
= ops
.push(seq
.kleene
);
343 check_nested_occurrences(sess
, node_id
, &seq
.tts
, macros
, binders
, &ops
, valid
);
348 /// Represents the processed prefix of a nested macro.
349 #[derive(Clone, Copy, PartialEq, Eq)]
350 enum NestedMacroState
{
351 /// Nothing that matches a nested macro definition was processed yet.
353 /// The token `macro_rules` was processed.
355 /// The tokens `macro_rules!` were processed.
357 /// The tokens `macro_rules!` followed by a name were processed. The name may be either directly
358 /// an identifier or a meta-variable (that hopefully would be instantiated by an identifier).
360 /// The keyword `macro` was processed.
362 /// The keyword `macro` followed by a name was processed.
364 /// The keyword `macro` followed by a name and a token delimited by parentheses was processed.
368 /// Checks `tts` as part of the RHS of a macro definition, tries to recognize nested macro
369 /// definitions, and sets `valid` to false in case of errors.
372 /// - `sess` is used to emit diagnostics and lints
373 /// - `node_id` is used to emit lints
374 /// - `tts` is checked as part of a RHS and may contain macro definitions
375 /// - `macros` is the stack of possible outer macros
376 /// - `binders` contains the binders of the associated LHS
377 /// - `ops` is the stack of Kleene operators from the RHS
378 /// - `valid` is set in case of errors
379 fn check_nested_occurrences(
383 macros
: &Stack
<'_
, MacroState
<'_
>>,
385 ops
: &Stack
<'_
, KleeneToken
>,
388 let mut state
= NestedMacroState
::Empty
;
389 let nested_macros
= macros
.push(MacroState { binders, ops: ops.into() }
);
390 let mut nested_binders
= Binders
::default();
394 NestedMacroState
::Empty
,
395 &TokenTree
::Token(Token { kind: TokenKind::Ident(name, false), .. }
),
397 if name
== kw
::MacroRules
{
398 state
= NestedMacroState
::MacroRules
;
399 } else if name
== kw
::Macro
{
400 state
= NestedMacroState
::Macro
;
404 NestedMacroState
::MacroRules
,
405 &TokenTree
::Token(Token { kind: TokenKind::Not, .. }
),
407 state
= NestedMacroState
::MacroRulesNot
;
410 NestedMacroState
::MacroRulesNot
,
411 &TokenTree
::Token(Token { kind: TokenKind::Ident(..), .. }
),
413 state
= NestedMacroState
::MacroRulesNotName
;
415 (NestedMacroState
::MacroRulesNot
, &TokenTree
::MetaVar(..)) => {
416 state
= NestedMacroState
::MacroRulesNotName
;
417 // We check that the meta-variable is correctly used.
418 check_occurrences(sess
, node_id
, tt
, macros
, binders
, ops
, valid
);
420 (NestedMacroState
::MacroRulesNotName
, &TokenTree
::Delimited(_
, ref del
))
421 | (NestedMacroState
::MacroName
, &TokenTree
::Delimited(_
, ref del
))
422 if del
.delim
== DelimToken
::Brace
=>
424 let macro_rules
= state
== NestedMacroState
::MacroRulesNotName
;
425 state
= NestedMacroState
::Empty
;
427 check_nested_macro(sess
, node_id
, macro_rules
, &del
.tts
, &nested_macros
, valid
);
428 // If we did not check the whole macro definition, then check the rest as if outside
429 // the macro definition.
430 check_nested_occurrences(
441 NestedMacroState
::Macro
,
442 &TokenTree
::Token(Token { kind: TokenKind::Ident(..), .. }
),
444 state
= NestedMacroState
::MacroName
;
446 (NestedMacroState
::Macro
, &TokenTree
::MetaVar(..)) => {
447 state
= NestedMacroState
::MacroName
;
448 // We check that the meta-variable is correctly used.
449 check_occurrences(sess
, node_id
, tt
, macros
, binders
, ops
, valid
);
451 (NestedMacroState
::MacroName
, &TokenTree
::Delimited(_
, ref del
))
452 if del
.delim
== DelimToken
::Paren
=>
454 state
= NestedMacroState
::MacroNameParen
;
455 nested_binders
= Binders
::default();
466 (NestedMacroState
::MacroNameParen
, &TokenTree
::Delimited(_
, ref del
))
467 if del
.delim
== DelimToken
::Brace
=>
469 state
= NestedMacroState
::Empty
;
481 state
= NestedMacroState
::Empty
;
482 check_occurrences(sess
, node_id
, tt
, macros
, binders
, ops
, valid
);
488 /// Checks the body of nested macro, returns where the check stopped, and sets `valid` to false in
491 /// The token trees are checked as long as they look like a list of (LHS) => {RHS} token trees. This
492 /// check is a best-effort to detect a macro definition. It returns the position in `tts` where we
493 /// stopped checking because we detected we were not in a macro definition anymore.
496 /// - `sess` is used to emit diagnostics and lints
497 /// - `node_id` is used to emit lints
498 /// - `macro_rules` specifies whether the macro is `macro_rules`
499 /// - `tts` is checked as a list of (LHS) => {RHS}
500 /// - `macros` is the stack of outer macros
501 /// - `valid` is set in case of errors
502 fn check_nested_macro(
507 macros
: &Stack
<'_
, MacroState
<'_
>>,
512 let separator
= if macro_rules { TokenKind::Semi }
else { TokenKind::Comma }
;
514 // We expect 3 token trees: `(LHS) => {RHS}`. The separator is checked after.
516 || !tts
[i
].is_delimited()
517 || !tts
[i
+ 1].is_token(&TokenKind
::FatArrow
)
518 || !tts
[i
+ 2].is_delimited()
523 let rhs
= &tts
[i
+ 2];
524 let mut binders
= Binders
::default();
525 check_binders(sess
, node_id
, lhs
, macros
, &mut binders
, &Stack
::Empty
, valid
);
526 check_occurrences(sess
, node_id
, rhs
, macros
, &binders
, &Stack
::Empty
, valid
);
527 // Since the last semicolon is optional for `macro_rules` macros and decl_macro are not terminated,
528 // we increment our checked position by how many token trees we already checked (the 3
529 // above) before checking for the separator.
531 if i
== n
|| !tts
[i
].is_token(&separator
) {
534 // We increment our checked position for the semicolon.
540 /// Checks that a meta-variable occurrence is valid.
543 /// - `sess` is used to emit diagnostics and lints
544 /// - `node_id` is used to emit lints
545 /// - `macros` is the stack of possible outer macros
546 /// - `binders` contains the binders of the associated LHS
547 /// - `ops` is the stack of Kleene operators from the RHS
548 /// - `span` is the span of the meta-variable to check
549 /// - `name` is the name of the meta-variable to check
550 fn check_ops_is_prefix(
553 macros
: &Stack
<'_
, MacroState
<'_
>>,
555 ops
: &Stack
<'_
, KleeneToken
>,
557 name
: MacroRulesNormalizedIdent
,
559 let macros
= macros
.push(MacroState { binders, ops: ops.into() }
);
560 // Accumulates the stacks the operators of each state until (and including when) the
561 // meta-variable is found. The innermost stack is first.
562 let mut acc
: SmallVec
<[&SmallVec
<[KleeneToken
; 1]>; 1]> = SmallVec
::new();
563 for state
in ¯os
{
564 acc
.push(&state
.ops
);
565 if let Some(binder
) = state
.binders
.get(&name
) {
566 // This variable concatenates the stack of operators from the RHS of the LHS where the
567 // meta-variable was defined to where it is used (in possibly nested macros). The
568 // outermost operator is first.
569 let mut occurrence_ops
: SmallVec
<[KleeneToken
; 2]> = SmallVec
::new();
570 // We need to iterate from the end to start with outermost stack.
571 for ops
in acc
.iter().rev() {
572 occurrence_ops
.extend_from_slice(ops
);
574 ops_is_prefix(sess
, node_id
, span
, name
, &binder
.ops
, &occurrence_ops
);
578 buffer_lint(sess
, span
.into(), node_id
, &format
!("unknown macro variable `{}`", name
));
581 /// Returns whether `binder_ops` is a prefix of `occurrence_ops`.
583 /// The stack of Kleene operators of a meta-variable occurrence just needs to have the stack of
584 /// Kleene operators of its binder as a prefix.
586 /// Consider $i in the following example:
588 /// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* }
590 /// It occurs under the Kleene stack ["*", "+"] and is bound under ["*"] only.
593 /// - `sess` is used to emit diagnostics and lints
594 /// - `node_id` is used to emit lints
595 /// - `span` is the span of the meta-variable being check
596 /// - `name` is the name of the meta-variable being check
597 /// - `binder_ops` is the stack of Kleene operators for the binder
598 /// - `occurrence_ops` is the stack of Kleene operators for the occurrence
603 name
: MacroRulesNormalizedIdent
,
604 binder_ops
: &[KleeneToken
],
605 occurrence_ops
: &[KleeneToken
],
607 for (i
, binder
) in binder_ops
.iter().enumerate() {
608 if i
>= occurrence_ops
.len() {
609 let mut span
= MultiSpan
::from_span(span
);
610 span
.push_span_label(binder
.span
, "expected repetition".into());
611 let message
= &format
!("variable '{}' is still repeating at this depth", name
);
612 buffer_lint(sess
, span
, node_id
, message
);
615 let occurrence
= &occurrence_ops
[i
];
616 if occurrence
.op
!= binder
.op
{
617 let mut span
= MultiSpan
::from_span(span
);
618 span
.push_span_label(binder
.span
, "expected repetition".into());
619 span
.push_span_label(occurrence
.span
, "conflicting repetition".into());
620 let message
= "meta-variable repeats with different Kleene operator";
621 buffer_lint(sess
, span
, node_id
, message
);
627 fn buffer_lint(sess
: &ParseSess
, span
: MultiSpan
, node_id
: NodeId
, message
: &str) {
628 // Macros loaded from other crates have dummy node ids.
629 if node_id
!= DUMMY_NODE_ID
{
630 sess
.buffer_lint(&META_VARIABLE_MISUSE
, span
, node_id
, message
);