-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement #[proc_macro_attribute]
#38842
Changes from all commits
f6c0c48
375cbd2
6843961
04ecee1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,9 +39,9 @@ extern crate syntax; | |
use std::fmt; | ||
use std::str::FromStr; | ||
|
||
use syntax::ast; | ||
use syntax::errors::DiagnosticBuilder; | ||
use syntax::parse; | ||
use syntax::ptr::P; | ||
use syntax::tokenstream::TokenStream as TokenStream_; | ||
|
||
/// The main type provided by this crate, representing an abstract stream of | ||
/// tokens. | ||
|
@@ -54,7 +54,7 @@ use syntax::ptr::P; | |
/// time! | ||
#[stable(feature = "proc_macro_lib", since = "1.15.0")] | ||
pub struct TokenStream { | ||
inner: Vec<P<ast::Item>>, | ||
inner: TokenStream_, | ||
} | ||
|
||
/// Error returned from `TokenStream::from_str`. | ||
|
@@ -77,17 +77,41 @@ pub struct LexError { | |
#[doc(hidden)] | ||
pub mod __internal { | ||
use std::cell::Cell; | ||
use std::rc::Rc; | ||
|
||
use syntax::ast; | ||
use syntax::ptr::P; | ||
use syntax::parse::ParseSess; | ||
use super::TokenStream; | ||
use syntax::parse::{self, token, ParseSess}; | ||
use syntax::tokenstream::TokenStream as TokenStream_; | ||
|
||
use super::{TokenStream, LexError}; | ||
|
||
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream { | ||
TokenStream { inner: vec![item] } | ||
TokenStream { inner: TokenStream_::from_tokens(vec![ | ||
token::Interpolated(Rc::new(token::NtItem(item))) | ||
])} | ||
} | ||
|
||
pub fn token_stream_wrap(inner: TokenStream_) -> TokenStream { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we refactor away this function? Just using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What do you mean? You can't construct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, good point. |
||
TokenStream { | ||
inner: inner | ||
} | ||
} | ||
|
||
pub fn token_stream_parse_items(stream: TokenStream) -> Result<Vec<P<ast::Item>>, LexError> { | ||
with_parse_sess(move |sess| { | ||
let mut parser = parse::new_parser_from_ts(sess, stream.inner); | ||
let mut items = Vec::new(); | ||
|
||
while let Some(item) = try!(parser.parse_item().map_err(super::parse_to_lex_err)) { | ||
items.push(item) | ||
} | ||
|
||
Ok(items) | ||
}) | ||
} | ||
|
||
pub fn token_stream_items(stream: TokenStream) -> Vec<P<ast::Item>> { | ||
pub fn token_stream_inner(stream: TokenStream) -> TokenStream_ { | ||
stream.inner | ||
} | ||
|
||
|
@@ -96,6 +120,10 @@ pub mod __internal { | |
trait_name: &str, | ||
expand: fn(TokenStream) -> TokenStream, | ||
attributes: &[&'static str]); | ||
|
||
fn register_attr_proc_macro(&mut self, | ||
name: &str, | ||
expand: fn(TokenStream, TokenStream) -> TokenStream); | ||
} | ||
|
||
// Emulate scoped_thread_local!() here essentially | ||
|
@@ -125,11 +153,17 @@ pub mod __internal { | |
where F: FnOnce(&ParseSess) -> R | ||
{ | ||
let p = CURRENT_SESS.with(|p| p.get()); | ||
assert!(!p.is_null()); | ||
assert!(!p.is_null(), "proc_macro::__internal::with_parse_sess() called \ | ||
before set_parse_sess()!"); | ||
f(unsafe { &*p }) | ||
} | ||
} | ||
|
||
fn parse_to_lex_err(mut err: DiagnosticBuilder) -> LexError { | ||
err.cancel(); | ||
LexError { _inner: () } | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this belongs in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessarily, it's not used outside the crate. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I guess since this is also used outside |
||
|
||
#[stable(feature = "proc_macro_lib", since = "1.15.0")] | ||
impl FromStr for TokenStream { | ||
type Err = LexError; | ||
|
@@ -138,30 +172,17 @@ impl FromStr for TokenStream { | |
__internal::with_parse_sess(|sess| { | ||
let src = src.to_string(); | ||
let name = "<proc-macro source code>".to_string(); | ||
let mut parser = parse::new_parser_from_source_str(sess, name, src); | ||
let mut ret = TokenStream { inner: Vec::new() }; | ||
loop { | ||
match parser.parse_item() { | ||
Ok(Some(item)) => ret.inner.push(item), | ||
Ok(None) => return Ok(ret), | ||
Err(mut err) => { | ||
err.cancel(); | ||
return Err(LexError { _inner: () }) | ||
} | ||
} | ||
} | ||
let tts = try!(parse::parse_tts_from_source_str(name, src, sess) | ||
.map_err(parse_to_lex_err)); | ||
|
||
Ok(__internal::token_stream_wrap(TokenStream_::from_tts(tts))) | ||
}) | ||
} | ||
} | ||
|
||
#[stable(feature = "proc_macro_lib", since = "1.15.0")] | ||
impl fmt::Display for TokenStream { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
for item in self.inner.iter() { | ||
let item = syntax::print::pprust::item_to_string(item); | ||
try!(f.write_str(&item)); | ||
try!(f.write_str("\n")); | ||
} | ||
Ok(()) | ||
self.inner.fmt(f) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,7 @@ use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, Generics}; | |
use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind}; | ||
use syntax::ast::{Local, Mutability, Pat, PatKind, Path}; | ||
use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind}; | ||
use syntax::feature_gate::{emit_feature_err, GateIssue}; | ||
use syntax::feature_gate::{feature_err, emit_feature_err, GateIssue}; | ||
|
||
use syntax_pos::{Span, DUMMY_SP, MultiSpan}; | ||
use errors::DiagnosticBuilder; | ||
|
@@ -1123,6 +1123,12 @@ pub struct Resolver<'a> { | |
|
||
// Avoid duplicated errors for "name already defined". | ||
name_already_seen: FxHashMap<Name, Span>, | ||
|
||
// If `#![feature(proc_macro)]` is set | ||
proc_macro_enabled: bool, | ||
|
||
// A set of procedural macros imported by `#[macro_use]` that have already been warned about | ||
warned_proc_macros: FxHashSet<Name>, | ||
} | ||
|
||
pub struct ResolverArenas<'a> { | ||
|
@@ -1227,6 +1233,8 @@ impl<'a> Resolver<'a> { | |
invocations.insert(Mark::root(), | ||
arenas.alloc_invocation_data(InvocationData::root(graph_root))); | ||
|
||
let features = session.features.borrow(); | ||
|
||
Resolver { | ||
session: session, | ||
|
||
|
@@ -1284,7 +1292,9 @@ impl<'a> Resolver<'a> { | |
span: DUMMY_SP, | ||
vis: ty::Visibility::Public, | ||
}), | ||
use_extern_macros: session.features.borrow().use_extern_macros, | ||
|
||
// `#![feature(proc_macro)]` implies `#[feature(extern_macros)]` | ||
use_extern_macros: features.use_extern_macros || features.proc_macro, | ||
|
||
exported_macros: Vec::new(), | ||
crate_loader: crate_loader, | ||
|
@@ -1296,6 +1306,8 @@ impl<'a> Resolver<'a> { | |
invocations: invocations, | ||
name_already_seen: FxHashMap(), | ||
whitelisted_legacy_custom_derives: Vec::new(), | ||
proc_macro_enabled: features.proc_macro, | ||
warned_proc_macros: FxHashSet(), | ||
} | ||
} | ||
|
||
|
@@ -1525,6 +1537,8 @@ impl<'a> Resolver<'a> { | |
|
||
debug!("(resolving item) resolving {}", name); | ||
|
||
self.check_proc_macro_attrs(&item.attrs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we also need to call this for items in traits ( |
||
|
||
match item.node { | ||
ItemKind::Enum(_, ref generics) | | ||
ItemKind::Ty(_, ref generics) | | ||
|
@@ -1554,6 +1568,8 @@ impl<'a> Resolver<'a> { | |
walk_list!(this, visit_ty_param_bound, bounds); | ||
|
||
for trait_item in trait_items { | ||
this.check_proc_macro_attrs(&trait_item.attrs); | ||
|
||
match trait_item.node { | ||
TraitItemKind::Const(_, ref default) => { | ||
// Only impose the restrictions of | ||
|
@@ -1738,6 +1754,7 @@ impl<'a> Resolver<'a> { | |
this.with_self_rib(Def::SelfTy(trait_id, Some(item_def_id)), |this| { | ||
this.with_current_self_type(self_type, |this| { | ||
for impl_item in impl_items { | ||
this.check_proc_macro_attrs(&impl_item.attrs); | ||
this.resolve_visibility(&impl_item.vis); | ||
match impl_item.node { | ||
ImplItemKind::Const(..) => { | ||
|
@@ -3184,6 +3201,31 @@ impl<'a> Resolver<'a> { | |
let msg = "`self` no longer imports values".to_string(); | ||
self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg); | ||
} | ||
|
||
fn check_proc_macro_attrs(&mut self, attrs: &[ast::Attribute]) { | ||
if self.proc_macro_enabled { return; } | ||
|
||
for attr in attrs { | ||
let maybe_binding = self.builtin_macros.get(&attr.name()).cloned().or_else(|| { | ||
let ident = Ident::with_empty_ctxt(attr.name()); | ||
self.resolve_lexical_macro_path_segment(ident, MacroNS, None).ok() | ||
}); | ||
|
||
if let Some(binding) = maybe_binding { | ||
if let SyntaxExtension::AttrProcMacro(..) = *binding.get_macro(self) { | ||
attr::mark_known(attr); | ||
|
||
let msg = "attribute procedural macros are experimental"; | ||
let feature = "proc_macro"; | ||
|
||
feature_err(&self.session.parse_sess, feature, | ||
attr.span, GateIssue::Language, msg) | ||
.span_note(binding.span, "procedural macro imported here") | ||
.emit(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn is_struct_like(def: Def) -> bool { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the convention here usually
TokenStreamInner
rather than something ending with a_
?(I don't really know what I'm talking about, mostly wondering)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but since this is a private import of
tokenstream::TokenStream
(as opposed to a type specifically for this purpose) I don't think it's that big of a deal.