Module rustc_expand::mbe::macro_check
source · Expand description
Checks that meta-variables in macro definition are correctly declared and used.
What is checked
Meta-variables must not be bound twice
macro_rules! foo { ($x:tt $x:tt) => { $x }; }
This check is sound (no false-negative) and complete (no false-positive).
Meta-variables must not be free
macro_rules! foo { () => { $x }; }
This check is also done at macro instantiation but only if the branch is taken.
Meta-variables must repeat at least as many times as their binder
macro_rules! foo { ($($x:tt)*) => { $x }; }
This check is also done at macro instantiation but only if the branch is taken.
Meta-variables must repeat with the same Kleene operators as their binder
macro_rules! foo { ($($x:tt)+) => { $($x)* }; }
This check is not done at macro instantiation.
Disclaimer
In the presence of nested macros (a macro defined in a macro), those checks may have false positives and false negatives. We try to detect those cases by recognizing potential macro definitions in RHSes, but nested macros may be hidden through the use of particular values of meta-variables.
Examples of false positive
False positives can come from cases where we don’t recognize a nested macro, because it depends
on particular values of meta-variables. In the following example, we think both instances of
$x
are free, which is a correct statement if $name
is anything but macro_rules
. But when
$name
is macro_rules
, like in the instantiation below, then $x:tt
is actually a binder of
the nested macro and $x
is bound to it.
macro_rules! foo { ($name:ident) => { $name! bar { ($x:tt) => { $x }; } }; }
foo!(macro_rules);
False positives can also come from cases where we think there is a nested macro while there
isn’t. In the following example, we think $x
is free, which is incorrect because bar
is not
a nested macro since it is not evaluated as code by stringify!
.
macro_rules! foo { () => { stringify!(macro_rules! bar { () => { $x }; }) }; }
Examples of false negative
False negatives can come from cases where we don’t recognize a meta-variable, because it depends
on particular values of meta-variables. In the following examples, we don’t see that if $d
is
instantiated with $
then $d z
becomes $z
in the nested macro definition and is thus a free
meta-variable. Note however, that if foo
is instantiated, then we would check the definition
of bar
and would see the issue.
macro_rules! foo { ($d:tt) => { macro_rules! bar { ($y:tt) => { $d z }; } }; }
How it is checked
There are 3 main functions: check_binders
, check_occurrences
, and check_nested_macro
. They
all need some kind of environment.
Environments
Environments are used to pass information.
From LHS to RHS
When checking a LHS with check_binders
, we produce (and use) an environment for binders,
namely Binders
. This is a mapping from binder name to information about that binder: the span
of the binder for error messages and the stack of Kleene operators under which it was bound in
the LHS.
This environment is used by both the LHS and RHS. The LHS uses it to detect duplicate binders. The RHS uses it to detect the other errors.
From outer macro to inner macro
When checking the RHS of an outer macro and we detect a nested macro definition, we push the
current state, namely MacroState
, to an environment of nested macro definitions. Each state
stores the LHS binders when entering the macro definition as well as the stack of Kleene
operators under which the inner macro is defined in the RHS.
This environment is a stack representing the nesting of macro definitions. As such, the stack of Kleene operators under which a meta-variable is repeating is the concatenation of the stacks stored when entering a macro definition starting from the state in which the meta-variable is bound.
Structs
Enums
Functions
lhs
as part of the LHS of a macro definition, extends binders
with new binders, and
sets valid
to false in case of errors.valid
to false in
case of errors.tts
as part of the RHS of a macro definition, tries to recognize nested macro
definitions, and sets valid
to false in case of errors.rhs
as part of the RHS of a macro definition and sets valid
to false in case of
errors.binder_ops
is a prefix of occurrence_ops
.