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/diagnostics/utils.rs2
-rw-r--r--compiler/rustc_macros/src/lib.rs13
-rw-r--r--compiler/rustc_macros/src/print_attribute.rs60
-rw-r--r--compiler/rustc_macros/src/visitable.rs82
4 files changed, 115 insertions, 42 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 060799e981d..c310b99d535 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -146,7 +146,7 @@ impl<'ty> FieldInnerTy<'ty> {
             };
 
             let path = &ty_path.path;
-            let ty = path.segments.iter().last().unwrap();
+            let ty = path.segments.last().unwrap();
             let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments else {
                 panic!("expected bracketed generic arguments");
             };
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/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs
index 9023520c750..0114e0dfde0 100644
--- a/compiler/rustc_macros/src/print_attribute.rs
+++ b/compiler/rustc_macros/src/print_attribute.rs
@@ -4,7 +4,7 @@ use syn::spanned::Spanned;
 use syn::{Data, Fields, Ident};
 use synstructure::Structure;
 
-fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, TokenStream) {
+fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream) {
     let string_name = name.to_string();
     let mut disps = vec![quote! {let mut __printed_anything = false;}];
 
@@ -43,7 +43,6 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
                     #(#disps)*
                     __p.word("}");
                 },
-                quote! { true },
             )
         }
         Fields::Unnamed(fields_unnamed) => {
@@ -76,10 +75,9 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
                     #(#disps)*
                     __p.pclose();
                 },
-                quote! { true },
             )
         }
-        Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }, quote! { true }),
+        Fields::Unit => (quote! {}, quote! { __p.word(#string_name) }),
     }
 }
 
@@ -89,51 +87,33 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
     };
 
     // Must be applied to an enum type.
-    let (code, printed) = match &input.ast().data {
+    let code = match &input.ast().data {
         Data::Enum(e) => {
-            let (arms, printed) = e
+            let arms = e
                 .variants
                 .iter()
                 .map(|x| {
                     let ident = &x.ident;
-                    let (pat, code, printed) = print_fields(ident, &x.fields);
+                    let (pat, code) = print_fields(ident, &x.fields);
 
-                    (
-                        quote! {
-                            Self::#ident #pat => {#code}
-                        },
-                        quote! {
-                            Self::#ident #pat => {#printed}
-                        },
-                    )
+                    quote! {
+                        Self::#ident #pat => {#code}
+                    }
                 })
-                .unzip::<_, _, Vec<_>, Vec<_>>();
+                .collect::<Vec<_>>();
 
-            (
-                quote! {
-                    match self {
-                        #(#arms)*
-                    }
-                },
-                quote! {
-                    match self {
-                        #(#printed)*
-                    }
-                },
-            )
+            quote! {
+                match self {
+                    #(#arms)*
+                }
+            }
         }
         Data::Struct(s) => {
-            let (pat, code, printed) = print_fields(&input.ast().ident, &s.fields);
-            (
-                quote! {
-                    let Self #pat = self;
-                    #code
-                },
-                quote! {
-                    let Self #pat = self;
-                    #printed
-                },
-            )
+            let (pat, code) = print_fields(&input.ast().ident, &s.fields);
+            quote! {
+                let Self #pat = self;
+                #code
+            }
         }
         Data::Union(u) => {
             return span_error(u.union_token.span(), "can't derive PrintAttribute on unions");
@@ -144,7 +124,7 @@ pub(crate) fn print_attribute(input: Structure<'_>) -> TokenStream {
     input.gen_impl(quote! {
         #[allow(unused)]
         gen impl PrintAttribute for @Self {
-            fn should_render(&self) -> bool { #printed }
+            fn should_render(&self) -> bool { true }
             fn print_attribute(&self, __p: &mut rustc_ast_pretty::pp::Printer) { #code }
         }
     })
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 }
+            }
+        }
+    })
+}