[−][src]Module rustc_ap_syntax::ext::tt::macro_check
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.
Functions
check_meta_variables | Checks that meta-variables are used correctly in a macro definition. |