rust/src/libsyntax/ext/base.rs

820 lines
27 KiB
Rust
Raw Normal View History

// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub use self::SyntaxExtension::*;
use ast::{self, Attribute, Name, PatKind};
2016-06-12 12:45:16 +00:00
use attr::HasAttrs;
2016-09-02 22:01:35 +00:00
use codemap::{self, CodeMap, ExpnInfo, Spanned, respan};
use syntax_pos::{Span, ExpnId, NO_EXPANSION};
2015-12-21 10:00:43 +13:00
use errors::DiagnosticBuilder;
use ext::expand::{self, Invocation, Expansion};
use ext::hygiene::Mark;
2015-01-01 16:37:47 -08:00
use ext::tt::macro_rules;
use parse;
use parse::parser;
2013-03-26 16:38:07 -04:00
use parse::token;
use parse::token::{InternedString, str_to_ident};
2014-09-13 19:06:01 +03:00
use ptr::P;
2016-09-01 06:44:54 +00:00
use std_inject;
use util::small_vector::SmallVector;
use fold::Folder;
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
use feature_gate;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use tokenstream;
2015-01-28 08:34:18 -05:00
#[derive(Debug,Clone)]
pub enum Annotatable {
Item(P<ast::Item>),
TraitItem(P<ast::TraitItem>),
ImplItem(P<ast::ImplItem>),
}
2016-06-12 12:45:16 +00:00
impl HasAttrs for Annotatable {
fn attrs(&self) -> &[Attribute] {
match *self {
2016-06-12 12:45:16 +00:00
Annotatable::Item(ref item) => &item.attrs,
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
}
}
fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
match self {
2016-06-12 12:45:16 +00:00
Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)),
}
}
2016-06-12 12:45:16 +00:00
}
impl Annotatable {
pub fn expect_item(self) -> P<ast::Item> {
match self {
Annotatable::Item(i) => i,
_ => panic!("expected Item")
}
}
pub fn map_item_or<F, G>(self, mut f: F, mut or: G) -> Annotatable
where F: FnMut(P<ast::Item>) -> P<ast::Item>,
G: FnMut(Annotatable) -> Annotatable
{
match self {
Annotatable::Item(i) => Annotatable::Item(f(i)),
_ => or(self)
}
}
pub fn expect_trait_item(self) -> ast::TraitItem {
match self {
Annotatable::TraitItem(i) => i.unwrap(),
_ => panic!("expected Item")
}
}
pub fn expect_impl_item(self) -> ast::ImplItem {
match self {
Annotatable::ImplItem(i) => i.unwrap(),
_ => panic!("expected Item")
}
}
}
2015-01-16 22:59:41 +01:00
// A more flexible ItemDecorator.
pub trait MultiItemDecorator {
fn expand(&self,
ecx: &mut ExtCtxt,
sp: Span,
meta_item: &ast::MetaItem,
item: &Annotatable,
push: &mut FnMut(Annotatable));
2015-01-16 22:59:41 +01:00
}
impl<F> MultiItemDecorator for F
where F : Fn(&mut ExtCtxt, Span, &ast::MetaItem, &Annotatable, &mut FnMut(Annotatable))
2015-01-16 22:59:41 +01:00
{
fn expand(&self,
ecx: &mut ExtCtxt,
sp: Span,
meta_item: &ast::MetaItem,
item: &Annotatable,
push: &mut FnMut(Annotatable)) {
2015-01-16 22:59:41 +01:00
(*self)(ecx, sp, meta_item, item, push)
}
}
// `meta_item` is the annotation, and `item` is the item being modified.
// FIXME Decorators should follow the same pattern too.
pub trait MultiItemModifier {
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
-> Vec<Annotatable>;
}
impl<F, T> MultiItemModifier for F
where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T,
T: Into<Vec<Annotatable>>,
{
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
-> Vec<Annotatable> {
(*self)(ecx, span, meta_item, item).into()
}
}
impl Into<Vec<Annotatable>> for Annotatable {
fn into(self) -> Vec<Annotatable> {
vec![self]
}
}
/// Represents a thing that maps token trees to Macro Results
pub trait TTMacroExpander {
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
token_tree: &[tokenstream::TokenTree])
-> Box<MacResult+'cx>;
}
pub type MacroExpanderFn =
for<'cx> fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree])
-> Box<MacResult+'cx>;
impl<F> TTMacroExpander for F
where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree])
-> Box<MacResult+'cx>
{
fn expand<'cx>(&self,
ecx: &'cx mut ExtCtxt,
span: Span,
token_tree: &[tokenstream::TokenTree])
-> Box<MacResult+'cx> {
(*self)(ecx, span, token_tree)
}
}
2013-07-08 15:55:14 -07:00
pub trait IdentMacroExpander {
fn expand<'cx>(&self,
cx: &'cx mut ExtCtxt,
sp: Span,
ident: ast::Ident,
token_tree: Vec<tokenstream::TokenTree>,
attrs: Vec<ast::Attribute>)
-> Box<MacResult+'cx>;
}
pub type IdentMacroExpanderFn =
for<'cx> fn(&'cx mut ExtCtxt, Span, ast::Ident, Vec<tokenstream::TokenTree>)
-> Box<MacResult+'cx>;
impl<F> IdentMacroExpander for F
where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, ast::Ident,
Vec<tokenstream::TokenTree>) -> Box<MacResult+'cx>
{
fn expand<'cx>(&self,
cx: &'cx mut ExtCtxt,
sp: Span,
ident: ast::Ident,
token_tree: Vec<tokenstream::TokenTree>,
_attrs: Vec<ast::Attribute>)
-> Box<MacResult+'cx>
{
(*self)(cx, sp, ident, token_tree)
}
}
// Use a macro because forwarding to a simple function has type system issues
macro_rules! make_stmts_default {
($me:expr) => {
2016-06-17 02:30:01 +00:00
$me.make_expr().map(|e| SmallVector::one(ast::Stmt {
id: ast::DUMMY_NODE_ID,
span: e.span,
node: ast::StmtKind::Expr(e),
}))
}
}
/// The result of a macro expansion. The return values of the various
/// methods are spliced into the AST at the callsite of the macro.
pub trait MacResult {
/// Create an expression.
2014-09-13 19:06:01 +03:00
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
None
}
/// Create zero or more items.
2014-09-13 19:06:01 +03:00
fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
None
}
/// Create zero or more impl items.
fn make_impl_items(self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> {
None
}
/// Create zero or more trait items.
fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
None
}
2014-05-19 13:32:51 -07:00
/// Create a pattern.
2014-09-13 19:06:01 +03:00
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
2014-05-19 13:32:51 -07:00
None
}
/// Create zero or more statements.
///
/// By default this attempts to create an expression statement,
/// returning None if that fails.
fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
make_stmts_default!(self)
}
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
None
}
}
2013-07-08 15:55:14 -07:00
macro_rules! make_MacEager {
( $( $fld:ident: $t:ty, )* ) => {
/// `MacResult` implementation for the common case where you've already
/// built each form of AST that you might return.
#[derive(Default)]
pub struct MacEager {
$(
pub $fld: Option<$t>,
)*
}
impl MacEager {
$(
pub fn $fld(v: $t) -> Box<MacResult> {
Box::new(MacEager {
$fld: Some(v),
..Default::default()
})
}
)*
}
}
}
make_MacEager! {
expr: P<ast::Expr>,
pat: P<ast::Pat>,
items: SmallVector<P<ast::Item>>,
impl_items: SmallVector<ast::ImplItem>,
trait_items: SmallVector<ast::TraitItem>,
stmts: SmallVector<ast::Stmt>,
ty: P<ast::Ty>,
2014-05-19 13:32:51 -07:00
}
impl MacResult for MacEager {
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
self.expr
2014-05-19 13:32:51 -07:00
}
fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
self.items
2014-05-19 13:32:51 -07:00
}
fn make_impl_items(self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> {
self.impl_items
}
fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
self.trait_items
}
fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
match self.stmts.as_ref().map_or(0, |s| s.len()) {
0 => make_stmts_default!(self),
_ => self.stmts,
}
}
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
if let Some(p) = self.pat {
return Some(p);
}
if let Some(e) = self.expr {
if let ast::ExprKind::Lit(_) = e.node {
return Some(P(ast::Pat {
id: ast::DUMMY_NODE_ID,
span: e.span,
2016-02-11 21:16:33 +03:00
node: PatKind::Lit(e),
}));
}
}
None
}
fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
self.ty
}
}
/// Fill-in macro expansion result, to allow compilation to continue
/// after hitting errors.
2015-03-30 09:38:59 -04:00
#[derive(Copy, Clone)]
pub struct DummyResult {
expr_only: bool,
span: Span
2012-07-06 14:29:50 -07:00
}
impl DummyResult {
/// Create a default MacResult that can be anything.
///
/// Use this as a return value after hitting any errors and
/// calling `span_err`.
pub fn any(sp: Span) -> Box<MacResult+'static> {
Box::new(DummyResult { expr_only: false, span: sp })
}
/// Create a default MacResult that can only be an expression.
///
/// Use this for macros that must expand to an expression, so even
/// if an error is encountered internally, the user will receive
/// an error that they also used it in the wrong place.
pub fn expr(sp: Span) -> Box<MacResult+'static> {
Box::new(DummyResult { expr_only: true, span: sp })
}
/// A plain dummy expression.
2014-09-13 19:06:01 +03:00
pub fn raw_expr(sp: Span) -> P<ast::Expr> {
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::Lit(P(codemap::respan(sp, ast::LitKind::Bool(false)))),
span: sp,
attrs: ast::ThinVec::new(),
2014-09-13 19:06:01 +03:00
})
}
2014-05-19 13:32:51 -07:00
/// A plain dummy pattern.
2014-09-13 19:06:01 +03:00
pub fn raw_pat(sp: Span) -> ast::Pat {
ast::Pat {
2014-05-19 13:32:51 -07:00
id: ast::DUMMY_NODE_ID,
2016-02-11 21:16:33 +03:00
node: PatKind::Wild,
2014-05-19 13:32:51 -07:00
span: sp,
}
}
pub fn raw_ty(sp: Span) -> P<ast::Ty> {
P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ast::TyKind::Infer,
span: sp
})
}
}
impl MacResult for DummyResult {
2014-09-13 19:06:01 +03:00
fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> {
Some(DummyResult::raw_expr(self.span))
}
2014-09-13 19:06:01 +03:00
fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> {
Some(P(DummyResult::raw_pat(self.span)))
2014-05-19 13:32:51 -07:00
}
2014-09-13 19:06:01 +03:00
fn make_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Item>>> {
// this code needs a comment... why not always just return the Some() ?
if self.expr_only {
None
} else {
Some(SmallVector::zero())
}
}
fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<ast::ImplItem>> {
if self.expr_only {
None
} else {
Some(SmallVector::zero())
}
}
fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVector<ast::TraitItem>> {
if self.expr_only {
None
} else {
Some(SmallVector::zero())
}
}
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<ast::Stmt>> {
2016-06-17 02:30:01 +00:00
Some(SmallVector::one(ast::Stmt {
id: ast::DUMMY_NODE_ID,
node: ast::StmtKind::Expr(DummyResult::raw_expr(self.span)),
span: self.span,
}))
}
fn make_ty(self: Box<DummyResult>) -> Option<P<ast::Ty>> {
Some(DummyResult::raw_ty(self.span))
}
}
/// An enum representing the different kinds of syntax extensions.
pub enum SyntaxExtension {
/// A syntax extension that is attached to an item and creates new items
/// based upon it.
///
2015-01-20 20:28:36 +01:00
/// `#[derive(...)]` is a `MultiItemDecorator`.
MultiDecorator(Box<MultiItemDecorator + 'static>),
/// A syntax extension that is attached to an item and modifies it
/// in-place. More flexible version than Modifier.
MultiModifier(Box<MultiItemModifier + 'static>),
/// A normal, function-like syntax extension.
///
/// `bytes!` is a `NormalTT`.
///
/// The `bool` dictates whether the contents of the macro can
/// directly use `#[unstable]` things (true == yes).
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool),
/// A function-like syntax extension that has an extra ident before
/// the block.
///
IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool),
}
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
pub trait Resolver {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
fn next_node_id(&mut self) -> ast::NodeId;
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion);
fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>);
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>>;
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
}
pub enum LoadedMacro {
Def(ast::MacroDef),
CustomDerive(String, Box<MultiItemModifier>),
}
pub struct DummyResolver;
impl Resolver for DummyResolver {
fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec<LoadedMacro> {
Vec::new()
}
fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID }
fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {}
fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {}
fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {}
fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option<Rc<SyntaxExtension>> { None }
}
#[derive(Clone)]
pub struct ModuleData {
pub mod_path: Vec<ast::Ident>,
pub directory: PathBuf,
}
#[derive(Clone)]
pub struct ExpansionData {
pub mark: Mark,
pub depth: usize,
pub backtrace: ExpnId,
pub module: Rc<ModuleData>,
pub in_block: bool,
}
2014-06-09 13:12:30 -07:00
/// One of these is made during expansion and incrementally updated as we go;
/// when a macro expansion occurs, the resulting nodes have the backtrace()
/// -> expn_info of their expansion context stored into their span.
2013-12-25 11:10:33 -07:00
pub struct ExtCtxt<'a> {
pub parse_sess: &'a parse::ParseSess,
pub cfg: ast::CrateConfig,
pub ecfg: expand::ExpansionConfig<'a>,
pub crate_root: Option<&'static str>,
pub resolver: &'a mut Resolver,
pub exported_macros: Vec<ast::MacroDef>,
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
pub derive_modes: HashMap<InternedString, Box<MultiItemModifier>>,
pub current_expansion: ExpansionData,
}
2013-03-15 15:24:24 -04:00
2013-12-25 11:10:33 -07:00
impl<'a> ExtCtxt<'a> {
2014-12-12 11:09:32 -05:00
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
ecfg: expand::ExpansionConfig<'a>,
resolver: &'a mut Resolver)
-> ExtCtxt<'a> {
2013-12-27 17:17:36 -07:00
ExtCtxt {
parse_sess: parse_sess,
cfg: cfg,
ecfg: ecfg,
crate_root: None,
exported_macros: Vec::new(),
resolver: resolver,
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
derive_modes: HashMap::new(),
current_expansion: ExpansionData {
mark: Mark::root(),
depth: 0,
backtrace: NO_EXPANSION,
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
in_block: false,
},
}
}
/// Returns a `Folder` for deeply expanding all macros in an AST node.
pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
expand::MacroExpander::new(self, false)
}
/// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node.
/// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified.
pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> {
expand::MacroExpander::new(self, true)
}
pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree])
-> parser::Parser<'a> {
parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg())
}
pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() }
2014-03-09 16:54:34 +02:00
pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }
pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() }
pub fn call_site(&self) -> Span {
self.codemap().with_expn_info(self.backtrace(), |ei| match ei {
Some(expn_info) => expn_info.call_site,
None => self.bug("missing top span")
})
}
pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace }
/// Returns span for the macro which originally caused the current expansion to happen.
///
/// Stops backtracing at include! boundary.
pub fn expansion_cause(&self) -> Span {
let mut expn_id = self.backtrace();
let mut last_macro = None;
loop {
if self.codemap().with_expn_info(expn_id, |info| {
info.map_or(None, |i| {
if i.callee.name().as_str() == "include" {
// Stop going up the backtrace once include! is encountered
return None;
}
expn_id = i.call_site.expn_id;
last_macro = Some(i.call_site);
return Some(());
})
}).is_none() {
break
}
}
last_macro.expect("missing expansion backtrace")
}
pub fn bt_push(&mut self, ei: ExpnInfo) {
if self.current_expansion.depth > self.ecfg.recursion_limit {
2015-11-24 07:23:53 +05:30
self.span_fatal(ei.call_site,
2015-01-07 11:58:31 -05:00
&format!("recursion limit reached while expanding the macro `{}`",
2015-11-24 07:23:53 +05:30
ei.callee.name()));
}
let mut call_site = ei.call_site;
call_site.expn_id = self.backtrace();
self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo {
call_site: call_site,
callee: ei.callee
});
}
2016-09-02 09:12:47 +00:00
pub fn bt_pop(&mut self) {}
2015-01-01 16:37:47 -08:00
pub fn insert_macro(&mut self, def: ast::MacroDef) {
if def.export {
self.exported_macros.push(def.clone());
}
2015-01-02 12:50:45 -08:00
if def.use_locally {
let ext = macro_rules::compile(self, &def);
self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext));
2015-01-02 12:50:45 -08:00
}
2015-01-01 16:37:47 -08:00
}
pub fn insert_custom_derive(&mut self, name: &str, ext: Box<MultiItemModifier>, sp: Span) {
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
if !self.ecfg.enable_rustc_macro() {
feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic,
"rustc_macro",
sp,
feature_gate::GateIssue::Language,
"loading custom derive macro crates \
is experimentally supported");
}
let name = token::intern_and_get_ident(name);
if self.derive_modes.insert(name.clone(), ext).is_some() {
self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name));
rustc: Implement custom derive (macros 1.1) This commit is an implementation of [RFC 1681] which adds support to the compiler for first-class user-define custom `#[derive]` modes with a far more stable API than plugins have today. [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md The main features added by this commit are: * A new `rustc-macro` crate-type. This crate type represents one which will provide custom `derive` implementations and perhaps eventually flower into the implementation of macros 2.0 as well. * A new `rustc_macro` crate in the standard distribution. This crate will provide the runtime interface between macro crates and the compiler. The API here is particularly conservative right now but has quite a bit of room to expand into any manner of APIs required by macro authors. * The ability to load new derive modes through the `#[macro_use]` annotations on other crates. All support added here is gated behind the `rustc_macro` feature gate, both for the library support (the `rustc_macro` crate) as well as the language features. There are a few minor differences from the implementation outlined in the RFC, such as the `rustc_macro` crate being available as a dylib and all symbols are `dlsym`'d directly instead of having a shim compiled. These should only affect the implementation, however, not the public interface. This commit also ended up touching a lot of code related to `#[derive]`, making a few notable changes: * Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't sure how to keep this behavior and *not* expose it to custom derive. * Derive attributes no longer have access to unstable features by default, they have to opt in on a granular level. * The `derive(Copy,Clone)` optimization is now done through another "obscure attribute" which is just intended to ferry along in the compiler that such an optimization is possible. The `derive(PartialEq,Eq)` optimization was also updated to do something similar. --- One part of this PR which needs to be improved before stabilizing are the errors and exact interfaces here. The error messages are relatively poor quality and there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]` not working by default. The custom attributes added by the compiler end up becoming unstable again when going through a custom impl. Hopefully though this is enough to start allowing experimentation on crates.io! syntax-[breaking-change]
2016-08-22 17:07:11 -07:00
}
}
2015-12-21 10:00:43 +13:00
pub fn struct_span_warn(&self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
self.parse_sess.span_diagnostic.struct_span_warn(sp, msg)
}
pub fn struct_span_err(&self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
}
pub fn struct_span_fatal(&self,
sp: Span,
msg: &str)
-> DiagnosticBuilder<'a> {
self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg)
}
/// Emit `msg` attached to `sp`, and stop compilation immediately.
///
2014-07-02 21:27:07 -04:00
/// `span_err` should be strongly preferred where-ever possible:
/// this should *only* be used when
/// - continuing has a high risk of flow-on errors (e.g. errors in
/// declaring a macro would cause all uses of that macro to
/// complain about "undefined macro"), or
/// - there is literally nothing else that can be done (however,
/// in most cases one can construct a dummy expression/item to
/// substitute; we never hit resolve/type-checking so the dummy
/// value doesn't have to match anything)
pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
panic!(self.parse_sess.span_diagnostic.span_fatal(sp, msg));
}
/// Emit `msg` attached to `sp`, without immediately stopping
/// compilation.
///
/// Compilation will be stopped in the near future (at the end of
/// the macro expansion phase).
pub fn span_err(&self, sp: Span, msg: &str) {
self.parse_sess.span_diagnostic.span_err(sp, msg);
}
pub fn span_warn(&self, sp: Span, msg: &str) {
self.parse_sess.span_diagnostic.span_warn(sp, msg);
}
pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
}
pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
self.parse_sess.span_diagnostic.span_bug(sp, msg);
}
pub fn bug(&self, msg: &str) -> ! {
self.parse_sess.span_diagnostic.bug(msg);
}
pub fn trace_macros(&self) -> bool {
self.ecfg.trace_mac
}
2013-12-28 22:35:38 -07:00
pub fn set_trace_macros(&mut self, x: bool) {
self.ecfg.trace_mac = x
}
2013-09-02 02:50:59 +02:00
pub fn ident_of(&self, st: &str) -> ast::Ident {
str_to_ident(st)
}
pub fn std_path(&self, components: &[&str]) -> Vec<ast::Ident> {
let mut v = Vec::new();
if let Some(s) = self.crate_root {
v.push(self.ident_of(s));
}
v.extend(components.iter().map(|s| self.ident_of(s)));
return v
}
2014-07-06 01:17:59 -07:00
pub fn name_of(&self, st: &str) -> ast::Name {
token::intern(st)
}
2016-09-01 06:44:54 +00:00
pub fn initialize(&mut self, user_exts: Vec<NamedSyntaxExtension>, krate: &ast::Crate) {
if std_inject::no_core(&krate) {
self.crate_root = None;
} else if std_inject::no_std(&krate) {
self.crate_root = Some("core");
} else {
self.crate_root = Some("std");
}
for (name, extension) in user_exts {
let ident = ast::Ident::with_empty_ctxt(name);
self.resolver.add_macro(Mark::root(), ident, Rc::new(extension));
2016-09-01 06:44:54 +00:00
}
let mut module = ModuleData {
2016-09-01 06:44:54 +00:00
mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)],
directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)),
};
module.directory.pop();
self.current_expansion.module = Rc::new(module);
2016-09-01 06:44:54 +00:00
}
}
/// Extract a string literal from the macro expanded version of `expr`,
/// emitting `err_msg` if `expr` is not a string literal. This does not stop
/// compilation on error, merely emits a non-fatal error and returns None.
2016-09-02 22:01:35 +00:00
pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
-> Option<Spanned<(InternedString, ast::StrStyle)>> {
// Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation.
let expr = expr.map(|mut expr| {
expr.span.expn_id = cx.backtrace();
expr
});
// we want to be able to handle e.g. concat("foo", "bar")
let expr = cx.expander().fold_expr(expr);
2012-08-06 12:34:08 -07:00
match expr.node {
ast::ExprKind::Lit(ref l) => match l.node {
2016-09-02 22:01:35 +00:00
ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))),
_ => cx.span_err(l.span, err_msg)
},
_ => cx.span_err(expr.span, err_msg)
}
None
}
2016-09-02 22:01:35 +00:00
pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
-> Option<(InternedString, ast::StrStyle)> {
expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node)
}
/// Non-fatally assert that `tts` is empty. Note that this function
/// returns even when `tts` is non-empty, macros that *need* to stop
/// compilation should call
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
/// done as rarely as possible).
pub fn check_zero_tts(cx: &ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree],
name: &str) {
if !tts.is_empty() {
cx.span_err(sp, &format!("{} takes no arguments", name));
}
}
/// Extract the string literal from the first token of `tts`. If this
/// is not a string literal, emit an error and return None.
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree],
name: &str)
-> Option<String> {
let mut p = cx.new_parser_from_tts(tts);
2014-11-02 23:10:09 -08:00
if p.token == token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
2014-11-02 23:10:09 -08:00
return None
}
2016-09-02 22:01:35 +00:00
let ret = panictry!(p.parse_expr());
if p.token != token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| {
2015-02-03 23:31:06 +01:00
s.to_string()
})
}
/// Extract comma-separated expressions from `tts`. If there is a
/// parsing error, emit a non-fatal error and return None.
pub fn get_exprs_from_tts(cx: &mut ExtCtxt,
sp: Span,
tts: &[tokenstream::TokenTree]) -> Option<Vec<P<ast::Expr>>> {
let mut p = cx.new_parser_from_tts(tts);
let mut es = Vec::new();
2014-10-27 19:22:52 +11:00
while p.token != token::Eof {
es.push(cx.expander().fold_expr(panictry!(p.parse_expr())));
if p.eat(&token::Comma) {
continue;
}
2014-10-27 19:22:52 +11:00
if p.token != token::Eof {
cx.span_err(sp, "expected token: `,`");
return None;
}
}
Some(es)
}