Skip to content

Commit 375cbd2

Browse files
committed
Implement #[proc_macro_attribute]
* Add support for `#[proc_macro]` * Reactivate `proc_macro` feature and gate `#[proc_macro_attribute]` under it * Have `#![feature(proc_macro)]` imply `#![feature(use_extern_macros)]`, error on legacy import of proc macros via `#[macro_use]`
1 parent f6c0c48 commit 375cbd2

File tree

16 files changed

+526
-79
lines changed

16 files changed

+526
-79
lines changed

src/librustc_driver/driver.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
677677
should_test: sess.opts.test,
678678
..syntax::ext::expand::ExpansionConfig::default(crate_name.to_string())
679679
};
680+
680681
let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver);
681682
let err_count = ecx.parse_sess.span_diagnostic.err_count();
682683

src/librustc_metadata/creader.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ impl<'a> CrateLoader<'a> {
578578
use proc_macro::__internal::Registry;
579579
use rustc_back::dynamic_lib::DynamicLibrary;
580580
use syntax_ext::deriving::custom::CustomDerive;
581+
use syntax_ext::proc_macro_impl::AttrProcMacro;
581582

582583
let path = match dylib {
583584
Some(dylib) => dylib,
@@ -613,6 +614,15 @@ impl<'a> CrateLoader<'a> {
613614
);
614615
self.0.push((Symbol::intern(trait_name), Rc::new(derive)));
615616
}
617+
618+
fn register_attr_proc_macro(&mut self,
619+
name: &str,
620+
expand: fn(TokenStream, TokenStream) -> TokenStream) {
621+
let expand = SyntaxExtension::AttrProcMacro(
622+
Box::new(AttrProcMacro { inner: expand })
623+
);
624+
self.0.push((Symbol::intern(name), Rc::new(expand)));
625+
}
616626
}
617627

618628
let mut my_registrar = MyRegistrar(Vec::new());

src/librustc_resolve/lib.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, Generics};
6161
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
6262
use syntax::ast::{Local, Mutability, Pat, PatKind, Path};
6363
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
64-
use syntax::feature_gate::{emit_feature_err, GateIssue};
64+
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue};
6565

6666
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
6767
use errors::DiagnosticBuilder;
@@ -1123,6 +1123,12 @@ pub struct Resolver<'a> {
11231123

11241124
// Avoid duplicated errors for "name already defined".
11251125
name_already_seen: FxHashMap<Name, Span>,
1126+
1127+
// If `#![feature(proc_macro)]` is set
1128+
proc_macro_enabled: bool,
1129+
1130+
// A set of procedural macros imported by `#[macro_use]` that have already been warned about
1131+
warned_proc_macros: FxHashSet<Name>,
11261132
}
11271133

11281134
pub struct ResolverArenas<'a> {
@@ -1227,6 +1233,8 @@ impl<'a> Resolver<'a> {
12271233
invocations.insert(Mark::root(),
12281234
arenas.alloc_invocation_data(InvocationData::root(graph_root)));
12291235

1236+
let features = session.features.borrow();
1237+
12301238
Resolver {
12311239
session: session,
12321240

@@ -1284,7 +1292,9 @@ impl<'a> Resolver<'a> {
12841292
span: DUMMY_SP,
12851293
vis: ty::Visibility::Public,
12861294
}),
1287-
use_extern_macros: session.features.borrow().use_extern_macros,
1295+
1296+
// `#![feature(proc_macro)]` implies `#[feature(extern_macros)]`
1297+
use_extern_macros: features.use_extern_macros || features.proc_macro,
12881298

12891299
exported_macros: Vec::new(),
12901300
crate_loader: crate_loader,
@@ -1296,6 +1306,8 @@ impl<'a> Resolver<'a> {
12961306
invocations: invocations,
12971307
name_already_seen: FxHashMap(),
12981308
whitelisted_legacy_custom_derives: Vec::new(),
1309+
proc_macro_enabled: features.proc_macro,
1310+
warned_proc_macros: FxHashSet(),
12991311
}
13001312
}
13011313

@@ -1525,6 +1537,8 @@ impl<'a> Resolver<'a> {
15251537

15261538
debug!("(resolving item) resolving {}", name);
15271539

1540+
self.check_proc_macro_attrs(&item.attrs);
1541+
15281542
match item.node {
15291543
ItemKind::Enum(_, ref generics) |
15301544
ItemKind::Ty(_, ref generics) |
@@ -1554,6 +1568,8 @@ impl<'a> Resolver<'a> {
15541568
walk_list!(this, visit_ty_param_bound, bounds);
15551569

15561570
for trait_item in trait_items {
1571+
this.check_proc_macro_attrs(&trait_item.attrs);
1572+
15571573
match trait_item.node {
15581574
TraitItemKind::Const(_, ref default) => {
15591575
// Only impose the restrictions of
@@ -1738,6 +1754,7 @@ impl<'a> Resolver<'a> {
17381754
this.with_self_rib(Def::SelfTy(trait_id, Some(item_def_id)), |this| {
17391755
this.with_current_self_type(self_type, |this| {
17401756
for impl_item in impl_items {
1757+
this.check_proc_macro_attrs(&impl_item.attrs);
17411758
this.resolve_visibility(&impl_item.vis);
17421759
match impl_item.node {
17431760
ImplItemKind::Const(..) => {
@@ -3184,6 +3201,31 @@ impl<'a> Resolver<'a> {
31843201
let msg = "`self` no longer imports values".to_string();
31853202
self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
31863203
}
3204+
3205+
fn check_proc_macro_attrs(&mut self, attrs: &[ast::Attribute]) {
3206+
if self.proc_macro_enabled { return; }
3207+
3208+
for attr in attrs {
3209+
let maybe_binding = self.builtin_macros.get(&attr.name()).cloned().or_else(|| {
3210+
let ident = Ident::with_empty_ctxt(attr.name());
3211+
self.resolve_lexical_macro_path_segment(ident, MacroNS, None).ok()
3212+
});
3213+
3214+
if let Some(binding) = maybe_binding {
3215+
if let SyntaxExtension::AttrProcMacro(..) = *binding.get_macro(self) {
3216+
attr::mark_known(attr);
3217+
3218+
let msg = "attribute procedural macros are experimental";
3219+
let feature = "proc_macro";
3220+
3221+
feature_err(&self.session.parse_sess, feature,
3222+
attr.span, GateIssue::Language, msg)
3223+
.span_note(binding.span, "procedural macro imported here")
3224+
.emit();
3225+
}
3226+
}
3227+
}
3228+
}
31873229
}
31883230

31893231
fn is_struct_like(def: Def) -> bool {

src/librustc_resolve/macros.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use syntax::ext::base::{NormalTT, Resolver as SyntaxResolver, SyntaxExtension};
2727
use syntax::ext::expand::{Expansion, mark_tts};
2828
use syntax::ext::hygiene::Mark;
2929
use syntax::ext::tt::macro_rules;
30-
use syntax::feature_gate::{emit_feature_err, GateIssue};
30+
use syntax::feature_gate::{emit_feature_err, GateIssue, is_builtin_attr};
3131
use syntax::fold::{self, Folder};
3232
use syntax::ptr::P;
3333
use syntax::symbol::keywords;
@@ -183,6 +183,10 @@ impl<'a> base::Resolver for Resolver<'a> {
183183
},
184184
None => {}
185185
}
186+
187+
if self.proc_macro_enabled && !is_builtin_attr(&attrs[i]) {
188+
return Some(attrs.remove(i));
189+
}
186190
}
187191
None
188192
}
@@ -373,6 +377,10 @@ impl<'a> Resolver<'a> {
373377
let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, Some(span));
374378
let (legacy_resolution, resolution) = match (legacy_resolution, resolution) {
375379
(Some(legacy_resolution), Ok(resolution)) => (legacy_resolution, resolution),
380+
(Some(MacroBinding::Modern(binding)), Err(_)) => {
381+
self.err_if_macro_use_proc_macro(ident.name, span, binding);
382+
continue
383+
},
376384
_ => continue,
377385
};
378386
let (legacy_span, participle) = match legacy_resolution {
@@ -469,4 +477,37 @@ impl<'a> Resolver<'a> {
469477
self.exported_macros.push(def);
470478
}
471479
}
480+
481+
/// Error if `ext` is a Macros 1.1 procedural macro being imported by `#[macro_use]`
482+
fn err_if_macro_use_proc_macro(&mut self, name: Name, use_span: Span,
483+
binding: &NameBinding<'a>) {
484+
use self::SyntaxExtension::*;
485+
486+
let krate = binding.def().def_id().krate;
487+
488+
// Plugin-based syntax extensions are exempt from this check
489+
if krate == BUILTIN_MACROS_CRATE { return; }
490+
491+
let ext = binding.get_macro(self);
492+
493+
match *ext {
494+
// If `ext` is a procedural macro, check if we've already warned about it
495+
AttrProcMacro(_) | ProcMacro(_) => if !self.warned_proc_macros.insert(name) { return; },
496+
_ => return,
497+
}
498+
499+
let warn_msg = match *ext {
500+
AttrProcMacro(_) => "attribute procedural macros cannot be \
501+
imported with `#[macro_use]`",
502+
ProcMacro(_) => "procedural macros cannot be imported with `#[macro_use]`",
503+
_ => return,
504+
};
505+
506+
let crate_name = self.session.cstore.crate_name(krate);
507+
508+
self.session.struct_span_err(use_span, warn_msg)
509+
.help(&format!("instead, import the procedural macro like any other item: \
510+
`use {}::{};`", crate_name, name))
511+
.emit();
512+
}
472513
}

src/libsyntax/ext/expand.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
364364
kind.expect_from_annotatables(items)
365365
}
366366
SyntaxExtension::AttrProcMacro(ref mac) => {
367-
let attr_toks = TokenStream::from_tts(tts_for_attr(&attr, &self.cx.parse_sess));
367+
let attr_toks = TokenStream::from_tts(tts_for_attr_args(&attr,
368+
&self.cx.parse_sess));
369+
368370
let item_toks = TokenStream::from_tts(tts_for_item(&item, &self.cx.parse_sess));
369371

370372
let tok_result = mac.expand(self.cx, attr.span, attr_toks, item_toks);
@@ -640,8 +642,30 @@ fn tts_for_item(item: &Annotatable, parse_sess: &ParseSess) -> Vec<TokenTree> {
640642
string_to_tts(text, parse_sess)
641643
}
642644

643-
fn tts_for_attr(attr: &ast::Attribute, parse_sess: &ParseSess) -> Vec<TokenTree> {
644-
string_to_tts(pprust::attr_to_string(attr), parse_sess)
645+
fn tts_for_attr_args(attr: &ast::Attribute, parse_sess: &ParseSess) -> Vec<TokenTree> {
646+
use ast::MetaItemKind::*;
647+
use print::pp::Breaks;
648+
use print::pprust::PrintState;
649+
650+
let token_string = match attr.value.node {
651+
// For `#[foo]`, an empty token
652+
Word => return vec![],
653+
// For `#[foo(bar, baz)]`, returns `(bar, baz)`
654+
List(ref items) => pprust::to_string(|s| {
655+
s.popen()?;
656+
s.commasep(Breaks::Consistent,
657+
&items[..],
658+
|s, i| s.print_meta_list_item(&i))?;
659+
s.pclose()
660+
}),
661+
// For `#[foo = "bar"]`, returns `= "bar"`
662+
NameValue(ref lit) => pprust::to_string(|s| {
663+
s.word_space("=")?;
664+
s.print_literal(lit)
665+
}),
666+
};
667+
668+
string_to_tts(token_string, parse_sess)
645669
}
646670

647671
fn string_to_tts(text: String, parse_sess: &ParseSess) -> Vec<TokenTree> {

src/libsyntax/feature_gate.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use ast::{self, NodeId, PatKind};
3030
use attr;
3131
use codemap::{CodeMap, Spanned};
3232
use syntax_pos::Span;
33-
use errors::{DiagnosticBuilder, Handler};
33+
use errors::{DiagnosticBuilder, Handler, FatalError};
3434
use visit::{self, FnKind, Visitor};
3535
use parse::ParseSess;
3636
use symbol::Symbol;
@@ -325,6 +325,9 @@ declare_features! (
325325
// The `unadjusted` ABI. Perma unstable.
326326
(active, abi_unadjusted, "1.16.0", None),
327327

328+
// Macros 1.1
329+
(active, proc_macro, "1.16.0", Some(35900)),
330+
328331
// Allows attributes on struct literal fields.
329332
(active, struct_field_attributes, "1.16.0", Some(38814)),
330333
);
@@ -377,8 +380,6 @@ declare_features! (
377380
// Allows `..` in tuple (struct) patterns
378381
(accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627)),
379382
(accepted, item_like_imports, "1.14.0", Some(35120)),
380-
// Macros 1.1
381-
(accepted, proc_macro, "1.15.0", Some(35900)),
382383
);
383384
// (changing above list without updating src/doc/reference.md makes @cmr sad)
384385

@@ -446,6 +447,10 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att
446447
BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
447448
}
448449

450+
pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
451+
BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| attr.check_name(builtin_name))
452+
}
453+
449454
// Attributes that have a special meaning to rustc or rustdoc
450455
pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
451456
// Normal attributes
@@ -739,6 +744,16 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
739744
is currently unstable",
740745
cfg_fn!(windows_subsystem))),
741746

747+
("proc_macro_attribute", Normal, Gated(Stability::Unstable,
748+
"proc_macro",
749+
"attribute proc macros are currently unstable",
750+
cfg_fn!(proc_macro))),
751+
752+
("rustc_derive_registrar", Normal, Gated(Stability::Unstable,
753+
"rustc_derive_registrar",
754+
"used internally by rustc",
755+
cfg_fn!(rustc_attrs))),
756+
742757
// Crate level attributes
743758
("crate_name", CrateLevel, Ungated),
744759
("crate_type", CrateLevel, Ungated),
@@ -1380,6 +1395,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
13801395
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
13811396
let mut features = Features::new();
13821397

1398+
let mut feature_checker = MutexFeatureChecker::default();
1399+
13831400
for attr in krate_attrs {
13841401
if !attr.check_name("feature") {
13851402
continue
@@ -1403,6 +1420,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F
14031420
if let Some(&(_, _, _, setter)) = ACTIVE_FEATURES.iter()
14041421
.find(|& &(n, _, _, _)| name == n) {
14051422
*(setter(&mut features)) = true;
1423+
feature_checker.collect(&features, mi.span);
14061424
}
14071425
else if let Some(&(_, _, _)) = REMOVED_FEATURES.iter()
14081426
.find(|& &(n, _, _)| name == n) {
@@ -1419,9 +1437,45 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F
14191437
}
14201438
}
14211439

1440+
feature_checker.check(span_handler);
1441+
14221442
features
14231443
}
14241444

1445+
// A collector for mutually-exclusive features and their flag spans
1446+
#[derive(Default)]
1447+
struct MutexFeatureChecker {
1448+
proc_macro: Option<Span>,
1449+
custom_attribute: Option<Span>,
1450+
}
1451+
1452+
impl MutexFeatureChecker {
1453+
// If this method turns out to be a hotspot due to branching,
1454+
// the branching can be eliminated by modifying `setter!()` to set these spans
1455+
// only for the features that need to be checked for mutual exclusion.
1456+
fn collect(&mut self, features: &Features, span: Span) {
1457+
if features.proc_macro {
1458+
// If self.proc_macro is None, set to Some(span)
1459+
self.proc_macro = self.proc_macro.or(Some(span));
1460+
}
1461+
1462+
if features.custom_attribute {
1463+
self.custom_attribute = self.custom_attribute.or(Some(span));
1464+
}
1465+
}
1466+
1467+
fn check(self, handler: &Handler) {
1468+
if let (Some(pm_span), Some(ca_span)) = (self.proc_macro, self.custom_attribute) {
1469+
handler.struct_span_err(pm_span, "Cannot use `#![feature(proc_macro)]` and \
1470+
`#![feature(custom_attribute)] at the same time")
1471+
.span_note(ca_span, "`#![feature(custom_attribute)]` declared here")
1472+
.emit();
1473+
1474+
panic!(FatalError);
1475+
}
1476+
}
1477+
}
1478+
14251479
pub fn check_crate(krate: &ast::Crate,
14261480
sess: &ParseSess,
14271481
features: &Features,

src/libsyntax_ext/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub mod proc_macro_registrar;
4747
// for custom_derive
4848
pub mod deriving;
4949

50+
pub mod proc_macro_impl;
51+
5052
use std::rc::Rc;
5153
use syntax::ast;
5254
use syntax::ext::base::{MacroExpanderFn, NormalTT, MultiModifier, NamedSyntaxExtension};

0 commit comments

Comments
 (0)