How to understand "the second macro will see an opaque AST" in the reference?

There is no bug here, it's all working as intended. I've explained this phenomenon several times already, posting it here for reference:






From your first post, it looks like you got the :tt / :ident vs. matcher situation reversed:

  • If foo! were to capture a :tt, :ident (or :lifetime), then you'd be able to transparently match against the exact token capture within bar!.

  • But if foo! uses a high-level auto-grouped capture such as :expr, then that metavariable will thenceforward represent an invisibly-parenthesized group. It's thus a single token tree (quite handy for recursing, btw), but one which appears opaque to the second macro, in the same fashion that bar! { ( quux ) } will not be a valid call if bar! were to expect a quux argument.

    So, bar! can only handle a higher-level-grouped metavariable from foo! if and only if:

    • It takes a :tt, since all (parenthesized, braced, …) groups, including the "invisibly parenthesized" ones, are single token-trees each, or if it takes some other higher-level capture compatible with the first one: an :expr is compatible with an :expr (and more generally, for any kind, a :kind is always compatible with :kind), and then you can have a :path be compatible with :expr, or with :ty, or with :pat; an :item is compatible with a :stmt , a :block is compatible with an :expr, etc.

    See what the reference has to say about the Rust grammar to better figure out these compatibilities.

4 Likes