Skip to content

Commit d4d276f

Browse files
committed
Rollup merge of rust-lang#38842 - abonander:proc_macro_attribute, r=jseyfried
Implement `#[proc_macro_attribute]` This implements `#[proc_macro_attribute]` as described in rust-lang/rfcs#1566 The following major (hopefully non-breaking) changes are included: * Refactor `proc_macro::TokenStream` to use `syntax::tokenstream::TokenStream`. * `proc_macro::tokenstream::TokenStream` no longer emits newlines between items, this can be trivially restored if desired * `proc_macro::TokenStream::from_str` does not try to parse an item anymore, moved to `impl MultiItemModifier for CustomDerive` with more informative error message * Implement `#[proc_macro_attribute]`, which expects functions of the kind `fn(TokenStream, TokenStream) -> TokenStream` * Reactivated `#![feature(proc_macro)]` and gated `#[proc_macro_attribute]` under it * `#![feature(proc_macro)]` and `#![feature(custom_attribute)]` are mutually exclusive * adding `#![feature(proc_macro)]` makes the expansion pass assume that any attributes that are not built-in, or introduced by existing syntax extensions, are proc-macro attributes * Fix `feature_gate::find_lang_feature_issue()` to not use `unwrap()` * This change wasn't necessary for this PR, but it helped debugging a problem where I was using the wrong feature string. * Move "completed feature gate checking" pass to after "name resolution" pass * This was necessary for proper feature-gating of `#[proc_macro_attribute]` invocations when the `proc_macro` feature flag isn't set. Prototype/Litmus Test: [Implementation](https://github.com/abonander/anterofit/blob/proc_macro/service-attr/src/lib.rs#L13) -- [Usage](https://github.com/abonander/anterofit/blob/proc_macro/service-attr/examples/post_service.rs#L35)
2 parents 437d2b5 + 04ecee1 commit d4d276f

File tree

24 files changed

+613
-127
lines changed

24 files changed

+613
-127
lines changed

Diff for: src/libproc_macro/lib.rs

+47-26
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ extern crate syntax;
3939
use std::fmt;
4040
use std::str::FromStr;
4141

42-
use syntax::ast;
42+
use syntax::errors::DiagnosticBuilder;
4343
use syntax::parse;
44-
use syntax::ptr::P;
44+
use syntax::tokenstream::TokenStream as TokenStream_;
4545

4646
/// The main type provided by this crate, representing an abstract stream of
4747
/// tokens.
@@ -54,7 +54,7 @@ use syntax::ptr::P;
5454
/// time!
5555
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
5656
pub struct TokenStream {
57-
inner: Vec<P<ast::Item>>,
57+
inner: TokenStream_,
5858
}
5959

6060
/// Error returned from `TokenStream::from_str`.
@@ -77,17 +77,41 @@ pub struct LexError {
7777
#[doc(hidden)]
7878
pub mod __internal {
7979
use std::cell::Cell;
80+
use std::rc::Rc;
8081

8182
use syntax::ast;
8283
use syntax::ptr::P;
83-
use syntax::parse::ParseSess;
84-
use super::TokenStream;
84+
use syntax::parse::{self, token, ParseSess};
85+
use syntax::tokenstream::TokenStream as TokenStream_;
86+
87+
use super::{TokenStream, LexError};
8588

8689
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
87-
TokenStream { inner: vec![item] }
90+
TokenStream { inner: TokenStream_::from_tokens(vec![
91+
token::Interpolated(Rc::new(token::NtItem(item)))
92+
])}
93+
}
94+
95+
pub fn token_stream_wrap(inner: TokenStream_) -> TokenStream {
96+
TokenStream {
97+
inner: inner
98+
}
99+
}
100+
101+
pub fn token_stream_parse_items(stream: TokenStream) -> Result<Vec<P<ast::Item>>, LexError> {
102+
with_parse_sess(move |sess| {
103+
let mut parser = parse::new_parser_from_ts(sess, stream.inner);
104+
let mut items = Vec::new();
105+
106+
while let Some(item) = try!(parser.parse_item().map_err(super::parse_to_lex_err)) {
107+
items.push(item)
108+
}
109+
110+
Ok(items)
111+
})
88112
}
89113

90-
pub fn token_stream_items(stream: TokenStream) -> Vec<P<ast::Item>> {
114+
pub fn token_stream_inner(stream: TokenStream) -> TokenStream_ {
91115
stream.inner
92116
}
93117

@@ -96,6 +120,10 @@ pub mod __internal {
96120
trait_name: &str,
97121
expand: fn(TokenStream) -> TokenStream,
98122
attributes: &[&'static str]);
123+
124+
fn register_attr_proc_macro(&mut self,
125+
name: &str,
126+
expand: fn(TokenStream, TokenStream) -> TokenStream);
99127
}
100128

101129
// Emulate scoped_thread_local!() here essentially
@@ -125,11 +153,17 @@ pub mod __internal {
125153
where F: FnOnce(&ParseSess) -> R
126154
{
127155
let p = CURRENT_SESS.with(|p| p.get());
128-
assert!(!p.is_null());
156+
assert!(!p.is_null(), "proc_macro::__internal::with_parse_sess() called \
157+
before set_parse_sess()!");
129158
f(unsafe { &*p })
130159
}
131160
}
132161

162+
fn parse_to_lex_err(mut err: DiagnosticBuilder) -> LexError {
163+
err.cancel();
164+
LexError { _inner: () }
165+
}
166+
133167
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
134168
impl FromStr for TokenStream {
135169
type Err = LexError;
@@ -138,30 +172,17 @@ impl FromStr for TokenStream {
138172
__internal::with_parse_sess(|sess| {
139173
let src = src.to_string();
140174
let name = "<proc-macro source code>".to_string();
141-
let mut parser = parse::new_parser_from_source_str(sess, name, src);
142-
let mut ret = TokenStream { inner: Vec::new() };
143-
loop {
144-
match parser.parse_item() {
145-
Ok(Some(item)) => ret.inner.push(item),
146-
Ok(None) => return Ok(ret),
147-
Err(mut err) => {
148-
err.cancel();
149-
return Err(LexError { _inner: () })
150-
}
151-
}
152-
}
175+
let tts = try!(parse::parse_tts_from_source_str(name, src, sess)
176+
.map_err(parse_to_lex_err));
177+
178+
Ok(__internal::token_stream_wrap(TokenStream_::from_tts(tts)))
153179
})
154180
}
155181
}
156182

157183
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
158184
impl fmt::Display for TokenStream {
159185
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160-
for item in self.inner.iter() {
161-
let item = syntax::print::pprust::item_to_string(item);
162-
try!(f.write_str(&item));
163-
try!(f.write_str("\n"));
164-
}
165-
Ok(())
186+
self.inner.fmt(f)
166187
}
167188
}

Diff for: src/librustc_driver/driver.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
681681
should_test: sess.opts.test,
682682
..syntax::ext::expand::ExpansionConfig::default(crate_name.to_string())
683683
};
684+
684685
let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver);
685686
let err_count = ecx.parse_sess.span_diagnostic.err_count();
686687

@@ -740,17 +741,6 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
740741
"checking for inline asm in case the target doesn't support it",
741742
|| no_asm::check_crate(sess, &krate));
742743

743-
// Needs to go *after* expansion to be able to check the results of macro expansion.
744-
time(time_passes, "complete gated feature checking", || {
745-
sess.track_errors(|| {
746-
syntax::feature_gate::check_crate(&krate,
747-
&sess.parse_sess,
748-
&sess.features.borrow(),
749-
&attributes,
750-
sess.opts.unstable_features);
751-
})
752-
})?;
753-
754744
time(sess.time_passes(),
755745
"early lint checks",
756746
|| lint::check_ast_crate(sess, &krate));
@@ -768,6 +758,17 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
768758
Ok(())
769759
})?;
770760

761+
// Needs to go *after* expansion to be able to check the results of macro expansion.
762+
time(time_passes, "complete gated feature checking", || {
763+
sess.track_errors(|| {
764+
syntax::feature_gate::check_crate(&krate,
765+
&sess.parse_sess,
766+
&sess.features.borrow(),
767+
&attributes,
768+
sess.opts.unstable_features);
769+
})
770+
})?;
771+
771772
// Lower ast -> hir.
772773
let hir_forest = time(sess.time_passes(), "lowering ast -> hir", || {
773774
let hir_crate = lower_crate(sess, &krate, &mut resolver);

Diff for: src/librustc_metadata/creader.rs

+10
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());

Diff for: src/librustc_resolve/lib.rs

+44-2
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 {

Diff for: src/librustc_resolve/macros.rs

+42-1
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
}

0 commit comments

Comments
 (0)