about summary refs log tree commit diff
path: root/compiler/rustc_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_macros/src')
-rw-r--r--compiler/rustc_macros/src/lib.rs13
-rw-r--r--compiler/rustc_macros/src/visitable.rs82
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 }
+            }
+        }
+    })
+}