diff options
| author | Austin Bonander <austin.bonander@gmail.com> | 2017-01-09 01:31:14 -0800 |
|---|---|---|
| committer | Austin Bonander <austin.bonander@gmail.com> | 2017-01-16 22:41:22 -0800 |
| commit | 375cbd20cfcc9dbf15682bcfc0081ce5ce95567b (patch) | |
| tree | 6eee726bbefda54496b97963b696404cc50287aa /src/libsyntax | |
| parent | f6c0c4837c303e327a8b37649dd72f115b48f309 (diff) | |
| download | rust-375cbd20cfcc9dbf15682bcfc0081ce5ce95567b.tar.gz rust-375cbd20cfcc9dbf15682bcfc0081ce5ce95567b.zip | |
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]`
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 30 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 60 |
2 files changed, 84 insertions, 6 deletions
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 201e8d69494..1f7874274f7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -364,7 +364,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { kind.expect_from_annotatables(items) } SyntaxExtension::AttrProcMacro(ref mac) => { - let attr_toks = TokenStream::from_tts(tts_for_attr(&attr, &self.cx.parse_sess)); + let attr_toks = TokenStream::from_tts(tts_for_attr_args(&attr, + &self.cx.parse_sess)); + let item_toks = TokenStream::from_tts(tts_for_item(&item, &self.cx.parse_sess)); 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> { string_to_tts(text, parse_sess) } -fn tts_for_attr(attr: &ast::Attribute, parse_sess: &ParseSess) -> Vec<TokenTree> { - string_to_tts(pprust::attr_to_string(attr), parse_sess) +fn tts_for_attr_args(attr: &ast::Attribute, parse_sess: &ParseSess) -> Vec<TokenTree> { + use ast::MetaItemKind::*; + use print::pp::Breaks; + use print::pprust::PrintState; + + let token_string = match attr.value.node { + // For `#[foo]`, an empty token + Word => return vec![], + // For `#[foo(bar, baz)]`, returns `(bar, baz)` + List(ref items) => pprust::to_string(|s| { + s.popen()?; + s.commasep(Breaks::Consistent, + &items[..], + |s, i| s.print_meta_list_item(&i))?; + s.pclose() + }), + // For `#[foo = "bar"]`, returns `= "bar"` + NameValue(ref lit) => pprust::to_string(|s| { + s.word_space("=")?; + s.print_literal(lit) + }), + }; + + string_to_tts(token_string, parse_sess) } fn string_to_tts(text: String, parse_sess: &ParseSess) -> Vec<TokenTree> { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 90cca3129dc..2478ed169cd 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -30,7 +30,7 @@ use ast::{self, NodeId, PatKind}; use attr; use codemap::{CodeMap, Spanned}; use syntax_pos::Span; -use errors::{DiagnosticBuilder, Handler}; +use errors::{DiagnosticBuilder, Handler, FatalError}; use visit::{self, FnKind, Visitor}; use parse::ParseSess; use symbol::Symbol; @@ -325,6 +325,9 @@ declare_features! ( // The `unadjusted` ABI. Perma unstable. (active, abi_unadjusted, "1.16.0", None), + // Macros 1.1 + (active, proc_macro, "1.16.0", Some(35900)), + // Allows attributes on struct literal fields. (active, struct_field_attributes, "1.16.0", Some(38814)), ); @@ -377,8 +380,6 @@ declare_features! ( // Allows `..` in tuple (struct) patterns (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627)), (accepted, item_like_imports, "1.14.0", Some(35120)), - // Macros 1.1 - (accepted, proc_macro, "1.15.0", Some(35900)), ); // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -446,6 +447,10 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect() } +pub fn is_builtin_attr(attr: &ast::Attribute) -> bool { + BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| attr.check_name(builtin_name)) +} + // Attributes that have a special meaning to rustc or rustdoc pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[ // Normal attributes @@ -739,6 +744,16 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is currently unstable", cfg_fn!(windows_subsystem))), + ("proc_macro_attribute", Normal, Gated(Stability::Unstable, + "proc_macro", + "attribute proc macros are currently unstable", + cfg_fn!(proc_macro))), + + ("rustc_derive_registrar", Normal, Gated(Stability::Unstable, + "rustc_derive_registrar", + "used internally by rustc", + cfg_fn!(rustc_attrs))), + // Crate level attributes ("crate_name", CrateLevel, Ungated), ("crate_type", CrateLevel, Ungated), @@ -1380,6 +1395,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features { let mut features = Features::new(); + let mut feature_checker = MutexFeatureChecker::default(); + for attr in krate_attrs { if !attr.check_name("feature") { continue @@ -1403,6 +1420,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F if let Some(&(_, _, _, setter)) = ACTIVE_FEATURES.iter() .find(|& &(n, _, _, _)| name == n) { *(setter(&mut features)) = true; + feature_checker.collect(&features, mi.span); } else if let Some(&(_, _, _)) = REMOVED_FEATURES.iter() .find(|& &(n, _, _)| name == n) { @@ -1419,9 +1437,45 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F } } + feature_checker.check(span_handler); + features } +// A collector for mutually-exclusive features and their flag spans +#[derive(Default)] +struct MutexFeatureChecker { + proc_macro: Option<Span>, + custom_attribute: Option<Span>, +} + +impl MutexFeatureChecker { + // If this method turns out to be a hotspot due to branching, + // the branching can be eliminated by modifying `setter!()` to set these spans + // only for the features that need to be checked for mutual exclusion. + fn collect(&mut self, features: &Features, span: Span) { + if features.proc_macro { + // If self.proc_macro is None, set to Some(span) + self.proc_macro = self.proc_macro.or(Some(span)); + } + + if features.custom_attribute { + self.custom_attribute = self.custom_attribute.or(Some(span)); + } + } + + fn check(self, handler: &Handler) { + if let (Some(pm_span), Some(ca_span)) = (self.proc_macro, self.custom_attribute) { + handler.struct_span_err(pm_span, "Cannot use `#![feature(proc_macro)]` and \ + `#![feature(custom_attribute)] at the same time") + .span_note(ca_span, "`#![feature(custom_attribute)]` declared here") + .emit(); + + panic!(FatalError); + } + } +} + pub fn check_crate(krate: &ast::Crate, sess: &ParseSess, features: &Features, |
