expand: Support helper attributes for built-in derive macros
This commit is contained in:
parent
3e1c75c6e2
commit
6c9ea1e8a9
@ -5,7 +5,7 @@
|
|||||||
use rustc_ast::visit::{self, Visitor};
|
use rustc_ast::visit::{self, Visitor};
|
||||||
use rustc_ast::{self as ast, NodeId};
|
use rustc_ast::{self as ast, NodeId};
|
||||||
use rustc_ast_pretty::pprust;
|
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_expand::expand::{AstFragment, ExpansionConfig};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::hygiene::AstPass;
|
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) {
|
fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
|
||||||
// Once we've located the `#[proc_macro_derive]` attribute, verify
|
let (trait_name, proc_attrs) =
|
||||||
// that it's of the form `#[proc_macro_derive(Foo)]` or
|
match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") {
|
||||||
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
|
Some(name_and_attrs) => name_and_attrs,
|
||||||
let list = match attr.meta_item_list() {
|
None => return,
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.in_root && item.vis.kind.is_pub() {
|
if self.in_root && item.vis.kind.is_pub() {
|
||||||
self.macros.push(ProcMacro::Derive(ProcMacroDerive {
|
self.macros.push(ProcMacro::Derive(ProcMacroDerive {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
span: item.span,
|
span: item.span,
|
||||||
trait_name: trait_ident.name,
|
trait_name,
|
||||||
function_name: item.ident,
|
function_name: item.ident,
|
||||||
attrs: proc_attrs,
|
attrs: proc_attrs,
|
||||||
}));
|
}));
|
||||||
|
@ -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)
|
.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);
|
let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
|
||||||
if let Some((_, sp)) = const_stability {
|
if let Some((_, sp)) = const_stability {
|
||||||
sess.parse_sess
|
sess.parse_sess
|
||||||
@ -1213,6 +1221,88 @@ pub fn get_exprs_from_tts(
|
|||||||
Some(es)
|
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
|
/// This nonterminal looks like some specific enums from
|
||||||
/// `proc-macro-hack` and `procedural-masquerade` crates.
|
/// `proc-macro-hack` and `procedural-masquerade` crates.
|
||||||
/// We need to maintain some special pretty-printing behavior for them due to incorrect
|
/// We need to maintain some special pretty-printing behavior for them due to incorrect
|
||||||
|
@ -448,7 +448,11 @@ macro_rules! experimental {
|
|||||||
// Internal attributes, Macro related:
|
// 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_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
|
||||||
rustc_attr!(
|
rustc_attr!(
|
||||||
rustc_macro_transparency, AssumedUsed,
|
rustc_macro_transparency, AssumedUsed,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#[doc = include_str!("panic.md")]
|
#[doc = include_str!("panic.md")]
|
||||||
#[macro_export]
|
#[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)]
|
#[allow_internal_unstable(edition_panic)]
|
||||||
#[stable(feature = "core", since = "1.6.0")]
|
#[stable(feature = "core", since = "1.6.0")]
|
||||||
#[rustc_diagnostic_item = "core_panic_macro"]
|
#[rustc_diagnostic_item = "core_panic_macro"]
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
#[doc = include_str!("../../core/src/macros/panic.md")]
|
#[doc = include_str!("../../core/src/macros/panic.md")]
|
||||||
#[macro_export]
|
#[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")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[allow_internal_unstable(edition_panic)]
|
#[allow_internal_unstable(edition_panic)]
|
||||||
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
|
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
|
||||||
|
36
src/test/ui/deriving/deriving-with-helper.rs
Normal file
36
src/test/ui/deriving/deriving-with-helper.rs
Normal file
@ -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,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user