Skip to content

Commit 5ebd7c5

Browse files
authored
Rollup merge of rust-lang#37614 - keeperofdakeys:proc_macro, r=jseyfried
macros 1.1: Allow proc_macro functions to declare attributes to be mark as used This PR allows proc macro functions to declare attribute names that should be marked as used when attached to the deriving item. There are a few questions for this PR. - Currently this uses a separate attribute named `#[proc_macro_attributes(..)]`, is this the best choice? - In order to make this work, the `check_attribute` function had to be modified to not error on attributes marked as used. This is a pretty large change in semantics, is there a better way to do this? - I've got a few clones where I don't know if I need them (like turning `item` into a `TokenStream`), can these be avoided? - Is switching to `MultiItemDecorator` the right thing here? Also fixes rust-lang#37563.
2 parents 3d2ffa0 + 134ef4f commit 5ebd7c5

File tree

21 files changed

+298
-72
lines changed

21 files changed

+298
-72
lines changed

src/libproc_macro/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ pub mod __internal {
9595
pub trait Registry {
9696
fn register_custom_derive(&mut self,
9797
trait_name: &str,
98-
expand: fn(TokenStream) -> TokenStream);
98+
expand: fn(TokenStream) -> TokenStream,
99+
attributes: &[&'static str]);
99100
}
100101

101102
// Emulate scoped_thread_local!() here essentially

src/librustc_lint/unused.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
1919

2020
use syntax::ast;
2121
use syntax::attr;
22-
use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType};
22+
use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
2323
use syntax::parse::token::keywords;
2424
use syntax::ptr::P;
2525
use syntax_pos::Span;
@@ -245,7 +245,7 @@ impl LateLintPass for UnusedAttributes {
245245
debug!("checking attribute: {:?}", attr);
246246

247247
// Note that check_name() marks the attribute as used if it matches.
248-
for &(ref name, ty, _) in KNOWN_ATTRIBUTES {
248+
for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
249249
match ty {
250250
AttributeType::Whitelisted if attr.check_name(name) => {
251251
debug!("{:?} is Whitelisted", name);
@@ -267,7 +267,7 @@ impl LateLintPass for UnusedAttributes {
267267
debug!("Emitting warning for: {:?}", attr);
268268
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
269269
// Is it a builtin attribute that must be used at the crate level?
270-
let known_crate = KNOWN_ATTRIBUTES.iter()
270+
let known_crate = BUILTIN_ATTRIBUTES.iter()
271271
.find(|&&(name, ty, _)| attr.name() == name && ty == AttributeType::CrateLevel)
272272
.is_some();
273273

src/librustc_metadata/creader.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,12 @@ impl<'a> CrateLoader<'a> {
624624
impl Registry for MyRegistrar {
625625
fn register_custom_derive(&mut self,
626626
trait_name: &str,
627-
expand: fn(TokenStream) -> TokenStream) {
628-
let derive = SyntaxExtension::CustomDerive(Box::new(CustomDerive::new(expand)));
627+
expand: fn(TokenStream) -> TokenStream,
628+
attributes: &[&'static str]) {
629+
let attrs = attributes.iter().map(|s| InternedString::new(s)).collect();
630+
let derive = SyntaxExtension::CustomDerive(
631+
Box::new(CustomDerive::new(expand, attrs))
632+
);
629633
self.0.push((intern(trait_name), derive));
630634
}
631635
}

src/libsyntax/attr.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ use std::cell::{RefCell, Cell};
3232
use std::collections::HashSet;
3333

3434
thread_local! {
35-
static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new())
35+
static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new());
36+
static KNOWN_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new());
3637
}
3738

3839
enum AttrError {
@@ -81,6 +82,29 @@ pub fn is_used(attr: &Attribute) -> bool {
8182
})
8283
}
8384

85+
pub fn mark_known(attr: &Attribute) {
86+
debug!("Marking {:?} as known.", attr);
87+
let AttrId(id) = attr.node.id;
88+
KNOWN_ATTRS.with(|slot| {
89+
let idx = (id / 64) as usize;
90+
let shift = id % 64;
91+
if slot.borrow().len() <= idx {
92+
slot.borrow_mut().resize(idx + 1, 0);
93+
}
94+
slot.borrow_mut()[idx] |= 1 << shift;
95+
});
96+
}
97+
98+
pub fn is_known(attr: &Attribute) -> bool {
99+
let AttrId(id) = attr.node.id;
100+
KNOWN_ATTRS.with(|slot| {
101+
let idx = (id / 64) as usize;
102+
let shift = id % 64;
103+
slot.borrow().get(idx).map(|bits| bits & (1 << shift) != 0)
104+
.unwrap_or(false)
105+
})
106+
}
107+
84108
impl NestedMetaItem {
85109
/// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
86110
pub fn meta_item(&self) -> Option<&P<MetaItem>> {

src/libsyntax/feature_gate.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,11 @@ macro_rules! cfg_fn {
421421
}
422422

423423
pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, AttributeGate)> {
424-
KNOWN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
424+
BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
425425
}
426426

427427
// Attributes that have a special meaning to rustc or rustdoc
428-
pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
428+
pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
429429
// Normal attributes
430430

431431
("warn", Normal, Ungated),
@@ -800,12 +800,12 @@ impl<'a> Context<'a> {
800800
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
801801
debug!("check_attribute(attr = {:?})", attr);
802802
let name = &*attr.name();
803-
for &(n, ty, ref gateage) in KNOWN_ATTRIBUTES {
803+
for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
804804
if n == name {
805805
if let &Gated(_, ref name, ref desc, ref has_feature) = gateage {
806806
gate_feature_fn!(self, has_feature, attr.span, name, desc);
807807
}
808-
debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
808+
debug!("check_attribute: {:?} is builtin, {:?}, {:?}", name, ty, gateage);
809809
return;
810810
}
811811
}
@@ -825,6 +825,8 @@ impl<'a> Context<'a> {
825825
are reserved for internal compiler diagnostics");
826826
} else if name.starts_with("derive_") {
827827
gate_feature!(self, custom_derive, attr.span, EXPLAIN_DERIVE_UNDERSCORE);
828+
} else if attr::is_known(attr) {
829+
debug!("check_attribute: {:?} is known", name);
828830
} else {
829831
// Only run the custom attribute lint during regular
830832
// feature gate checking. Macro gating runs

src/libsyntax_ext/deriving/custom.rs

+35-27
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,43 @@ use std::panic;
1212

1313
use errors::FatalError;
1414
use proc_macro::{TokenStream, __internal};
15-
use syntax::ast::{self, ItemKind};
16-
use syntax::codemap::{ExpnInfo, MacroAttribute, NameAndSpan, Span};
15+
use syntax::ast::{self, ItemKind, Attribute};
16+
use syntax::attr::{mark_used, mark_known};
17+
use syntax::codemap::Span;
1718
use syntax::ext::base::*;
1819
use syntax::fold::Folder;
19-
use syntax::parse::token::intern;
20-
use syntax::print::pprust;
20+
use syntax::parse::token::InternedString;
21+
use syntax::visit::Visitor;
22+
23+
struct MarkAttrs<'a>(&'a [InternedString]);
24+
25+
impl<'a> Visitor for MarkAttrs<'a> {
26+
fn visit_attribute(&mut self, attr: &Attribute) {
27+
if self.0.contains(&attr.name()) {
28+
mark_used(attr);
29+
mark_known(attr);
30+
}
31+
}
32+
}
2133

2234
pub struct CustomDerive {
2335
inner: fn(TokenStream) -> TokenStream,
36+
attrs: Vec<InternedString>,
2437
}
2538

2639
impl CustomDerive {
27-
pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
28-
CustomDerive { inner: inner }
40+
pub fn new(inner: fn(TokenStream) -> TokenStream,
41+
attrs: Vec<InternedString>)
42+
-> CustomDerive {
43+
CustomDerive { inner: inner, attrs: attrs }
2944
}
3045
}
3146

3247
impl MultiItemModifier for CustomDerive {
3348
fn expand(&self,
3449
ecx: &mut ExtCtxt,
3550
span: Span,
36-
meta_item: &ast::MetaItem,
51+
_meta_item: &ast::MetaItem,
3752
item: Annotatable)
3853
-> Vec<Annotatable> {
3954
let item = match item {
@@ -47,31 +62,23 @@ impl MultiItemModifier for CustomDerive {
4762
};
4863
match item.node {
4964
ItemKind::Struct(..) |
50-
ItemKind::Enum(..) => {}
65+
ItemKind::Enum(..) => {},
5166
_ => {
5267
ecx.span_err(span, "custom derive attributes may only be \
5368
applied to struct/enum items");
5469
return Vec::new()
5570
}
5671
}
5772

58-
let input_span = Span {
59-
expn_id: ecx.codemap().record_expansion(ExpnInfo {
60-
call_site: span,
61-
callee: NameAndSpan {
62-
format: MacroAttribute(intern(&pprust::meta_item_to_string(meta_item))),
63-
span: Some(span),
64-
allow_internal_unstable: true,
65-
},
66-
}),
67-
..item.span
68-
};
69-
let input = __internal::new_token_stream(item);
73+
// Mark attributes as known, and used.
74+
MarkAttrs(&self.attrs).visit_item(&item);
75+
76+
let input = __internal::new_token_stream(item.clone());
7077
let res = __internal::set_parse_sess(&ecx.parse_sess, || {
7178
let inner = self.inner;
7279
panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
7380
});
74-
let item = match res {
81+
let new_items = match res {
7582
Ok(stream) => __internal::token_stream_items(stream),
7683
Err(e) => {
7784
let msg = "custom derive attribute panicked";
@@ -88,12 +95,13 @@ impl MultiItemModifier for CustomDerive {
8895
}
8996
};
9097

91-
// Right now we have no knowledge of spans at all in custom derive
92-
// macros, everything is just parsed as a string. Reassign all spans to
93-
// the input `item` for better errors here.
94-
item.into_iter().flat_map(|item| {
95-
ChangeSpan { span: input_span }.fold_item(item)
96-
}).map(Annotatable::Item).collect()
98+
let mut res = vec![Annotatable::Item(item)];
99+
// Reassign spans of all expanded items to the input `item`
100+
// for better errors here.
101+
res.extend(new_items.into_iter().flat_map(|item| {
102+
ChangeSpan { span: span }.fold_item(item)
103+
}).map(Annotatable::Item));
104+
res
97105
}
98106
}
99107

src/libsyntax_ext/proc_macro_registrar.rs

+52-15
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct CustomDerive {
3030
trait_name: InternedString,
3131
function_name: Ident,
3232
span: Span,
33+
attrs: Vec<InternedString>,
3334
}
3435

3536
struct CollectCustomDerives<'a> {
@@ -144,7 +145,8 @@ impl<'a> Visitor for CollectCustomDerives<'a> {
144145
}
145146

146147
// Once we've located the `#[proc_macro_derive]` attribute, verify
147-
// that it's of the form `#[proc_macro_derive(Foo)]`
148+
// that it's of the form `#[proc_macro_derive(Foo)]` or
149+
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
148150
let list = match attr.meta_item_list() {
149151
Some(list) => list,
150152
None => {
@@ -154,38 +156,69 @@ impl<'a> Visitor for CollectCustomDerives<'a> {
154156
return
155157
}
156158
};
157-
if list.len() != 1 {
159+
if list.len() != 1 && list.len() != 2 {
158160
self.handler.span_err(attr.span(),
159-
"attribute must only have one argument");
161+
"attribute must have either one or two arguments");
160162
return
161163
}
162-
let attr = &list[0];
163-
let trait_name = match attr.name() {
164+
let trait_attr = &list[0];
165+
let attributes_attr = list.get(1);
166+
let trait_name = match trait_attr.name() {
164167
Some(name) => name,
165168
_ => {
166-
self.handler.span_err(attr.span(), "not a meta item");
169+
self.handler.span_err(trait_attr.span(), "not a meta item");
167170
return
168171
}
169172
};
170-
if !attr.is_word() {
171-
self.handler.span_err(attr.span(), "must only be one word");
173+
if !trait_attr.is_word() {
174+
self.handler.span_err(trait_attr.span(), "must only be one word");
172175
}
173176

174177
if deriving::is_builtin_trait(&trait_name) {
175-
self.handler.span_err(attr.span(),
178+
self.handler.span_err(trait_attr.span(),
176179
"cannot override a built-in #[derive] mode");
177180
}
178181

179182
if self.derives.iter().any(|d| d.trait_name == trait_name) {
180-
self.handler.span_err(attr.span(),
183+
self.handler.span_err(trait_attr.span(),
181184
"derive mode defined twice in this crate");
182185
}
183186

187+
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
188+
if !attr.check_name("attributes") {
189+
self.handler.span_err(attr.span(), "second argument must be `attributes`")
190+
}
191+
attr.meta_item_list().unwrap_or_else(|| {
192+
self.handler.span_err(attr.span(),
193+
"attribute must be of form: \
194+
`attributes(foo, bar)`");
195+
&[]
196+
}).into_iter().filter_map(|attr| {
197+
let name = match attr.name() {
198+
Some(name) => name,
199+
_ => {
200+
self.handler.span_err(attr.span(), "not a meta item");
201+
return None;
202+
},
203+
};
204+
205+
if !attr.is_word() {
206+
self.handler.span_err(attr.span(), "must only be one word");
207+
return None;
208+
}
209+
210+
Some(name)
211+
}).collect()
212+
} else {
213+
Vec::new()
214+
};
215+
184216
if self.in_root {
185217
self.derives.push(CustomDerive {
186218
span: item.span,
187219
trait_name: trait_name,
188220
function_name: item.ident,
221+
attrs: proc_attrs,
189222
});
190223
} else {
191224
let msg = "functions tagged with `#[proc_macro_derive]` must \
@@ -219,8 +252,8 @@ impl<'a> Visitor for CollectCustomDerives<'a> {
219252
//
220253
// #[plugin_registrar]
221254
// fn registrar(registrar: &mut Registry) {
222-
// registrar.register_custom_derive($name_trait1, ::$name1);
223-
// registrar.register_custom_derive($name_trait2, ::$name2);
255+
// registrar.register_custom_derive($name_trait1, ::$name1, &[]);
256+
// registrar.register_custom_derive($name_trait2, ::$name2, &["attribute_name"]);
224257
// // ...
225258
// }
226259
// }
@@ -249,14 +282,18 @@ fn mk_registrar(cx: &mut ExtCtxt,
249282
let stmts = custom_derives.iter().map(|cd| {
250283
let path = cx.path_global(cd.span, vec![cd.function_name]);
251284
let trait_name = cx.expr_str(cd.span, cd.trait_name.clone());
252-
(path, trait_name)
253-
}).map(|(path, trait_name)| {
285+
let attrs = cx.expr_vec_slice(
286+
span,
287+
cd.attrs.iter().map(|s| cx.expr_str(cd.span, s.clone())).collect::<Vec<_>>()
288+
);
289+
(path, trait_name, attrs)
290+
}).map(|(path, trait_name, attrs)| {
254291
let registrar = cx.expr_ident(span, registrar);
255292
let ufcs_path = cx.path(span, vec![proc_macro, __internal, registry,
256293
register_custom_derive]);
257294
cx.expr_call(span,
258295
cx.expr_path(ufcs_path),
259-
vec![registrar, trait_name, cx.expr_path(path)])
296+
vec![registrar, trait_name, cx.expr_path(path), attrs])
260297
}).map(|expr| {
261298
cx.stmt_expr(expr)
262299
}).collect::<Vec<_>>();

0 commit comments

Comments
 (0)