]> git.lizzy.rs Git - rust.git/commitdiff
expand: Support helper attributes for built-in derive macros
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 10 Jul 2021 14:16:53 +0000 (17:16 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Tue, 13 Jul 2021 18:59:22 +0000 (21:59 +0300)
compiler/rustc_builtin_macros/src/proc_macro_harness.rs
compiler/rustc_expand/src/base.rs
compiler/rustc_feature/src/builtin_attrs.rs
library/core/src/macros/mod.rs
library/std/src/macros.rs
src/test/ui/deriving/deriving-with-helper.rs [new file with mode: 0644]

index 71bbae1161b4bebcef863c749dde5e5f53697b2b..a8c61d53346de279ea68c618d70ad47a5e5b967a 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{self as ast, NodeId};
 use rustc_ast_pretty::pprust;
-use rustc_expand::base::{ExtCtxt, ResolverExpand};
+use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_session::Session;
 use rustc_span::hygiene::AstPass;
@@ -109,86 +109,17 @@ fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
     }
 
     fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
-        // Once we've located the `#[proc_macro_derive]` attribute, verify
-        // that it's of the form `#[proc_macro_derive(Foo)]` or
-        // `#[proc_macro_derive(Foo, attributes(A, ..))]`
-        let list = match attr.meta_item_list() {
-            Some(list) => list,
-            None => return,
-        };
-        if list.len() != 1 && list.len() != 2 {
-            self.handler.span_err(attr.span, "attribute must have either one or two arguments");
-            return;
-        }
-        let trait_attr = match list[0].meta_item() {
-            Some(meta_item) => meta_item,
-            _ => {
-                self.handler.span_err(list[0].span(), "not a meta item");
-                return;
-            }
-        };
-        let trait_ident = match trait_attr.ident() {
-            Some(trait_ident) if trait_attr.is_word() => trait_ident,
-            _ => {
-                self.handler.span_err(trait_attr.span, "must only be one word");
-                return;
-            }
-        };
-
-        if !trait_ident.name.can_be_raw() {
-            self.handler.span_err(
-                trait_attr.span,
-                &format!("`{}` cannot be a name of derive macro", trait_ident),
-            );
-        }
-
-        let attributes_attr = list.get(1);
-        let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
-            if !attr.has_name(sym::attributes) {
-                self.handler.span_err(attr.span(), "second argument must be `attributes`")
-            }
-            attr.meta_item_list()
-                .unwrap_or_else(|| {
-                    self.handler
-                        .span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
-                    &[]
-                })
-                .iter()
-                .filter_map(|attr| {
-                    let attr = match attr.meta_item() {
-                        Some(meta_item) => meta_item,
-                        _ => {
-                            self.handler.span_err(attr.span(), "not a meta item");
-                            return None;
-                        }
-                    };
-
-                    let ident = match attr.ident() {
-                        Some(ident) if attr.is_word() => ident,
-                        _ => {
-                            self.handler.span_err(attr.span, "must only be one word");
-                            return None;
-                        }
-                    };
-                    if !ident.name.can_be_raw() {
-                        self.handler.span_err(
-                            attr.span,
-                            &format!("`{}` cannot be a name of derive helper attribute", ident),
-                        );
-                    }
-
-                    Some(ident.name)
-                })
-                .collect()
-        } else {
-            Vec::new()
-        };
+        let (trait_name, proc_attrs) =
+            match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") {
+                Some(name_and_attrs) => name_and_attrs,
+                None => return,
+            };
 
         if self.in_root && item.vis.kind.is_pub() {
             self.macros.push(ProcMacro::Derive(ProcMacroDerive {
                 id: item.id,
                 span: item.span,
-                trait_name: trait_ident.name,
+                trait_name,
                 function_name: item.ident,
                 attrs: proc_attrs,
             }));
index b3e52502b0739245b962c190a53de2db75ddc199..0183add495777184abf448fc63988f44bd2d5bb9 100644 (file)
@@ -745,9 +745,17 @@ pub fn new(
             }
         }
 
-        let builtin_name = sess
+        let (builtin_name, helper_attrs) = sess
             .find_by_name(attrs, sym::rustc_builtin_macro)
-            .map(|a| a.value_str().unwrap_or(name));
+            .map(|attr| {
+                // Override `helper_attrs` passed above if it's a built-in macro,
+                // marking `proc_macro_derive` macros as built-in is not a realistic use case.
+                parse_macro_name_and_helper_attrs(sess.diagnostic(), attr, "built-in").map_or_else(
+                    || (Some(name), Vec::new()),
+                    |(name, helper_attrs)| (Some(name), helper_attrs),
+                )
+            })
+            .unwrap_or_else(|| (None, helper_attrs));
         let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
         if let Some((_, sp)) = const_stability {
             sess.parse_sess
@@ -1213,6 +1221,88 @@ pub fn get_exprs_from_tts(
     Some(es)
 }
 
+pub fn parse_macro_name_and_helper_attrs(
+    diag: &rustc_errors::Handler,
+    attr: &Attribute,
+    descr: &str,
+) -> Option<(Symbol, Vec<Symbol>)> {
+    // Once we've located the `#[proc_macro_derive]` attribute, verify
+    // that it's of the form `#[proc_macro_derive(Foo)]` or
+    // `#[proc_macro_derive(Foo, attributes(A, ..))]`
+    let list = match attr.meta_item_list() {
+        Some(list) => list,
+        None => return None,
+    };
+    if list.len() != 1 && list.len() != 2 {
+        diag.span_err(attr.span, "attribute must have either one or two arguments");
+        return None;
+    }
+    let trait_attr = match list[0].meta_item() {
+        Some(meta_item) => meta_item,
+        _ => {
+            diag.span_err(list[0].span(), "not a meta item");
+            return None;
+        }
+    };
+    let trait_ident = match trait_attr.ident() {
+        Some(trait_ident) if trait_attr.is_word() => trait_ident,
+        _ => {
+            diag.span_err(trait_attr.span, "must only be one word");
+            return None;
+        }
+    };
+
+    if !trait_ident.name.can_be_raw() {
+        diag.span_err(
+            trait_attr.span,
+            &format!("`{}` cannot be a name of {} macro", trait_ident, descr),
+        );
+    }
+
+    let attributes_attr = list.get(1);
+    let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
+        if !attr.has_name(sym::attributes) {
+            diag.span_err(attr.span(), "second argument must be `attributes`")
+        }
+        attr.meta_item_list()
+            .unwrap_or_else(|| {
+                diag.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
+                &[]
+            })
+            .iter()
+            .filter_map(|attr| {
+                let attr = match attr.meta_item() {
+                    Some(meta_item) => meta_item,
+                    _ => {
+                        diag.span_err(attr.span(), "not a meta item");
+                        return None;
+                    }
+                };
+
+                let ident = match attr.ident() {
+                    Some(ident) if attr.is_word() => ident,
+                    _ => {
+                        diag.span_err(attr.span, "must only be one word");
+                        return None;
+                    }
+                };
+                if !ident.name.can_be_raw() {
+                    diag.span_err(
+                        attr.span,
+                        &format!("`{}` cannot be a name of derive helper attribute", ident),
+                    );
+                }
+
+                Some(ident.name)
+            })
+            .collect()
+    } else {
+        Vec::new()
+    };
+
+    Some((trait_ident.name, proc_attrs))
+}
+
 /// This nonterminal looks like some specific enums from
 /// `proc-macro-hack` and `procedural-masquerade` crates.
 /// We need to maintain some special pretty-printing behavior for them due to incorrect
index 77d8f0f920c145846d9559c9e59982276800e719..b1c725ecd85c857db40b883d631ac7b529263add 100644 (file)
@@ -448,7 +448,11 @@ macro_rules! experimental {
     // Internal attributes, Macro related:
     // ==========================================================================
 
-    rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL),
+    rustc_attr!(
+        rustc_builtin_macro, AssumedUsed,
+        template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"),
+        IMPL_DETAIL,
+    ),
     rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
     rustc_attr!(
         rustc_macro_transparency, AssumedUsed,
index 7eb65483b99e77e09ea5825062c6e5828a110d94..7d22bfedb93b946234540931169e3d71c9cd04d6 100644 (file)
@@ -1,6 +1,7 @@
 #[doc = include_str!("panic.md")]
 #[macro_export]
-#[rustc_builtin_macro = "core_panic"]
+#[cfg_attr(bootstrap, rustc_builtin_macro = "core_panic")]
+#[cfg_attr(not(bootstrap), rustc_builtin_macro(core_panic))]
 #[allow_internal_unstable(edition_panic)]
 #[stable(feature = "core", since = "1.6.0")]
 #[rustc_diagnostic_item = "core_panic_macro"]
index b2c5df5410dcab9fa484fc654722a5241a1c9315..7afe52a3fd693ccf2cec1d69901eb4ba3df56135 100644 (file)
@@ -6,7 +6,8 @@
 
 #[doc = include_str!("../../core/src/macros/panic.md")]
 #[macro_export]
-#[rustc_builtin_macro = "std_panic"]
+#[cfg_attr(bootstrap, rustc_builtin_macro = "std_panic")]
+#[cfg_attr(not(bootstrap), rustc_builtin_macro(std_panic))]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow_internal_unstable(edition_panic)]
 #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
diff --git a/src/test/ui/deriving/deriving-with-helper.rs b/src/test/ui/deriving/deriving-with-helper.rs
new file mode 100644 (file)
index 0000000..ea74a15
--- /dev/null
@@ -0,0 +1,36 @@
+// check-pass
+// compile-flags: --crate-type=lib
+
+#![feature(decl_macro)]
+#![feature(lang_items)]
+#![feature(no_core)]
+#![feature(rustc_attrs)]
+
+#![no_core]
+
+#[rustc_builtin_macro]
+macro derive() {}
+
+#[rustc_builtin_macro(Default, attributes(default))]
+macro Default() {}
+
+mod default {
+    pub trait Default {
+        fn default() -> Self;
+    }
+
+    impl Default for u8 {
+        fn default() -> u8 {
+            0
+        }
+    }
+}
+
+#[lang = "sized"]
+trait Sized {}
+
+#[derive(Default)]
+struct S {
+    #[default] // OK
+    field: u8,
+}