Skip to content

Commit 4d141f5

Browse files
Rollup merge of #87027 - petrochenkov:builderhelp, r=oli-obk
expand: Support helper attributes for built-in derive macros This is needed for #86735 (derive macro `Default` should have a helper attribute `default`). With this PR we can specify helper attributes for built-in derives using syntax `#[rustc_builtin_macro(MacroName, attributes(attr1, attr2, ...))]` which mirrors equivalent syntax for proc macros `#[proc_macro_derive(MacroName, attributes(attr1, attr2, ...))]`. Otherwise expansion infra was already ready for this. The attribute parsing code is shared between proc macro derives and built-in macros (`fn parse_macro_name_and_helper_attrs`).
2 parents ee5ed4a + 6c9ea1e commit 4d141f5

File tree

6 files changed

+144
-81
lines changed

6 files changed

+144
-81
lines changed

compiler/rustc_builtin_macros/src/proc_macro_harness.rs

+7-76
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_ast::ptr::P;
55
use rustc_ast::visit::{self, Visitor};
66
use rustc_ast::{self as ast, NodeId};
77
use rustc_ast_pretty::pprust;
8-
use rustc_expand::base::{ExtCtxt, ResolverExpand};
8+
use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
99
use rustc_expand::expand::{AstFragment, ExpansionConfig};
1010
use rustc_session::Session;
1111
use rustc_span::hygiene::AstPass;
@@ -109,86 +109,17 @@ impl<'a> CollectProcMacros<'a> {
109109
}
110110

111111
fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
112-
// Once we've located the `#[proc_macro_derive]` attribute, verify
113-
// that it's of the form `#[proc_macro_derive(Foo)]` or
114-
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
115-
let list = match attr.meta_item_list() {
116-
Some(list) => list,
117-
None => return,
118-
};
119-
if list.len() != 1 && list.len() != 2 {
120-
self.handler.span_err(attr.span, "attribute must have either one or two arguments");
121-
return;
122-
}
123-
let trait_attr = match list[0].meta_item() {
124-
Some(meta_item) => meta_item,
125-
_ => {
126-
self.handler.span_err(list[0].span(), "not a meta item");
127-
return;
128-
}
129-
};
130-
let trait_ident = match trait_attr.ident() {
131-
Some(trait_ident) if trait_attr.is_word() => trait_ident,
132-
_ => {
133-
self.handler.span_err(trait_attr.span, "must only be one word");
134-
return;
135-
}
136-
};
137-
138-
if !trait_ident.name.can_be_raw() {
139-
self.handler.span_err(
140-
trait_attr.span,
141-
&format!("`{}` cannot be a name of derive macro", trait_ident),
142-
);
143-
}
144-
145-
let attributes_attr = list.get(1);
146-
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
147-
if !attr.has_name(sym::attributes) {
148-
self.handler.span_err(attr.span(), "second argument must be `attributes`")
149-
}
150-
attr.meta_item_list()
151-
.unwrap_or_else(|| {
152-
self.handler
153-
.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
154-
&[]
155-
})
156-
.iter()
157-
.filter_map(|attr| {
158-
let attr = match attr.meta_item() {
159-
Some(meta_item) => meta_item,
160-
_ => {
161-
self.handler.span_err(attr.span(), "not a meta item");
162-
return None;
163-
}
164-
};
165-
166-
let ident = match attr.ident() {
167-
Some(ident) if attr.is_word() => ident,
168-
_ => {
169-
self.handler.span_err(attr.span, "must only be one word");
170-
return None;
171-
}
172-
};
173-
if !ident.name.can_be_raw() {
174-
self.handler.span_err(
175-
attr.span,
176-
&format!("`{}` cannot be a name of derive helper attribute", ident),
177-
);
178-
}
179-
180-
Some(ident.name)
181-
})
182-
.collect()
183-
} else {
184-
Vec::new()
185-
};
112+
let (trait_name, proc_attrs) =
113+
match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") {
114+
Some(name_and_attrs) => name_and_attrs,
115+
None => return,
116+
};
186117

187118
if self.in_root && item.vis.kind.is_pub() {
188119
self.macros.push(ProcMacro::Derive(ProcMacroDerive {
189120
id: item.id,
190121
span: item.span,
191-
trait_name: trait_ident.name,
122+
trait_name,
192123
function_name: item.ident,
193124
attrs: proc_attrs,
194125
}));

compiler/rustc_expand/src/base.rs

+92-2
Original file line numberDiff line numberDiff line change
@@ -745,9 +745,17 @@ impl SyntaxExtension {
745745
}
746746
}
747747

748-
let builtin_name = sess
748+
let (builtin_name, helper_attrs) = sess
749749
.find_by_name(attrs, sym::rustc_builtin_macro)
750-
.map(|a| a.value_str().unwrap_or(name));
750+
.map(|attr| {
751+
// Override `helper_attrs` passed above if it's a built-in macro,
752+
// marking `proc_macro_derive` macros as built-in is not a realistic use case.
753+
parse_macro_name_and_helper_attrs(sess.diagnostic(), attr, "built-in").map_or_else(
754+
|| (Some(name), Vec::new()),
755+
|(name, helper_attrs)| (Some(name), helper_attrs),
756+
)
757+
})
758+
.unwrap_or_else(|| (None, helper_attrs));
751759
let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
752760
if let Some((_, sp)) = const_stability {
753761
sess.parse_sess
@@ -1213,6 +1221,88 @@ pub fn get_exprs_from_tts(
12131221
Some(es)
12141222
}
12151223

1224+
pub fn parse_macro_name_and_helper_attrs(
1225+
diag: &rustc_errors::Handler,
1226+
attr: &Attribute,
1227+
descr: &str,
1228+
) -> Option<(Symbol, Vec<Symbol>)> {
1229+
// Once we've located the `#[proc_macro_derive]` attribute, verify
1230+
// that it's of the form `#[proc_macro_derive(Foo)]` or
1231+
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
1232+
let list = match attr.meta_item_list() {
1233+
Some(list) => list,
1234+
None => return None,
1235+
};
1236+
if list.len() != 1 && list.len() != 2 {
1237+
diag.span_err(attr.span, "attribute must have either one or two arguments");
1238+
return None;
1239+
}
1240+
let trait_attr = match list[0].meta_item() {
1241+
Some(meta_item) => meta_item,
1242+
_ => {
1243+
diag.span_err(list[0].span(), "not a meta item");
1244+
return None;
1245+
}
1246+
};
1247+
let trait_ident = match trait_attr.ident() {
1248+
Some(trait_ident) if trait_attr.is_word() => trait_ident,
1249+
_ => {
1250+
diag.span_err(trait_attr.span, "must only be one word");
1251+
return None;
1252+
}
1253+
};
1254+
1255+
if !trait_ident.name.can_be_raw() {
1256+
diag.span_err(
1257+
trait_attr.span,
1258+
&format!("`{}` cannot be a name of {} macro", trait_ident, descr),
1259+
);
1260+
}
1261+
1262+
let attributes_attr = list.get(1);
1263+
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
1264+
if !attr.has_name(sym::attributes) {
1265+
diag.span_err(attr.span(), "second argument must be `attributes`")
1266+
}
1267+
attr.meta_item_list()
1268+
.unwrap_or_else(|| {
1269+
diag.span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`");
1270+
&[]
1271+
})
1272+
.iter()
1273+
.filter_map(|attr| {
1274+
let attr = match attr.meta_item() {
1275+
Some(meta_item) => meta_item,
1276+
_ => {
1277+
diag.span_err(attr.span(), "not a meta item");
1278+
return None;
1279+
}
1280+
};
1281+
1282+
let ident = match attr.ident() {
1283+
Some(ident) if attr.is_word() => ident,
1284+
_ => {
1285+
diag.span_err(attr.span, "must only be one word");
1286+
return None;
1287+
}
1288+
};
1289+
if !ident.name.can_be_raw() {
1290+
diag.span_err(
1291+
attr.span,
1292+
&format!("`{}` cannot be a name of derive helper attribute", ident),
1293+
);
1294+
}
1295+
1296+
Some(ident.name)
1297+
})
1298+
.collect()
1299+
} else {
1300+
Vec::new()
1301+
};
1302+
1303+
Some((trait_ident.name, proc_attrs))
1304+
}
1305+
12161306
/// This nonterminal looks like some specific enums from
12171307
/// `proc-macro-hack` and `procedural-masquerade` crates.
12181308
/// We need to maintain some special pretty-printing behavior for them due to incorrect

compiler/rustc_feature/src/builtin_attrs.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
448448
// Internal attributes, Macro related:
449449
// ==========================================================================
450450

451-
rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL),
451+
rustc_attr!(
452+
rustc_builtin_macro, AssumedUsed,
453+
template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"),
454+
IMPL_DETAIL,
455+
),
452456
rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
453457
rustc_attr!(
454458
rustc_macro_transparency, AssumedUsed,

library/core/src/macros/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[doc = include_str!("panic.md")]
22
#[macro_export]
3-
#[rustc_builtin_macro = "core_panic"]
3+
#[cfg_attr(bootstrap, rustc_builtin_macro = "core_panic")]
4+
#[cfg_attr(not(bootstrap), rustc_builtin_macro(core_panic))]
45
#[allow_internal_unstable(edition_panic)]
56
#[stable(feature = "core", since = "1.6.0")]
67
#[rustc_diagnostic_item = "core_panic_macro"]

library/std/src/macros.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
77
#[doc = include_str!("../../core/src/macros/panic.md")]
88
#[macro_export]
9-
#[rustc_builtin_macro = "std_panic"]
9+
#[cfg_attr(bootstrap, rustc_builtin_macro = "std_panic")]
10+
#[cfg_attr(not(bootstrap), rustc_builtin_macro(std_panic))]
1011
#[stable(feature = "rust1", since = "1.0.0")]
1112
#[allow_internal_unstable(edition_panic)]
1213
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// check-pass
2+
// compile-flags: --crate-type=lib
3+
4+
#![feature(decl_macro)]
5+
#![feature(lang_items)]
6+
#![feature(no_core)]
7+
#![feature(rustc_attrs)]
8+
9+
#![no_core]
10+
11+
#[rustc_builtin_macro]
12+
macro derive() {}
13+
14+
#[rustc_builtin_macro(Default, attributes(default))]
15+
macro Default() {}
16+
17+
mod default {
18+
pub trait Default {
19+
fn default() -> Self;
20+
}
21+
22+
impl Default for u8 {
23+
fn default() -> u8 {
24+
0
25+
}
26+
}
27+
}
28+
29+
#[lang = "sized"]
30+
trait Sized {}
31+
32+
#[derive(Default)]
33+
struct S {
34+
#[default] // OK
35+
field: u8,
36+
}

0 commit comments

Comments
 (0)