diff options
Diffstat (limited to 'compiler/rustc_macros/src')
| -rw-r--r-- | compiler/rustc_macros/src/lib.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_macros/src/visitable.rs | 82 |
2 files changed, 94 insertions, 1 deletions
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 1006ea3ba10..803b3621c88 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -21,6 +21,7 @@ mod symbols; mod try_from; mod type_foldable; mod type_visitable; +mod visitable; // Reads the rust version (e.g. "1.75.0") from the CFG_RELEASE env var and // produces a `RustcVersion` literal containing that version (e.g. @@ -101,6 +102,16 @@ decl_derive!( /// visited (and its type is not required to implement `TypeVisitable`). type_visitable::type_visitable_derive ); +decl_derive!( + [Walkable, attributes(visitable)] => + /// Derives `Walkable` for the annotated `struct` or `enum` (`union` is not supported). + /// + /// Each field of the struct or enum variant will be visited in definition order, using the + /// `Walkable` implementation for its type. However, if a field of a struct or an enum + /// variant is annotated with `#[visitable(ignore)]` then that field will not be + /// visited (and its type is not required to implement `Walkable`). + visitable::visitable_derive +); decl_derive!([Lift, attributes(lift)] => lift::lift_derive); decl_derive!( [Diagnostic, attributes( @@ -176,7 +187,7 @@ decl_derive! { decl_derive! { [PrintAttribute] => /// Derives `PrintAttribute` for `AttributeKind`. - /// This macro is pretty specific to `rustc_attr_data_structures` and likely not that useful in + /// This macro is pretty specific to `rustc_hir::attrs` and likely not that useful in /// other places. It's deriving something close to `Debug` without printing some extraneous /// things like spans. print_attribute::print_attribute diff --git a/compiler/rustc_macros/src/visitable.rs b/compiler/rustc_macros/src/visitable.rs new file mode 100644 index 00000000000..a7a82538eab --- /dev/null +++ b/compiler/rustc_macros/src/visitable.rs @@ -0,0 +1,82 @@ +use quote::quote; +use synstructure::BindingInfo; + +pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + let has_attr = |bind: &BindingInfo<'_>, name| { + let mut found = false; + bind.ast().attrs.iter().for_each(|attr| { + if !attr.path().is_ident("visitable") { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident(name) { + found = true; + } + Ok(()) + }); + }); + found + }; + + let get_attr = |bind: &BindingInfo<'_>, name: &str| { + let mut content = None; + bind.ast().attrs.iter().for_each(|attr| { + if !attr.path().is_ident("visitable") { + return; + } + let _ = attr.parse_nested_meta(|nested| { + if nested.path.is_ident(name) { + let value = nested.value()?; + let value = value.parse()?; + content = Some(value); + } + Ok(()) + }); + }); + content + }; + + s.add_bounds(synstructure::AddBounds::Generics); + s.bind_with(|_| synstructure::BindStyle::Ref); + let ref_visit = s.each(|bind| { + let extra = get_attr(bind, "extra").unwrap_or(quote! {}); + if has_attr(bind, "ignore") { + quote! {} + } else { + quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) } + } + }); + + s.bind_with(|_| synstructure::BindStyle::RefMut); + let mut_visit = s.each(|bind| { + let extra = get_attr(bind, "extra").unwrap_or(quote! {}); + if has_attr(bind, "ignore") { + quote! {} + } else { + quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) } + } + }); + + s.gen_impl(quote! { + gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self + where __V: crate::visit::Visitor<'__ast>, + { + fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result { + match *self { #ref_visit } + <__V::Result as rustc_ast_ir::visit::VisitorResult>::output() + } + } + + gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self + where __V: crate::mut_visit::MutVisitor, + { + fn walk_mut(&mut self, __visitor: &mut __V) { + match *self { #mut_visit } + } + } + }) +} |
