rust/src/libsyntax/mut_visit.rs
David Wood 5c3d1e5d76 Separate variant id and variant constructor id.
This commit makes two changes - separating the `NodeId` that identifies
an enum variant from the `NodeId` that identifies the variant's
constructor; and no longer creating a `NodeId` for `Struct`-style enum
variants and structs.

Separation of the variant id and variant constructor id will allow the
rest of RFC 2008 to be implemented by lowering the visibility of the
variant's constructor without lowering the visbility of the variant
itself.

No longer creating a `NodeId` for `Struct`-style enum variants and
structs mostly simplifies logic as previously this `NodeId` wasn't used.
There were various cases where the `NodeId` wouldn't be used unless
there was an unit or tuple struct or enum variant but not all uses of
this `NodeId` had that condition, by removing this `NodeId`, this must
be explicitly dealt with. This change mostly applied cleanly, but there
were one or two cases in name resolution and one case in type check
where the existing logic required a id for `Struct`-style enum variants
and structs.
2019-03-24 12:10:16 +03:00

1342 lines
46 KiB
Rust

//! A MutVisitor represents an AST modification; it accepts an AST piece and
//! and mutates it in place. So, for instance, macro expansion is a MutVisitor
//! that walks over an AST and modifies it.
//!
//! Note: using a MutVisitor (other than the MacroExpander MutVisitor) on
//! an AST before macro expansion is probably a bad idea. For instance,
//! a MutVisitor renaming item names in a module will miss all of those
//! that are created by the expansion of a macro.
use crate::ast::*;
use crate::source_map::{Spanned, respan};
use crate::parse::token::{self, Token};
use crate::ptr::P;
use crate::symbol::keywords;
use crate::ThinVec;
use crate::tokenstream::*;
use crate::util::map_in_place::MapInPlace;
use smallvec::{smallvec, Array, SmallVec};
use syntax_pos::Span;
use rustc_data_structures::sync::Lrc;
use std::ops::DerefMut;
use std::{panic, process, ptr};
pub trait ExpectOne<A: Array> {
fn expect_one(self, err: &'static str) -> A::Item;
}
impl<A: Array> ExpectOne<A> for SmallVec<A> {
fn expect_one(self, err: &'static str) -> A::Item {
assert!(self.len() == 1, err);
self.into_iter().next().unwrap()
}
}
pub trait MutVisitor: Sized {
// Methods in this trait have one of three forms:
//
// fn visit_t(&mut self, t: &mut T); // common
// fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>; // rare
// fn filter_map_t(&mut self, t: T) -> Option<T>; // rarest
//
// Any additions to this trait should happen in form of a call to a public
// `noop_*` function that only calls out to the visitor again, not other
// `noop_*` functions. This is a necessary API workaround to the problem of
// not being able to call out to the super default method in an overridden
// default method.
//
// When writing these methods, it is better to use destructuring like this:
//
// fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
// visit_a(a);
// visit_b(b);
// }
//
// than to use field access like this:
//
// fn visit_abc(&mut self, abc: &mut ABC) {
// visit_a(&mut abc.a);
// visit_b(&mut abc.b);
// // ignore abc.c
// }
//
// As well as being more concise, the former is explicit about which fields
// are skipped. Furthermore, if a new field is added, the destructuring
// version will cause a compile error, which is good. In comparison, the
// field access version will continue working and it would be easy to
// forget to add handling for it.
fn visit_crate(&mut self, c: &mut Crate) {
noop_visit_crate(c, self)
}
fn visit_meta_list_item(&mut self, list_item: &mut NestedMetaItem) {
noop_visit_meta_list_item(list_item, self);
}
fn visit_meta_item(&mut self, meta_item: &mut MetaItem) {
noop_visit_meta_item(meta_item, self);
}
fn visit_use_tree(&mut self, use_tree: &mut UseTree) {
noop_visit_use_tree(use_tree, self);
}
fn flat_map_foreign_item(&mut self, ni: ForeignItem) -> SmallVec<[ForeignItem; 1]> {
noop_flat_map_foreign_item(ni, self)
}
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
noop_flat_map_item(i, self)
}
fn visit_fn_header(&mut self, header: &mut FnHeader) {
noop_visit_fn_header(header, self);
}
fn visit_struct_field(&mut self, sf: &mut StructField) {
noop_visit_struct_field(sf, self);
}
fn visit_item_kind(&mut self, i: &mut ItemKind) {
noop_visit_item_kind(i, self);
}
fn flat_map_trait_item(&mut self, i: TraitItem) -> SmallVec<[TraitItem; 1]> {
noop_flat_map_trait_item(i, self)
}
fn flat_map_impl_item(&mut self, i: ImplItem) -> SmallVec<[ImplItem; 1]> {
noop_flat_map_impl_item(i, self)
}
fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
noop_visit_fn_decl(d, self);
}
fn visit_asyncness(&mut self, a: &mut IsAsync) {
noop_visit_asyncness(a, self);
}
fn visit_block(&mut self, b: &mut P<Block>) {
noop_visit_block(b, self);
}
fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
noop_flat_map_stmt(s, self)
}
fn visit_arm(&mut self, a: &mut Arm) {
noop_visit_arm(a, self);
}
fn visit_guard(&mut self, g: &mut Guard) {
noop_visit_guard(g, self);
}
fn visit_pat(&mut self, p: &mut P<Pat>) {
noop_visit_pat(p, self);
}
fn visit_anon_const(&mut self, c: &mut AnonConst) {
noop_visit_anon_const(c, self);
}
fn visit_expr(&mut self, e: &mut P<Expr>) {
noop_visit_expr(e, self);
}
fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
noop_filter_map_expr(e, self)
}
fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
noop_visit_generic_arg(arg, self);
}
fn visit_ty(&mut self, t: &mut P<Ty>) {
noop_visit_ty(t, self);
}
fn visit_lifetime(&mut self, l: &mut Lifetime) {
noop_visit_lifetime(l, self);
}
fn visit_ty_binding(&mut self, t: &mut TypeBinding) {
noop_visit_ty_binding(t, self);
}
fn visit_mod(&mut self, m: &mut Mod) {
noop_visit_mod(m, self);
}
fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) {
noop_visit_foreign_mod(nm, self);
}
fn visit_variant(&mut self, v: &mut Variant) {
noop_visit_variant(v, self);
}
fn visit_ident(&mut self, i: &mut Ident) {
noop_visit_ident(i, self);
}
fn visit_path(&mut self, p: &mut Path) {
noop_visit_path(p, self);
}
fn visit_qself(&mut self, qs: &mut Option<QSelf>) {
noop_visit_qself(qs, self);
}
fn visit_generic_args(&mut self, p: &mut GenericArgs) {
noop_visit_generic_args(p, self);
}
fn visit_angle_bracketed_parameter_data(&mut self, p: &mut AngleBracketedArgs) {
noop_visit_angle_bracketed_parameter_data(p, self);
}
fn visit_parenthesized_parameter_data(&mut self, p: &mut ParenthesizedArgs) {
noop_visit_parenthesized_parameter_data(p, self);
}
fn visit_local(&mut self, l: &mut P<Local>) {
noop_visit_local(l, self);
}
fn visit_mac(&mut self, _mac: &mut Mac) {
panic!("visit_mac disabled by default");
// N.B., see note about macros above. If you really want a visitor that
// works on macros, use this definition in your trait impl:
// mut_visit::noop_visit_mac(_mac, self);
}
fn visit_macro_def(&mut self, def: &mut MacroDef) {
noop_visit_macro_def(def, self);
}
fn visit_label(&mut self, label: &mut Label) {
noop_visit_label(label, self);
}
fn visit_attribute(&mut self, at: &mut Attribute) {
noop_visit_attribute(at, self);
}
fn visit_arg(&mut self, a: &mut Arg) {
noop_visit_arg(a, self);
}
fn visit_generics(&mut self, generics: &mut Generics) {
noop_visit_generics(generics, self);
}
fn visit_trait_ref(&mut self, tr: &mut TraitRef) {
noop_visit_trait_ref(tr, self);
}
fn visit_poly_trait_ref(&mut self, p: &mut PolyTraitRef) {
noop_visit_poly_trait_ref(p, self);
}
fn visit_variant_data(&mut self, vdata: &mut VariantData) {
noop_visit_variant_data(vdata, self);
}
fn visit_generic_param(&mut self, param: &mut GenericParam) {
noop_visit_generic_param(param, self);
}
fn visit_generic_params(&mut self, params: &mut Vec<GenericParam>) {
noop_visit_generic_params(params, self);
}
fn visit_tt(&mut self, tt: &mut TokenTree) {
noop_visit_tt(tt, self);
}
fn visit_tts(&mut self, tts: &mut TokenStream) {
noop_visit_tts(tts, self);
}
fn visit_token(&mut self, t: &mut Token) {
noop_visit_token(t, self);
}
fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) {
noop_visit_interpolated(nt, self);
}
fn visit_param_bound(&mut self, tpb: &mut GenericBound) {
noop_visit_param_bound(tpb, self);
}
fn visit_mt(&mut self, mt: &mut MutTy) {
noop_visit_mt(mt, self);
}
fn visit_field(&mut self, field: &mut Field) {
noop_visit_field(field, self);
}
fn visit_where_clause(&mut self, where_clause: &mut WhereClause) {
noop_visit_where_clause(where_clause, self);
}
fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
noop_visit_where_predicate(where_predicate, self);
}
fn visit_vis(&mut self, vis: &mut Visibility) {
noop_visit_vis(vis, self);
}
fn visit_id(&mut self, _id: &mut NodeId) {
// Do nothing.
}
fn visit_span(&mut self, _sp: &mut Span) {
// Do nothing.
}
}
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
/// when using a `flat_map_*` or `filter_map_*` method within a `visit_`
/// method. Abort the program if the closure panics.
//
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_clobber<T, F>(t: &mut T, f: F) where F: FnOnce(T) -> T {
unsafe {
// Safe because `t` is used in a read-only fashion by `read()` before
// being overwritten by `write()`.
let old_t = ptr::read(t);
let new_t = panic::catch_unwind(panic::AssertUnwindSafe(|| f(old_t)))
.unwrap_or_else(|_| process::abort());
ptr::write(t, new_t);
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
pub fn visit_vec<T, F>(elems: &mut Vec<T>, mut visit_elem: F) where F: FnMut(&mut T) {
for elem in elems {
visit_elem(elem);
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
#[inline]
pub fn visit_opt<T, F>(opt: &mut Option<T>, mut visit_elem: F) where F: FnMut(&mut T) {
if let Some(elem) = opt {
visit_elem(elem);
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_attrs<T: MutVisitor>(attrs: &mut Vec<Attribute>, vis: &mut T) {
visit_vec(attrs, |attr| vis.visit_attribute(attr));
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_thin_attrs<T: MutVisitor>(attrs: &mut ThinVec<Attribute>, vis: &mut T) {
for attr in attrs.iter_mut() {
vis.visit_attribute(attr);
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_exprs<T: MutVisitor>(exprs: &mut Vec<P<Expr>>, vis: &mut T) {
exprs.flat_map_in_place(|expr| vis.filter_map_expr(expr))
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_bounds<T: MutVisitor>(bounds: &mut GenericBounds, vis: &mut T) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_method_sig<T: MutVisitor>(MethodSig { header, decl }: &mut MethodSig, vis: &mut T) {
vis.visit_fn_header(header);
vis.visit_fn_decl(decl);
}
pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
let UseTree { prefix, kind, span } = use_tree;
vis.visit_path(prefix);
match kind {
UseTreeKind::Simple(rename, id1, id2) => {
visit_opt(rename, |rename| vis.visit_ident(rename));
vis.visit_id(id1);
vis.visit_id(id2);
}
UseTreeKind::Nested(items) => {
for (tree, id) in items {
vis.visit_use_tree(tree);
vis.visit_id(id);
}
}
UseTreeKind::Glob => {}
}
vis.visit_span(span);
}
pub fn noop_visit_arm<T: MutVisitor>(Arm { attrs, pats, guard, body }: &mut Arm, vis: &mut T) {
visit_attrs(attrs, vis);
visit_vec(pats, |pat| vis.visit_pat(pat));
visit_opt(guard, |guard| vis.visit_guard(guard));
vis.visit_expr(body);
}
pub fn noop_visit_guard<T: MutVisitor>(g: &mut Guard, vis: &mut T) {
match g {
Guard::If(e) => vis.visit_expr(e),
}
}
pub fn noop_visit_ty_binding<T: MutVisitor>(TypeBinding { id, ident, ty, span }: &mut TypeBinding,
vis: &mut T) {
vis.visit_id(id);
vis.visit_ident(ident);
vis.visit_ty(ty);
vis.visit_span(span);
}
pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
let Ty { id, node, span } = ty.deref_mut();
vis.visit_id(id);
match node {
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err |
TyKind::Never | TyKind::CVarArgs => {}
TyKind::Slice(ty) => vis.visit_ty(ty),
TyKind::Ptr(mt) => vis.visit_mt(mt),
TyKind::Rptr(lt, mt) => {
visit_opt(lt, |lt| noop_visit_lifetime(lt, vis));
vis.visit_mt(mt);
}
TyKind::BareFn(bft) => {
let BareFnTy { unsafety: _, abi: _, generic_params, decl } = bft.deref_mut();
vis.visit_generic_params(generic_params);
vis.visit_fn_decl(decl);
}
TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
TyKind::Paren(ty) => vis.visit_ty(ty),
TyKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
}
TyKind::Array(ty, length) => {
vis.visit_ty(ty);
vis.visit_anon_const(length);
}
TyKind::Typeof(expr) => vis.visit_anon_const(expr),
TyKind::TraitObject(bounds, _syntax) =>
visit_vec(bounds, |bound| vis.visit_param_bound(bound)),
TyKind::ImplTrait(id, bounds) => {
vis.visit_id(id);
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
TyKind::Mac(mac) => vis.visit_mac(mac),
}
vis.visit_span(span);
}
pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
let ForeignMod { abi: _, items} = foreign_mod;
items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
}
pub fn noop_visit_variant<T: MutVisitor>(variant: &mut Variant, vis: &mut T) {
let Spanned { node: Variant_ { ident, attrs, id, data, disr_expr }, span } = variant;
vis.visit_ident(ident);
visit_attrs(attrs, vis);
vis.visit_id(id);
vis.visit_variant_data(data);
visit_opt(disr_expr, |disr_expr| vis.visit_anon_const(disr_expr));
vis.visit_span(span);
}
pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: &mut T) {
vis.visit_span(span);
}
pub fn noop_visit_path<T: MutVisitor>(Path { segments, span }: &mut Path, vis: &mut T) {
vis.visit_span(span);
for PathSegment { ident, id, args } in segments {
vis.visit_ident(ident);
vis.visit_id(id);
visit_opt(args, |args| vis.visit_generic_args(args));
}
}
pub fn noop_visit_qself<T: MutVisitor>(qself: &mut Option<QSelf>, vis: &mut T) {
visit_opt(qself, |QSelf { ty, path_span, position: _ }| {
vis.visit_ty(ty);
vis.visit_span(path_span);
})
}
pub fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vis: &mut T) {
match generic_args {
GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
}
}
pub fn noop_visit_generic_arg<T: MutVisitor>(arg: &mut GenericArg, vis: &mut T) {
match arg {
GenericArg::Lifetime(lt) => vis.visit_lifetime(lt),
GenericArg::Type(ty) => vis.visit_ty(ty),
GenericArg::Const(ct) => vis.visit_anon_const(ct),
}
}
pub fn noop_visit_angle_bracketed_parameter_data<T: MutVisitor>(data: &mut AngleBracketedArgs,
vis: &mut T) {
let AngleBracketedArgs { args, bindings, span } = data;
visit_vec(args, |arg| vis.visit_generic_arg(arg));
visit_vec(bindings, |binding| vis.visit_ty_binding(binding));
vis.visit_span(span);
}
pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(args: &mut ParenthesizedArgs,
vis: &mut T) {
let ParenthesizedArgs { inputs, output, span } = args;
visit_vec(inputs, |input| vis.visit_ty(input));
visit_opt(output, |output| vis.visit_ty(output));
vis.visit_span(span);
}
pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
let Local { id, pat, ty, init, span, attrs } = local.deref_mut();
vis.visit_id(id);
vis.visit_pat(pat);
visit_opt(ty, |ty| vis.visit_ty(ty));
visit_opt(init, |init| vis.visit_expr(init));
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
}
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
let Attribute { id: _, style: _, path, tokens, is_sugared_doc: _, span } = attr;
vis.visit_path(path);
vis.visit_tts(tokens);
vis.visit_span(span);
}
pub fn noop_visit_mac<T: MutVisitor>(Spanned { node, span }: &mut Mac, vis: &mut T) {
let Mac_ { path, delim: _, tts } = node;
vis.visit_path(path);
vis.visit_tts(tts);
vis.visit_span(span);
}
pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T) {
let MacroDef { tokens, legacy: _ } = macro_def;
vis.visit_tts(tokens);
}
pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
match li {
NestedMetaItem::MetaItem(mi) => vis.visit_meta_item(mi),
NestedMetaItem::Literal(_lit) => {}
}
}
pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
let MetaItem { path: _, node, span } = mi;
match node {
MetaItemKind::Word => {}
MetaItemKind::List(mis) => visit_vec(mis, |mi| vis.visit_meta_list_item(mi)),
MetaItemKind::NameValue(_s) => {}
}
vis.visit_span(span);
}
pub fn noop_visit_arg<T: MutVisitor>(Arg { id, pat, ty }: &mut Arg, vis: &mut T) {
vis.visit_id(id);
vis.visit_pat(pat);
vis.visit_ty(ty);
}
pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
match tt {
TokenTree::Token(span, tok) => {
vis.visit_span(span);
vis.visit_token(tok);
}
TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
vis.visit_span(open);
vis.visit_span(close);
vis.visit_tts(tts);
}
}
}
pub fn noop_visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
visit_opt(tts, |tts| {
let tts = Lrc::make_mut(tts);
visit_vec(tts, |(tree, _is_joint)| vis.visit_tt(tree));
})
}
// apply ident visitor if it's an ident, apply other visits to interpolated nodes
pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
match t {
token::Ident(id, _is_raw) => vis.visit_ident(id),
token::Lifetime(id) => vis.visit_ident(id),
token::Interpolated(nt) => {
let mut nt = Lrc::make_mut(nt);
vis.visit_interpolated(&mut nt);
}
_ => {}
}
}
/// Apply visitor to elements of interpolated nodes.
//
// N.B., this can occur only when applying a visitor to partially expanded
// code, where parsed pieces have gotten implanted ito *other* macro
// invocations. This is relevant for macro hygiene, but possibly not elsewhere.
//
// One problem here occurs because the types for flat_map_item, flat_map_stmt,
// etc. allow the visitor to return *multiple* items; this is a problem for the
// nodes here, because they insist on having exactly one piece. One solution
// would be to mangle the MutVisitor trait to include one-to-many and
// one-to-one versions of these entry points, but that would probably confuse a
// lot of people and help very few. Instead, I'm just going to put in dynamic
// checks. I think the performance impact of this will be pretty much
// nonexistent. The danger is that someone will apply a MutVisitor to a
// partially expanded node, and will be confused by the fact that their
// "flat_map_item" or "flat_map_stmt" isn't getting called on NtItem or NtStmt
// nodes. Hopefully they'll wind up reading this comment, and doing something
// appropriate.
//
// BTW, design choice: I considered just changing the type of, e.g., NtItem to
// contain multiple items, but decided against it when I looked at
// parse_item_or_view_item and tried to figure out what I would do with
// multiple items there....
pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
match nt {
token::NtItem(item) =>
visit_clobber(item, |item| {
// This is probably okay, because the only visitors likely to
// peek inside interpolated nodes will be renamings/markings,
// which map single items to single items.
vis.flat_map_item(item).expect_one("expected visitor to produce exactly one item")
}),
token::NtBlock(block) => vis.visit_block(block),
token::NtStmt(stmt) =>
visit_clobber(stmt, |stmt| {
// See reasoning above.
vis.flat_map_stmt(stmt).expect_one("expected visitor to produce exactly one item")
}),
token::NtPat(pat) => vis.visit_pat(pat),
token::NtExpr(expr) => vis.visit_expr(expr),
token::NtTy(ty) => vis.visit_ty(ty),
token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
token::NtLifetime(ident) => vis.visit_ident(ident),
token::NtLiteral(expr) => vis.visit_expr(expr),
token::NtMeta(meta) => vis.visit_meta_item(meta),
token::NtPath(path) => vis.visit_path(path),
token::NtTT(tt) => vis.visit_tt(tt),
token::NtArm(arm) => vis.visit_arm(arm),
token::NtImplItem(item) =>
visit_clobber(item, |item| {
// See reasoning above.
vis.flat_map_impl_item(item)
.expect_one("expected visitor to produce exactly one item")
}),
token::NtTraitItem(item) =>
visit_clobber(item, |item| {
// See reasoning above.
vis.flat_map_trait_item(item)
.expect_one("expected visitor to produce exactly one item")
}),
token::NtGenerics(generics) => vis.visit_generics(generics),
token::NtWhereClause(where_clause) => vis.visit_where_clause(where_clause),
token::NtArg(arg) => vis.visit_arg(arg),
token::NtVis(visib) => vis.visit_vis(visib),
token::NtForeignItem(item) =>
visit_clobber(item, |item| {
// See reasoning above.
vis.flat_map_foreign_item(item)
.expect_one("expected visitor to produce exactly one item")
}),
}
}
pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T) {
match asyncness {
IsAsync::Async { closure_id, return_impl_trait_id } => {
vis.visit_id(closure_id);
vis.visit_id(return_impl_trait_id);
}
IsAsync::NotAsync => {}
}
}
pub fn noop_visit_fn_decl<T: MutVisitor>(decl: &mut P<FnDecl>, vis: &mut T) {
let FnDecl { inputs, output, c_variadic: _ } = decl.deref_mut();
visit_vec(inputs, |input| vis.visit_arg(input));
match output {
FunctionRetTy::Default(span) => vis.visit_span(span),
FunctionRetTy::Ty(ty) => vis.visit_ty(ty),
}
}
pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T) {
match pb {
GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis),
}
}
pub fn noop_visit_generic_param<T: MutVisitor>(param: &mut GenericParam, vis: &mut T) {
let GenericParam { id, ident, attrs, bounds, kind } = param;
vis.visit_id(id);
vis.visit_ident(ident);
visit_thin_attrs(attrs, vis);
visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
match kind {
GenericParamKind::Lifetime => {}
GenericParamKind::Type { default } => {
visit_opt(default, |default| vis.visit_ty(default));
}
GenericParamKind::Const { ty } => {
vis.visit_ty(ty);
}
}
}
pub fn noop_visit_generic_params<T: MutVisitor>(params: &mut Vec<GenericParam>, vis: &mut T){
visit_vec(params, |param| vis.visit_generic_param(param));
}
pub fn noop_visit_label<T: MutVisitor>(Label { ident }: &mut Label, vis: &mut T) {
vis.visit_ident(ident);
}
fn noop_visit_lifetime<T: MutVisitor>(Lifetime { id, ident }: &mut Lifetime, vis: &mut T) {
vis.visit_id(id);
vis.visit_ident(ident);
}
pub fn noop_visit_generics<T: MutVisitor>(generics: &mut Generics, vis: &mut T) {
let Generics { params, where_clause, span } = generics;
vis.visit_generic_params(params);
vis.visit_where_clause(where_clause);
vis.visit_span(span);
}
pub fn noop_visit_where_clause<T: MutVisitor>(wc: &mut WhereClause, vis: &mut T) {
let WhereClause { id, predicates, span } = wc;
vis.visit_id(id);
visit_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
vis.visit_span(span);
}
pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis: &mut T) {
match pred {
WherePredicate::BoundPredicate(bp) => {
let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp;
vis.visit_span(span);
vis.visit_generic_params(bound_generic_params);
vis.visit_ty(bounded_ty);
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
WherePredicate::RegionPredicate(rp) => {
let WhereRegionPredicate { span, lifetime, bounds } = rp;
vis.visit_span(span);
noop_visit_lifetime(lifetime, vis);
visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
}
WherePredicate::EqPredicate(ep) => {
let WhereEqPredicate { id, span, lhs_ty, rhs_ty } = ep;
vis.visit_id(id);
vis.visit_span(span);
vis.visit_ty(lhs_ty);
vis.visit_ty(rhs_ty);
}
}
}
pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
match vdata {
VariantData::Struct(fields, ..) => visit_vec(fields, |field| vis.visit_struct_field(field)),
VariantData::Tuple(fields, id) => {
visit_vec(fields, |field| vis.visit_struct_field(field));
vis.visit_id(id);
},
VariantData::Unit(id) => vis.visit_id(id),
}
}
pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
vis.visit_path(path);
vis.visit_id(ref_id);
}
pub fn noop_visit_poly_trait_ref<T: MutVisitor>(p: &mut PolyTraitRef, vis: &mut T) {
let PolyTraitRef { bound_generic_params, trait_ref, span } = p;
vis.visit_generic_params(bound_generic_params);
vis.visit_trait_ref(trait_ref);
vis.visit_span(span);
}
pub fn noop_visit_struct_field<T: MutVisitor>(f: &mut StructField, visitor: &mut T) {
let StructField { span, ident, vis, id, ty, attrs } = f;
visitor.visit_span(span);
visit_opt(ident, |ident| visitor.visit_ident(ident));
visitor.visit_vis(vis);
visitor.visit_id(id);
visitor.visit_ty(ty);
visit_attrs(attrs, visitor);
}
pub fn noop_visit_field<T: MutVisitor>(f: &mut Field, vis: &mut T) {
let Field { ident, expr, span, is_shorthand: _, attrs } = f;
vis.visit_ident(ident);
vis.visit_expr(expr);
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
}
pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mut T) {
vis.visit_ty(ty);
}
pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
let Block { id, stmts, rules: _, span } = block.deref_mut();
vis.visit_id(id);
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
vis.visit_span(span);
}
pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
match kind {
ItemKind::ExternCrate(_orig_name) => {}
ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
ItemKind::Static(ty, _mut, expr) => {
vis.visit_ty(ty);
vis.visit_expr(expr);
}
ItemKind::Const(ty, expr) => {
vis.visit_ty(ty);
vis.visit_expr(expr);
}
ItemKind::Fn(decl, header, generics, body) => {
vis.visit_fn_decl(decl);
vis.visit_fn_header(header);
vis.visit_generics(generics);
vis.visit_block(body);
}
ItemKind::Mod(m) => vis.visit_mod(m),
ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
ItemKind::GlobalAsm(_ga) => {}
ItemKind::Ty(ty, generics) => {
vis.visit_ty(ty);
vis.visit_generics(generics);
}
ItemKind::Existential(bounds, generics) => {
visit_bounds(bounds, vis);
vis.visit_generics(generics);
}
ItemKind::Enum(EnumDef { variants }, generics) => {
visit_vec(variants, |variant| vis.visit_variant(variant));
vis.visit_generics(generics);
}
ItemKind::Struct(variant_data, generics) |
ItemKind::Union(variant_data, generics) => {
vis.visit_variant_data(variant_data);
vis.visit_generics(generics);
}
ItemKind::Impl(_unsafety, _polarity, _defaultness, generics, trait_ref, ty, items) => {
vis.visit_generics(generics);
visit_opt(trait_ref, |trait_ref| vis.visit_trait_ref(trait_ref));
vis.visit_ty(ty);
items.flat_map_in_place(|item| vis.flat_map_impl_item(item));
}
ItemKind::Trait(_is_auto, _unsafety, generics, bounds, items) => {
vis.visit_generics(generics);
visit_bounds(bounds, vis);
items.flat_map_in_place(|item| vis.flat_map_trait_item(item));
}
ItemKind::TraitAlias(generics, bounds) => {
vis.visit_generics(generics);
visit_bounds(bounds, vis);
}
ItemKind::Mac(m) => vis.visit_mac(m),
ItemKind::MacroDef(def) => vis.visit_macro_def(def),
}
}
pub fn noop_flat_map_trait_item<T: MutVisitor>(mut item: TraitItem, vis: &mut T)
-> SmallVec<[TraitItem; 1]>
{
let TraitItem { id, ident, attrs, generics, node, span, tokens: _ } = &mut item;
vis.visit_id(id);
vis.visit_ident(ident);
visit_attrs(attrs, vis);
vis.visit_generics(generics);
match node {
TraitItemKind::Const(ty, default) => {
vis.visit_ty(ty);
visit_opt(default, |default| vis.visit_expr(default));
}
TraitItemKind::Method(sig, body) => {
visit_method_sig(sig, vis);
visit_opt(body, |body| vis.visit_block(body));
}
TraitItemKind::Type(bounds, default) => {
visit_bounds(bounds, vis);
visit_opt(default, |default| vis.visit_ty(default));
}
TraitItemKind::Macro(mac) => {
vis.visit_mac(mac);
}
}
vis.visit_span(span);
smallvec![item]
}
pub fn noop_flat_map_impl_item<T: MutVisitor>(mut item: ImplItem, visitor: &mut T)
-> SmallVec<[ImplItem; 1]>
{
let ImplItem { id, ident, vis, defaultness: _, attrs, generics, node, span, tokens: _ } =
&mut item;
visitor.visit_id(id);
visitor.visit_ident(ident);
visitor.visit_vis(vis);
visit_attrs(attrs, visitor);
visitor.visit_generics(generics);
match node {
ImplItemKind::Const(ty, expr) => {
visitor.visit_ty(ty);
visitor.visit_expr(expr);
}
ImplItemKind::Method(sig, body) => {
visit_method_sig(sig, visitor);
visitor.visit_block(body);
}
ImplItemKind::Type(ty) => visitor.visit_ty(ty),
ImplItemKind::Existential(bounds) => visit_bounds(bounds, visitor),
ImplItemKind::Macro(mac) => visitor.visit_mac(mac),
}
visitor.visit_span(span);
smallvec![item]
}
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
let FnHeader { unsafety: _, asyncness, constness: _, abi: _ } = header;
vis.visit_asyncness(&mut asyncness.node);
}
pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
vis.visit_span(inner);
items.flat_map_in_place(|item| vis.flat_map_item(item));
}
pub fn noop_visit_crate<T: MutVisitor>(krate: &mut Crate, vis: &mut T) {
visit_clobber(krate, |Crate { module, attrs, span }| {
let item = P(Item {
ident: keywords::Invalid.ident(),
attrs,
id: DUMMY_NODE_ID,
vis: respan(span.shrink_to_lo(), VisibilityKind::Public),
span,
node: ItemKind::Mod(module),
tokens: None,
});
let items = vis.flat_map_item(item);
let len = items.len();
if len == 0 {
let module = Mod { inner: span, items: vec![], inline: true };
Crate { module, attrs: vec![], span }
} else if len == 1 {
let Item { attrs, span, node, .. } = items.into_iter().next().unwrap().into_inner();
match node {
ItemKind::Mod(module) => Crate { module, attrs, span },
_ => panic!("visitor converted a module to not a module"),
}
} else {
panic!("a crate cannot expand to more than one item");
}
});
}
// Mutate one item into possibly many items.
pub fn noop_flat_map_item<T: MutVisitor>(mut item: P<Item>, visitor: &mut T)
-> SmallVec<[P<Item>; 1]> {
let Item { ident, attrs, id, node, vis, span, tokens: _ } = item.deref_mut();
visitor.visit_ident(ident);
visit_attrs(attrs, visitor);
visitor.visit_id(id);
visitor.visit_item_kind(node);
visitor.visit_vis(vis);
visitor.visit_span(span);
// FIXME: if `tokens` is modified with a call to `vis.visit_tts` it causes
// an ICE during resolve... odd!
smallvec![item]
}
pub fn noop_flat_map_foreign_item<T: MutVisitor>(mut item: ForeignItem, visitor: &mut T)
-> SmallVec<[ForeignItem; 1]>
{
let ForeignItem { ident, attrs, node, id, span, vis } = &mut item;
visitor.visit_ident(ident);
visit_attrs(attrs, visitor);
match node {
ForeignItemKind::Fn(fdec, generics) => {
visitor.visit_fn_decl(fdec);
visitor.visit_generics(generics);
}
ForeignItemKind::Static(t, _m) => visitor.visit_ty(t),
ForeignItemKind::Ty => {}
ForeignItemKind::Macro(mac) => visitor.visit_mac(mac),
}
visitor.visit_id(id);
visitor.visit_span(span);
visitor.visit_vis(vis);
smallvec![item]
}
pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
let Pat { id, node, span } = pat.deref_mut();
vis.visit_id(id);
match node {
PatKind::Wild => {}
PatKind::Ident(_binding_mode, ident, sub) => {
vis.visit_ident(ident);
visit_opt(sub, |sub| vis.visit_pat(sub));
}
PatKind::Lit(e) => vis.visit_expr(e),
PatKind::TupleStruct(path, pats, _ddpos) => {
vis.visit_path(path);
visit_vec(pats, |pat| vis.visit_pat(pat));
}
PatKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
}
PatKind::Struct(path, fields, _etc) => {
vis.visit_path(path);
for Spanned { node: FieldPat { ident, pat, is_shorthand: _, attrs }, span } in fields {
vis.visit_ident(ident);
vis.visit_pat(pat);
visit_thin_attrs(attrs, vis);
vis.visit_span(span);
};
}
PatKind::Tuple(elts, _ddpos) => visit_vec(elts, |elt| vis.visit_pat(elt)),
PatKind::Box(inner) => vis.visit_pat(inner),
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
vis.visit_expr(e1);
vis.visit_expr(e2);
vis.visit_span(span);
}
PatKind::Slice(before, slice, after) => {
visit_vec(before, |pat| vis.visit_pat(pat));
visit_opt(slice, |slice| vis.visit_pat(slice));
visit_vec(after, |pat| vis.visit_pat(pat));
}
PatKind::Paren(inner) => vis.visit_pat(inner),
PatKind::Mac(mac) => vis.visit_mac(mac),
}
vis.visit_span(span);
}
pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonConst, vis: &mut T) {
vis.visit_id(id);
vis.visit_expr(value);
}
pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr, vis: &mut T) {
match node {
ExprKind::Box(expr) => vis.visit_expr(expr),
ExprKind::ObsoleteInPlace(a, b) => {
vis.visit_expr(a);
vis.visit_expr(b);
}
ExprKind::Array(exprs) => visit_exprs(exprs, vis),
ExprKind::Repeat(expr, count) => {
vis.visit_expr(expr);
vis.visit_anon_const(count);
}
ExprKind::Tup(exprs) => visit_exprs(exprs, vis),
ExprKind::Call(f, args) => {
vis.visit_expr(f);
visit_exprs(args, vis);
}
ExprKind::MethodCall(PathSegment { ident, id, args }, exprs) => {
vis.visit_ident(ident);
vis.visit_id(id);
visit_opt(args, |args| vis.visit_generic_args(args));
visit_exprs(exprs, vis);
}
ExprKind::Binary(_binop, lhs, rhs) => {
vis.visit_expr(lhs);
vis.visit_expr(rhs);
}
ExprKind::Unary(_unop, ohs) => vis.visit_expr(ohs),
ExprKind::Lit(_lit) => {}
ExprKind::Cast(expr, ty) => {
vis.visit_expr(expr);
vis.visit_ty(ty);
}
ExprKind::Type(expr, ty) => {
vis.visit_expr(expr);
vis.visit_ty(ty);
}
ExprKind::AddrOf(_m, ohs) => vis.visit_expr(ohs),
ExprKind::If(cond, tr, fl) => {
vis.visit_expr(cond);
vis.visit_block(tr);
visit_opt(fl, |fl| vis.visit_expr(fl));
}
ExprKind::IfLet(pats, expr, tr, fl) => {
visit_vec(pats, |pat| vis.visit_pat(pat));
vis.visit_expr(expr);
vis.visit_block(tr);
visit_opt(fl, |fl| vis.visit_expr(fl));
}
ExprKind::While(cond, body, label) => {
vis.visit_expr(cond);
vis.visit_block(body);
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::WhileLet(pats, expr, body, label) => {
visit_vec(pats, |pat| vis.visit_pat(pat));
vis.visit_expr(expr);
vis.visit_block(body);
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::ForLoop(pat, iter, body, label) => {
vis.visit_pat(pat);
vis.visit_expr(iter);
vis.visit_block(body);
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::Loop(body, label) => {
vis.visit_block(body);
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::Match(expr, arms) => {
vis.visit_expr(expr);
visit_vec(arms, |arm| vis.visit_arm(arm));
}
ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => {
vis.visit_asyncness(asyncness);
vis.visit_fn_decl(decl);
vis.visit_expr(body);
vis.visit_span(span);
}
ExprKind::Block(blk, label) => {
vis.visit_block(blk);
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::Async(_capture_by, node_id, body) => {
vis.visit_id(node_id);
vis.visit_block(body);
}
ExprKind::Assign(el, er) => {
vis.visit_expr(el);
vis.visit_expr(er);
}
ExprKind::AssignOp(_op, el, er) => {
vis.visit_expr(el);
vis.visit_expr(er);
}
ExprKind::Field(el, ident) => {
vis.visit_expr(el);
vis.visit_ident(ident);
}
ExprKind::Index(el, er) => {
vis.visit_expr(el);
vis.visit_expr(er);
}
ExprKind::Range(e1, e2, _lim) => {
visit_opt(e1, |e1| vis.visit_expr(e1));
visit_opt(e2, |e2| vis.visit_expr(e2));
}
ExprKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
}
ExprKind::Break(label, expr) => {
visit_opt(label, |label| vis.visit_label(label));
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::Continue(label) => {
visit_opt(label, |label| vis.visit_label(label));
}
ExprKind::Ret(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::InlineAsm(asm) => {
let InlineAsm { asm: _, asm_str_style: _, outputs, inputs, clobbers: _, volatile: _,
alignstack: _, dialect: _, ctxt: _ } = asm.deref_mut();
for out in outputs {
let InlineAsmOutput { constraint: _, expr, is_rw: _, is_indirect: _ } = out;
vis.visit_expr(expr);
}
visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr));
}
ExprKind::Mac(mac) => vis.visit_mac(mac),
ExprKind::Struct(path, fields, expr) => {
vis.visit_path(path);
visit_vec(fields, |field| vis.visit_field(field));
visit_opt(expr, |expr| vis.visit_expr(expr));
},
ExprKind::Paren(expr) => {
vis.visit_expr(expr);
// Nodes that are equal modulo `Paren` sugar no-ops should have the same ids.
*id = expr.id;
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
return;
}
ExprKind::Yield(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::Try(expr) => vis.visit_expr(expr),
ExprKind::TryBlock(body) => vis.visit_block(body),
ExprKind::Err => {}
}
vis.visit_id(id);
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
}
pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Option<P<Expr>> {
Some({ vis.visit_expr(&mut e); e })
}
pub fn noop_flat_map_stmt<T: MutVisitor>(Stmt { node, mut span, mut id }: Stmt, vis: &mut T)
-> SmallVec<[Stmt; 1]>
{
vis.visit_id(&mut id);
vis.visit_span(&mut span);
noop_flat_map_stmt_kind(node, vis).into_iter().map(|node| {
Stmt { id, node, span }
}).collect()
}
pub fn noop_flat_map_stmt_kind<T: MutVisitor>(node: StmtKind, vis: &mut T)
-> SmallVec<[StmtKind; 1]> {
match node {
StmtKind::Local(mut local) =>
smallvec![StmtKind::Local({ vis.visit_local(&mut local); local })],
StmtKind::Item(item) => vis.flat_map_item(item).into_iter().map(StmtKind::Item).collect(),
StmtKind::Expr(expr) => {
vis.filter_map_expr(expr).into_iter().map(StmtKind::Expr).collect()
}
StmtKind::Semi(expr) => {
vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect()
}
StmtKind::Mac(mut mac) => {
let (mac_, _semi, attrs) = mac.deref_mut();
vis.visit_mac(mac_);
visit_thin_attrs(attrs, vis);
smallvec![StmtKind::Mac(mac)]
}
}
}
pub fn noop_visit_vis<T: MutVisitor>(Spanned { node, span }: &mut Visibility, vis: &mut T) {
match node {
VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {}
VisibilityKind::Restricted { path, id } => {
vis.visit_path(path);
vis.visit_id(id);
}
}
vis.visit_span(span);
}
#[cfg(test)]
mod tests {
use std::io;
use crate::ast::{self, Ident};
use crate::util::parser_testing::{string_to_crate, matches_codepattern};
use crate::print::pprust;
use crate::mut_visit;
use crate::with_globals;
use super::*;
// this version doesn't care about getting comments or docstrings in.
fn fake_print_crate(s: &mut pprust::State<'_>,
krate: &ast::Crate) -> io::Result<()> {
s.print_mod(&krate.module, &krate.attrs)
}
// change every identifier to "zz"
struct ToZzIdentMutVisitor;
impl MutVisitor for ToZzIdentMutVisitor {
fn visit_ident(&mut self, ident: &mut ast::Ident) {
*ident = Ident::from_str("zz");
}
fn visit_mac(&mut self, mac: &mut ast::Mac) {
mut_visit::noop_visit_mac(mac, self)
}
}
// maybe add to expand.rs...
macro_rules! assert_pred {
($pred:expr, $predname:expr, $a:expr , $b:expr) => (
{
let pred_val = $pred;
let a_val = $a;
let b_val = $b;
if !(pred_val(&a_val, &b_val)) {
panic!("expected args satisfying {}, got {} and {}",
$predname, a_val, b_val);
}
}
)
}
// make sure idents get transformed everywhere
#[test] fn ident_transformation () {
with_globals(|| {
let mut zz_visitor = ToZzIdentMutVisitor;
let mut krate = string_to_crate(
"#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string());
zz_visitor.visit_crate(&mut krate);
assert_pred!(
matches_codepattern,
"matches_codepattern",
pprust::to_string(|s| fake_print_crate(s, &krate)),
"#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string());
})
}
// even inside macro defs....
#[test] fn ident_transformation_in_defs () {
with_globals(|| {
let mut zz_visitor = ToZzIdentMutVisitor;
let mut krate = string_to_crate(
"macro_rules! a {(b $c:expr $(d $e:token)f+ => \
(g $(d $d $e)+))} ".to_string());
zz_visitor.visit_crate(&mut krate);
assert_pred!(
matches_codepattern,
"matches_codepattern",
pprust::to_string(|s| fake_print_crate(s, &krate)),
"macro_rules! zz((zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+)));".to_string());
})
}
}