2014-02-12 06:43:23 +00:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-04 00:48:01 +00:00
|
|
|
// 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.
|
|
|
|
|
2016-08-04 00:55:05 +00:00
|
|
|
use ast::{Block, Crate, Ident, Mac_, PatKind};
|
2016-08-30 23:03:52 +00:00
|
|
|
use ast::{MacStmtStyle, StmtKind, ItemKind};
|
2012-12-23 22:41:37 +00:00
|
|
|
use ast;
|
2016-07-16 19:11:28 +00:00
|
|
|
use ext::hygiene::Mark;
|
2016-09-02 09:12:47 +00:00
|
|
|
use ext::placeholders::{self, placeholder, PlaceholderExpander};
|
2016-06-22 08:03:42 +00:00
|
|
|
use attr::{self, HasAttrs};
|
2016-08-29 05:32:41 +00:00
|
|
|
use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
2016-06-21 22:08:13 +00:00
|
|
|
use syntax_pos::{self, Span, ExpnId};
|
2016-05-16 10:09:23 +00:00
|
|
|
use config::StripUnconfigured;
|
2012-09-04 18:37:29 +00:00
|
|
|
use ext::base::*;
|
2015-12-10 14:23:14 +00:00
|
|
|
use feature_gate::{self, Features};
|
2014-07-03 06:16:01 +00:00
|
|
|
use fold;
|
2012-12-23 22:41:37 +00:00
|
|
|
use fold::*;
|
2016-06-22 08:03:42 +00:00
|
|
|
use parse::token::{intern, keywords};
|
2014-09-13 16:06:01 +00:00
|
|
|
use ptr::P;
|
2016-06-20 15:49:33 +00:00
|
|
|
use tokenstream::TokenTree;
|
2014-09-13 16:06:01 +00:00
|
|
|
use util::small_vector::SmallVector;
|
2011-08-05 20:06:11 +00:00
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::mem;
|
2016-08-31 09:02:45 +00:00
|
|
|
use std::path::PathBuf;
|
2016-09-01 06:44:54 +00:00
|
|
|
use std::rc::Rc;
|
2016-08-31 09:02:45 +00:00
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
macro_rules! expansions {
|
2016-09-02 09:12:47 +00:00
|
|
|
($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident,
|
2016-09-02 02:20:03 +00:00
|
|
|
$(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => {
|
2016-08-27 05:27:59 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2016-08-29 05:32:41 +00:00
|
|
|
pub enum ExpansionKind { OptExpr, $( $kind, )* }
|
|
|
|
pub enum Expansion { OptExpr(Option<P<ast::Expr>>), $( $kind($ty), )* }
|
2016-08-27 05:27:59 +00:00
|
|
|
|
|
|
|
impl ExpansionKind {
|
|
|
|
fn name(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
ExpansionKind::OptExpr => "expression",
|
|
|
|
$( ExpansionKind::$kind => $kind_name, )*
|
|
|
|
}
|
|
|
|
}
|
2016-05-24 06:12:54 +00:00
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
fn make_from<'a>(self, result: Box<MacResult + 'a>) -> Option<Expansion> {
|
|
|
|
match self {
|
|
|
|
ExpansionKind::OptExpr => result.make_expr().map(Some).map(Expansion::OptExpr),
|
|
|
|
$( ExpansionKind::$kind => result.$make().map(Expansion::$kind), )*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-17 11:02:42 +00:00
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
impl Expansion {
|
2016-08-29 05:32:41 +00:00
|
|
|
pub fn make_opt_expr(self) -> Option<P<ast::Expr>> {
|
2016-08-27 05:27:59 +00:00
|
|
|
match self {
|
|
|
|
Expansion::OptExpr(expr) => expr,
|
|
|
|
_ => panic!("Expansion::make_* called on the wrong kind of expansion"),
|
|
|
|
}
|
|
|
|
}
|
2016-08-29 05:32:41 +00:00
|
|
|
$( pub fn $make(self) -> $ty {
|
2016-08-27 05:27:59 +00:00
|
|
|
match self {
|
|
|
|
Expansion::$kind(ast) => ast,
|
|
|
|
_ => panic!("Expansion::make_* called on the wrong kind of expansion"),
|
|
|
|
}
|
|
|
|
} )*
|
2016-05-19 09:45:37 +00:00
|
|
|
|
2016-08-29 05:32:41 +00:00
|
|
|
pub fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
|
2016-08-27 05:27:59 +00:00
|
|
|
use self::Expansion::*;
|
|
|
|
match self {
|
|
|
|
OptExpr(expr) => OptExpr(expr.and_then(|expr| folder.fold_opt_expr(expr))),
|
|
|
|
$($( $kind(ast) => $kind(folder.$fold(ast)), )*)*
|
|
|
|
$($( $kind(ast) => {
|
|
|
|
$kind(ast.into_iter().flat_map(|ast| folder.$fold_elt(ast)).collect())
|
|
|
|
}, )*)*
|
|
|
|
}
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-02 09:12:47 +00:00
|
|
|
|
|
|
|
impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
|
|
|
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
|
|
|
self.expand(Expansion::OptExpr(Some(expr))).make_opt_expr()
|
|
|
|
}
|
|
|
|
$($(fn $fold(&mut self, node: $ty) -> $ty {
|
|
|
|
self.expand(Expansion::$kind(node)).$make()
|
|
|
|
})*)*
|
|
|
|
$($(fn $fold_elt(&mut self, node: $ty_elt) -> $ty {
|
|
|
|
self.expand(Expansion::$kind(SmallVector::one(node))).$make()
|
|
|
|
})*)*
|
|
|
|
}
|
2016-08-27 05:27:59 +00:00
|
|
|
}
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
expansions! {
|
2016-09-02 02:20:03 +00:00
|
|
|
Expr: P<ast::Expr> [], "expression", .make_expr, .fold_expr;
|
|
|
|
Pat: P<ast::Pat> [], "pattern", .make_pat, .fold_pat;
|
|
|
|
Ty: P<ast::Ty> [], "type", .make_ty, .fold_ty;
|
2016-09-02 09:12:47 +00:00
|
|
|
Stmts: SmallVector<ast::Stmt> [SmallVector, ast::Stmt],
|
2016-09-02 02:20:03 +00:00
|
|
|
"statement", .make_stmts, lift .fold_stmt;
|
2016-09-02 09:12:47 +00:00
|
|
|
Items: SmallVector<P<ast::Item>> [SmallVector, P<ast::Item>],
|
2016-09-02 02:20:03 +00:00
|
|
|
"item", .make_items, lift .fold_item;
|
2016-09-02 09:12:47 +00:00
|
|
|
TraitItems: SmallVector<ast::TraitItem> [SmallVector, ast::TraitItem],
|
2016-09-02 02:20:03 +00:00
|
|
|
"trait item", .make_trait_items, lift .fold_trait_item;
|
2016-09-02 09:12:47 +00:00
|
|
|
ImplItems: SmallVector<ast::ImplItem> [SmallVector, ast::ImplItem],
|
2016-09-02 02:20:03 +00:00
|
|
|
"impl item", .make_impl_items, lift .fold_impl_item;
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
impl ExpansionKind {
|
|
|
|
fn dummy(self, span: Span) -> Expansion {
|
|
|
|
self.make_from(DummyResult::any(span)).unwrap()
|
2016-06-11 22:59:33 +00:00
|
|
|
}
|
2016-09-02 06:14:38 +00:00
|
|
|
|
|
|
|
fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(self, items: I) -> Expansion {
|
|
|
|
let items = items.into_iter();
|
|
|
|
match self {
|
|
|
|
ExpansionKind::Items =>
|
|
|
|
Expansion::Items(items.map(Annotatable::expect_item).collect()),
|
|
|
|
ExpansionKind::ImplItems =>
|
|
|
|
Expansion::ImplItems(items.map(Annotatable::expect_impl_item).collect()),
|
|
|
|
ExpansionKind::TraitItems =>
|
|
|
|
Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()),
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2016-05-16 10:09:23 +00:00
|
|
|
}
|
2015-01-27 00:22:12 +00:00
|
|
|
|
2016-08-27 05:27:59 +00:00
|
|
|
pub struct Invocation {
|
2016-09-02 06:14:38 +00:00
|
|
|
kind: InvocationKind,
|
|
|
|
expansion_kind: ExpansionKind,
|
2016-08-27 05:27:59 +00:00
|
|
|
mark: Mark,
|
2016-09-02 09:12:47 +00:00
|
|
|
module: Module,
|
|
|
|
backtrace: ExpnId,
|
|
|
|
depth: usize,
|
2016-09-02 06:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum InvocationKind {
|
|
|
|
Bang {
|
|
|
|
attrs: Vec<ast::Attribute>,
|
|
|
|
mac: ast::Mac,
|
|
|
|
ident: Option<Ident>,
|
|
|
|
span: Span,
|
|
|
|
},
|
|
|
|
Attr {
|
|
|
|
attr: ast::Attribute,
|
|
|
|
item: Annotatable,
|
|
|
|
},
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
2013-07-30 00:25:00 +00:00
|
|
|
|
2014-08-28 01:46:52 +00:00
|
|
|
pub struct MacroExpander<'a, 'b:'a> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub cx: &'a mut ExtCtxt<'b>,
|
2016-08-04 00:55:05 +00:00
|
|
|
pub single_step: bool,
|
|
|
|
pub keep_macs: bool,
|
2014-12-14 02:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> MacroExpander<'a, 'b> {
|
2016-08-04 00:55:05 +00:00
|
|
|
pub fn new(cx: &'a mut ExtCtxt<'b>,
|
|
|
|
single_step: bool,
|
|
|
|
keep_macs: bool) -> MacroExpander<'a, 'b> {
|
|
|
|
MacroExpander {
|
|
|
|
cx: cx,
|
|
|
|
single_step: single_step,
|
|
|
|
keep_macs: keep_macs
|
|
|
|
}
|
2014-12-14 02:42:41 +00:00
|
|
|
}
|
2016-05-16 10:09:23 +00:00
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
|
|
|
|
let err_count = self.cx.parse_sess.span_diagnostic.err_count();
|
|
|
|
|
|
|
|
let items = Expansion::Items(SmallVector::many(krate.module.items));
|
|
|
|
krate.module.items = self.expand(items).make_items().into();
|
|
|
|
krate.exported_macros = self.cx.exported_macros.clone();
|
|
|
|
|
|
|
|
if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
|
|
|
|
self.cx.parse_sess.span_diagnostic.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
|
|
|
krate
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fully expand all the invocations in `expansion`.
|
|
|
|
fn expand(&mut self, expansion: Expansion) -> Expansion {
|
|
|
|
let (expansion, mut invocations) = self.collect_invocations(expansion);
|
|
|
|
invocations.reverse();
|
|
|
|
|
|
|
|
let mut expansions = HashMap::new();
|
|
|
|
while let Some(invoc) = invocations.pop() {
|
|
|
|
let Invocation { mark, module, depth, backtrace, .. } = invoc;
|
|
|
|
self.cx.syntax_env.current_module = module;
|
|
|
|
self.cx.recursion_count = depth;
|
|
|
|
self.cx.backtrace = backtrace;
|
|
|
|
|
|
|
|
let expansion = self.expand_invoc(invoc);
|
|
|
|
|
|
|
|
self.cx.syntax_env.current_module = module;
|
|
|
|
self.cx.recursion_count = depth + 1;
|
|
|
|
let (expansion, new_invocations) = self.collect_invocations(expansion);
|
|
|
|
|
|
|
|
expansions.insert(mark.as_u32(), expansion);
|
|
|
|
if !self.single_step {
|
|
|
|
invocations.extend(new_invocations.into_iter().rev());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
expansion.fold_with(&mut PlaceholderExpander::new(expansions))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec<Invocation>) {
|
2016-09-07 22:24:01 +00:00
|
|
|
let crate_config = mem::replace(&mut self.cx.cfg, Vec::new());
|
|
|
|
let result = {
|
|
|
|
let mut collector = InvocationCollector {
|
|
|
|
cfg: StripUnconfigured {
|
|
|
|
config: &crate_config,
|
|
|
|
should_test: self.cx.ecfg.should_test,
|
|
|
|
sess: self.cx.parse_sess,
|
|
|
|
features: self.cx.ecfg.features,
|
|
|
|
},
|
|
|
|
cx: self.cx,
|
|
|
|
invocations: Vec::new(),
|
|
|
|
};
|
|
|
|
(expansion.fold_with(&mut collector), collector.invocations)
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cx.cfg = crate_config;
|
|
|
|
result
|
2016-05-16 10:09:23 +00:00
|
|
|
}
|
2016-06-12 01:50:52 +00:00
|
|
|
|
2016-09-01 07:01:45 +00:00
|
|
|
fn expand_invoc(&mut self, invoc: Invocation) -> Expansion {
|
|
|
|
match invoc.kind {
|
|
|
|
InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc),
|
|
|
|
InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion {
|
|
|
|
let Invocation { expansion_kind: kind, .. } = invoc;
|
|
|
|
let (attr, item) = match invoc.kind {
|
|
|
|
InvocationKind::Attr { attr, item } => (attr, item),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let extension = match self.cx.syntax_env.find(intern(&attr.name())) {
|
|
|
|
Some(extension) => extension,
|
|
|
|
None => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
attr::mark_used(&attr);
|
|
|
|
self.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroAttribute(intern(&attr.name())),
|
|
|
|
span: Some(attr.span),
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
match *extension {
|
2016-09-01 07:01:45 +00:00
|
|
|
MultiModifier(ref mac) => {
|
|
|
|
let item = mac.expand(self.cx, attr.span, &attr.node.value, item);
|
|
|
|
kind.expect_from_annotatables(item)
|
|
|
|
}
|
|
|
|
MultiDecorator(ref mac) => {
|
|
|
|
let mut items = Vec::new();
|
|
|
|
mac.expand(self.cx, attr.span, &attr.node.value, &item,
|
|
|
|
&mut |item| items.push(item));
|
|
|
|
items.push(item);
|
|
|
|
kind.expect_from_annotatables(items)
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
2016-09-02 09:12:47 +00:00
|
|
|
}
|
2016-09-01 07:01:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Expand a macro invocation. Returns the result of expansion.
|
|
|
|
fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion {
|
|
|
|
let Invocation { mark, expansion_kind: kind, .. } = invoc;
|
|
|
|
let (attrs, mac, ident, span) = match invoc.kind {
|
|
|
|
InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let Mac_ { path, tts, .. } = mac.node;
|
|
|
|
|
|
|
|
// Detect use of feature-gated or invalid attributes on macro invoations
|
|
|
|
// since they will not be detected after macro expansion.
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
feature_gate::check_attribute(&attr, &self.cx.parse_sess.span_diagnostic,
|
|
|
|
&self.cx.parse_sess.codemap(),
|
|
|
|
&self.cx.ecfg.features.unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
|
|
|
|
self.cx.span_err(path.span, "expected macro name without module separators");
|
|
|
|
return kind.dummy(span);
|
|
|
|
}
|
|
|
|
|
|
|
|
let extname = path.segments[0].identifier.name;
|
|
|
|
let extension = if let Some(extension) = self.cx.syntax_env.find(extname) {
|
|
|
|
extension
|
|
|
|
} else {
|
|
|
|
let mut err =
|
|
|
|
self.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname));
|
|
|
|
self.cx.suggest_macro_name(&extname.as_str(), &mut err);
|
|
|
|
err.emit();
|
|
|
|
return kind.dummy(span);
|
|
|
|
};
|
|
|
|
|
|
|
|
let ident = ident.unwrap_or(keywords::Invalid.ident());
|
|
|
|
let marked_tts = mark_tts(&tts, mark);
|
|
|
|
let opt_expanded = match *extension {
|
|
|
|
NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
|
|
|
|
if ident.name != keywords::Invalid.name() {
|
|
|
|
let msg =
|
|
|
|
format!("macro {}! expects no ident argument, given '{}'", extname, ident);
|
|
|
|
self.cx.span_err(path.span, &msg);
|
|
|
|
return kind.dummy(span);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: exp_span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
kind.make_from(expandfun.expand(self.cx, span, &marked_tts))
|
|
|
|
}
|
|
|
|
|
|
|
|
IdentTT(ref expander, tt_span, allow_internal_unstable) => {
|
|
|
|
if ident.name == keywords::Invalid.name() {
|
|
|
|
self.cx.span_err(path.span,
|
|
|
|
&format!("macro {}! expects an ident argument", extname));
|
|
|
|
return kind.dummy(span);
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: tt_span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
kind.make_from(expander.expand(self.cx, span, ident, marked_tts))
|
|
|
|
}
|
|
|
|
|
|
|
|
MacroRulesTT => {
|
|
|
|
if ident.name == keywords::Invalid.name() {
|
|
|
|
self.cx.span_err(path.span,
|
|
|
|
&format!("macro {}! expects an ident argument", extname));
|
|
|
|
return kind.dummy(span);
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: None,
|
|
|
|
// `macro_rules!` doesn't directly allow unstable
|
|
|
|
// (this is orthogonal to whether the macro it creates allows it)
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let def = ast::MacroDef {
|
|
|
|
ident: ident,
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
span: span,
|
|
|
|
imported_from: None,
|
|
|
|
use_locally: true,
|
|
|
|
body: marked_tts,
|
|
|
|
export: attr::contains_name(&attrs, "macro_export"),
|
|
|
|
allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
|
|
|
|
attrs: attrs,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cx.insert_macro(def.clone());
|
|
|
|
|
|
|
|
// If keep_macs is true, expands to a MacEager::items instead.
|
|
|
|
if self.keep_macs {
|
2016-08-29 05:32:41 +00:00
|
|
|
Some(placeholders::reconstructed_macro_rules(&def, &path))
|
2016-09-01 07:01:45 +00:00
|
|
|
} else {
|
2016-08-29 05:32:41 +00:00
|
|
|
Some(placeholders::macro_scope_placeholder())
|
2016-09-01 07:01:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MultiDecorator(..) | MultiModifier(..) => {
|
|
|
|
self.cx.span_err(path.span,
|
|
|
|
&format!("`{}` can only be used in attributes", extname));
|
|
|
|
return kind.dummy(span);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let expanded = if let Some(expanded) = opt_expanded {
|
|
|
|
expanded
|
|
|
|
} else {
|
|
|
|
let msg = format!("non-{kind} macro in {kind} position: {name}",
|
|
|
|
name = path.segments[0].identifier.name, kind = kind.name());
|
|
|
|
self.cx.span_err(path.span, &msg);
|
|
|
|
return kind.dummy(span);
|
|
|
|
};
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
expanded.fold_with(&mut Marker {
|
|
|
|
mark: mark,
|
|
|
|
expn_id: Some(self.cx.backtrace()),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct InvocationCollector<'a, 'b: 'a> {
|
|
|
|
cx: &'a mut ExtCtxt<'b>,
|
2016-09-07 22:24:01 +00:00
|
|
|
cfg: StripUnconfigured<'a>,
|
2016-09-02 09:12:47 +00:00
|
|
|
invocations: Vec<Invocation>,
|
|
|
|
}
|
|
|
|
|
2016-09-07 22:24:01 +00:00
|
|
|
macro_rules! fully_configure {
|
|
|
|
($this:ident, $node:ident, $noop_fold:ident) => {
|
|
|
|
match $noop_fold($node, &mut $this.cfg).pop() {
|
|
|
|
Some(node) => node,
|
|
|
|
None => return SmallVector::zero(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|
|
|
fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion {
|
|
|
|
let mark = Mark::fresh();
|
|
|
|
self.invocations.push(Invocation {
|
|
|
|
kind: kind,
|
|
|
|
expansion_kind: expansion_kind,
|
2016-09-01 07:01:45 +00:00
|
|
|
mark: mark,
|
2016-09-02 09:12:47 +00:00
|
|
|
module: self.cx.syntax_env.current_module,
|
|
|
|
backtrace: self.cx.backtrace,
|
|
|
|
depth: self.cx.recursion_count,
|
2016-09-01 07:01:45 +00:00
|
|
|
});
|
2016-09-02 09:12:47 +00:00
|
|
|
placeholder(expansion_kind, mark.as_u32())
|
|
|
|
}
|
2016-09-01 07:01:45 +00:00
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
fn collect_bang(
|
|
|
|
&mut self, mac: ast::Mac, attrs: Vec<ast::Attribute>, span: Span, kind: ExpansionKind,
|
|
|
|
) -> Expansion {
|
|
|
|
self.collect(kind, InvocationKind::Bang { attrs: attrs, mac: mac, ident: None, span: span })
|
|
|
|
}
|
2016-09-01 07:01:45 +00:00
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind)
|
|
|
|
-> Expansion {
|
|
|
|
self.collect(kind, InvocationKind::Attr { attr: attr, item: item })
|
|
|
|
}
|
|
|
|
|
|
|
|
// If `item` is an attr invocation, remove and return the macro attribute.
|
|
|
|
fn classify_item<T: HasAttrs>(&self, mut item: T) -> (T, Option<ast::Attribute>) {
|
|
|
|
let mut attr = None;
|
|
|
|
item = item.map_attrs(|mut attrs| {
|
|
|
|
for i in 0..attrs.len() {
|
|
|
|
if let Some(extension) = self.cx.syntax_env.find(intern(&attrs[i].name())) {
|
|
|
|
match *extension {
|
|
|
|
MultiModifier(..) | MultiDecorator(..) => {
|
|
|
|
attr = Some(attrs.remove(i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attrs
|
|
|
|
});
|
|
|
|
(item, attr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// does this attribute list contain "macro_use" ?
|
|
|
|
fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
|
|
|
|
for attr in attrs {
|
|
|
|
let mut is_use = attr.check_name("macro_use");
|
|
|
|
if attr.check_name("macro_escape") {
|
|
|
|
let msg = "macro_escape is a deprecated synonym for macro_use";
|
|
|
|
let mut err = self.cx.struct_span_warn(attr.span, msg);
|
|
|
|
is_use = true;
|
|
|
|
if let ast::AttrStyle::Inner = attr.node.style {
|
|
|
|
err.help("consider an outer attribute, #[macro_use] mod ...").emit();
|
|
|
|
} else {
|
|
|
|
err.emit();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if is_use {
|
|
|
|
if !attr.is_word() {
|
|
|
|
self.cx.span_err(attr.span, "arguments to macro_use are not allowed here");
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
2016-09-01 07:01:45 +00:00
|
|
|
}
|
2016-09-07 22:24:01 +00:00
|
|
|
|
|
|
|
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
|
|
|
|
self.cfg.configure(node)
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let mut expr = self.cfg.configure_expr(expr).unwrap();
|
|
|
|
expr.node = self.cfg.configure_expr_kind(expr.node);
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
if let ast::ExprKind::Mac(mac) = expr.node {
|
2016-09-02 09:12:47 +00:00
|
|
|
self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr).make_expr()
|
2016-08-30 23:03:52 +00:00
|
|
|
} else {
|
|
|
|
P(noop_fold_expr(expr, self))
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 10:09:23 +00:00
|
|
|
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let mut expr = configure!(self, expr).unwrap();
|
|
|
|
expr.node = self.cfg.configure_expr_kind(expr.node);
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
if let ast::ExprKind::Mac(mac) = expr.node {
|
2016-09-02 09:12:47 +00:00
|
|
|
self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr)
|
|
|
|
.make_opt_expr()
|
2016-08-30 23:03:52 +00:00
|
|
|
} else {
|
|
|
|
Some(P(noop_fold_expr(expr, self)))
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
|
2016-08-30 23:03:52 +00:00
|
|
|
match pat.node {
|
|
|
|
PatKind::Mac(_) => {}
|
2016-09-01 07:01:45 +00:00
|
|
|
_ => return noop_fold_pat(pat, self),
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
2014-05-19 20:59:35 +00:00
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
pat.and_then(|pat| match pat.node {
|
2016-09-02 09:12:47 +00:00
|
|
|
PatKind::Mac(mac) =>
|
|
|
|
self.collect_bang(mac, Vec::new(), pat.span, ExpansionKind::Pat).make_pat(),
|
2016-08-30 23:03:52 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
})
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:33:09 +00:00
|
|
|
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let stmt = match self.cfg.configure_stmt(stmt) {
|
|
|
|
Some(stmt) => stmt,
|
|
|
|
None => return SmallVector::zero(),
|
|
|
|
};
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
let (mac, style, attrs) = match stmt.node {
|
|
|
|
StmtKind::Mac(mac) => mac.unwrap(),
|
2016-09-01 07:01:45 +00:00
|
|
|
_ => return noop_fold_stmt(stmt, self),
|
2016-08-30 23:03:52 +00:00
|
|
|
};
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
let mut placeholder =
|
|
|
|
self.collect_bang(mac, attrs.into(), stmt.span, ExpansionKind::Stmts).make_stmts();
|
2016-08-30 23:03:52 +00:00
|
|
|
|
|
|
|
// If this is a macro invocation with a semicolon, then apply that
|
|
|
|
// semicolon to the final statement produced by expansion.
|
|
|
|
if style == MacStmtStyle::Semicolon {
|
2016-09-02 09:12:47 +00:00
|
|
|
if let Some(stmt) = placeholder.pop() {
|
|
|
|
placeholder.push(stmt.add_trailing_semicolon());
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-02 09:12:47 +00:00
|
|
|
placeholder
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn fold_block(&mut self, block: P<Block>) -> P<Block> {
|
2016-09-01 06:44:54 +00:00
|
|
|
let paths = self.cx.syntax_env.paths();
|
|
|
|
let module = self.cx.syntax_env.add_module(false, true, paths);
|
2016-09-02 09:12:47 +00:00
|
|
|
let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module);
|
2016-09-01 06:44:54 +00:00
|
|
|
let result = noop_fold_block(block, self);
|
2016-09-02 09:12:47 +00:00
|
|
|
self.cx.syntax_env.current_module = orig_module;
|
2016-03-01 09:28:42 +00:00
|
|
|
result
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item = configure!(self, item);
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
let (item, attr) = self.classify_item(item);
|
|
|
|
if let Some(attr) = attr {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item = Annotatable::Item(fully_configure!(self, item, noop_fold_item));
|
2016-09-02 09:12:47 +00:00
|
|
|
return self.collect_attr(attr, item, ExpansionKind::Items).make_items();
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match item.node {
|
|
|
|
ast::ItemKind::Mac(..) => {
|
|
|
|
if match item.node {
|
|
|
|
ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
} {
|
|
|
|
return SmallVector::one(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
item.and_then(|item| match item.node {
|
|
|
|
ItemKind::Mac(mac) => {
|
2016-09-02 09:12:47 +00:00
|
|
|
self.collect(ExpansionKind::Items, InvocationKind::Bang {
|
2016-08-30 23:03:52 +00:00
|
|
|
mac: mac,
|
|
|
|
attrs: item.attrs,
|
|
|
|
ident: Some(item.ident),
|
|
|
|
span: item.span,
|
2016-09-02 09:12:47 +00:00
|
|
|
}).make_items()
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
ast::ItemKind::Mod(ast::Mod { inner, .. }) => {
|
2016-09-01 06:44:54 +00:00
|
|
|
let mut paths = (*self.cx.syntax_env.paths()).clone();
|
|
|
|
paths.mod_path.push(item.ident);
|
2016-08-30 23:03:52 +00:00
|
|
|
if item.span.contains(inner) {
|
2016-09-01 06:44:54 +00:00
|
|
|
paths.directory.push(&*{
|
2016-08-30 23:03:52 +00:00
|
|
|
::attr::first_attr_value_str_by_name(&item.attrs, "path")
|
|
|
|
.unwrap_or(item.ident.name.as_str())
|
|
|
|
});
|
|
|
|
} else {
|
2016-09-01 06:44:54 +00:00
|
|
|
paths.directory = match inner {
|
2016-08-30 23:03:52 +00:00
|
|
|
syntax_pos::DUMMY_SP => PathBuf::new(),
|
|
|
|
_ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)),
|
|
|
|
};
|
2016-09-01 06:44:54 +00:00
|
|
|
paths.directory.pop();
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 06:44:54 +00:00
|
|
|
let macro_use = self.contains_macro_use(&item.attrs);
|
|
|
|
let in_block = self.cx.syntax_env.in_block();
|
|
|
|
let module = self.cx.syntax_env.add_module(macro_use, in_block, Rc::new(paths));
|
2016-09-02 09:12:47 +00:00
|
|
|
let module = mem::replace(&mut self.cx.syntax_env.current_module, module);
|
2016-09-01 06:44:54 +00:00
|
|
|
let result = noop_fold_item(item, self);
|
2016-09-02 09:12:47 +00:00
|
|
|
self.cx.syntax_env.current_module = module;
|
2016-08-30 23:03:52 +00:00
|
|
|
result
|
|
|
|
},
|
2016-09-02 02:20:03 +00:00
|
|
|
ast::ItemKind::ExternCrate(..) => {
|
|
|
|
// We need to error on `#[macro_use] extern crate` when it isn't at the
|
|
|
|
// crate root, because `$crate` won't work properly.
|
|
|
|
let is_crate_root = self.cx.syntax_env.is_crate_root();
|
|
|
|
for def in self.cx.loader.load_crate(&*item, is_crate_root) {
|
|
|
|
match def {
|
|
|
|
LoadedMacro::Def(def) => self.cx.insert_macro(def),
|
|
|
|
LoadedMacro::CustomDerive(name, ext) => {
|
|
|
|
self.cx.insert_custom_derive(&name, ext, item.span);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SmallVector::one(item)
|
|
|
|
},
|
2016-08-30 23:03:52 +00:00
|
|
|
_ => noop_fold_item(item, self),
|
|
|
|
}
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item = configure!(self, item);
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
let (item, attr) = self.classify_item(item);
|
|
|
|
if let Some(attr) = attr {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item =
|
|
|
|
Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item)));
|
2016-09-02 09:12:47 +00:00
|
|
|
return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items()
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match item.node {
|
|
|
|
ast::TraitItemKind::Macro(mac) => {
|
|
|
|
let ast::TraitItem { attrs, span, .. } = item;
|
2016-09-02 09:12:47 +00:00
|
|
|
self.collect_bang(mac, attrs, span, ExpansionKind::TraitItems).make_trait_items()
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
_ => fold::noop_fold_trait_item(item, self),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item = configure!(self, item);
|
|
|
|
|
2016-08-30 23:03:52 +00:00
|
|
|
let (item, attr) = self.classify_item(item);
|
|
|
|
if let Some(attr) = attr {
|
2016-09-07 22:24:01 +00:00
|
|
|
let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item)));
|
2016-09-02 09:12:47 +00:00
|
|
|
return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items();
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match item.node {
|
|
|
|
ast::ImplItemKind::Macro(mac) => {
|
|
|
|
let ast::ImplItem { attrs, span, .. } = item;
|
2016-09-02 09:12:47 +00:00
|
|
|
self.collect_bang(mac, attrs, span, ExpansionKind::ImplItems).make_impl_items()
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
2016-09-01 07:01:45 +00:00
|
|
|
_ => fold::noop_fold_impl_item(item, self),
|
2016-08-30 23:03:52 +00:00
|
|
|
}
|
2014-07-04 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2015-07-26 04:54:19 +00:00
|
|
|
fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
|
2016-08-30 23:03:52 +00:00
|
|
|
let ty = match ty.node {
|
|
|
|
ast::TyKind::Mac(_) => ty.unwrap(),
|
|
|
|
_ => return fold::noop_fold_ty(ty, self),
|
|
|
|
};
|
|
|
|
|
|
|
|
match ty.node {
|
2016-09-02 09:12:47 +00:00
|
|
|
ast::TyKind::Mac(mac) =>
|
|
|
|
self.collect_bang(mac, Vec::new(), ty.span, ExpansionKind::Ty).make_ty(),
|
2016-08-30 23:03:52 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2015-07-26 04:54:19 +00:00
|
|
|
}
|
2016-09-07 22:24:01 +00:00
|
|
|
|
|
|
|
fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
|
|
|
|
noop_fold_foreign_mod(self.cfg.configure_foreign_mod(foreign_mod), self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
|
|
|
|
noop_fold_item_kind(self.cfg.configure_item_kind(item), self)
|
|
|
|
}
|
2013-07-16 05:05:50 +00:00
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
pub struct ExpansionConfig<'feat> {
|
2014-06-06 20:21:18 +00:00
|
|
|
pub crate_name: String,
|
2015-02-15 20:30:45 +00:00
|
|
|
pub features: Option<&'feat Features>,
|
2015-01-17 23:33:05 +00:00
|
|
|
pub recursion_limit: usize,
|
2015-04-14 13:36:38 +00:00
|
|
|
pub trace_mac: bool,
|
2016-06-01 01:27:12 +00:00
|
|
|
pub should_test: bool, // If false, strip `#[test]` nodes
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
|
2015-03-06 20:56:28 +00:00
|
|
|
macro_rules! feature_tests {
|
|
|
|
($( fn $getter:ident = $field:ident, )*) => {
|
|
|
|
$(
|
|
|
|
pub fn $getter(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { $field: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
impl<'feat> ExpansionConfig<'feat> {
|
|
|
|
pub fn default(crate_name: String) -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig {
|
|
|
|
crate_name: crate_name,
|
2015-02-15 20:30:45 +00:00
|
|
|
features: None,
|
2014-10-01 09:38:54 +00:00
|
|
|
recursion_limit: 64,
|
2015-04-14 13:36:38 +00:00
|
|
|
trace_mac: false,
|
2016-06-01 01:27:12 +00:00
|
|
|
should_test: false,
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-15 20:30:45 +00:00
|
|
|
|
2015-03-06 20:56:28 +00:00
|
|
|
feature_tests! {
|
2016-04-04 15:08:41 +00:00
|
|
|
fn enable_quotes = quote,
|
|
|
|
fn enable_asm = asm,
|
|
|
|
fn enable_log_syntax = log_syntax,
|
|
|
|
fn enable_concat_idents = concat_idents,
|
|
|
|
fn enable_trace_macros = trace_macros,
|
2015-03-06 20:56:28 +00:00
|
|
|
fn enable_allow_internal_unstable = allow_internal_unstable,
|
2016-04-04 15:08:41 +00:00
|
|
|
fn enable_custom_derive = custom_derive,
|
|
|
|
fn enable_pushpop_unsafe = pushpop_unsafe,
|
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-23 00:07:11 +00:00
|
|
|
fn enable_rustc_macro = rustc_macro,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
}
|
2014-03-01 07:17:38 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 00:55:05 +00:00
|
|
|
pub fn expand_crate(cx: &mut ExtCtxt,
|
2015-12-10 14:23:14 +00:00
|
|
|
user_exts: Vec<NamedSyntaxExtension>,
|
2016-08-04 00:55:05 +00:00
|
|
|
c: Crate) -> Crate {
|
2016-09-02 09:12:47 +00:00
|
|
|
cx.initialize(user_exts, &c);
|
|
|
|
cx.expander().expand_crate(c)
|
2016-08-04 00:55:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Expands crate using supplied MacroExpander - allows for
|
|
|
|
// non-standard expansion behaviour (e.g. step-wise).
|
|
|
|
pub fn expand_crate_with_expander(expander: &mut MacroExpander,
|
|
|
|
user_exts: Vec<NamedSyntaxExtension>,
|
2016-09-02 09:12:47 +00:00
|
|
|
c: Crate) -> Crate {
|
2016-09-01 06:44:54 +00:00
|
|
|
expander.cx.initialize(user_exts, &c);
|
2016-09-02 09:12:47 +00:00
|
|
|
expander.expand_crate(c)
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2016-05-17 21:20:09 +00:00
|
|
|
// A Marker adds the given mark to the syntax context and
|
|
|
|
// sets spans' `expn_id` to the given expn_id (unless it is `None`).
|
2016-06-22 08:03:42 +00:00
|
|
|
struct Marker { mark: Mark, expn_id: Option<ExpnId> }
|
2013-07-09 22:56:21 +00:00
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
impl Folder for Marker {
|
2016-06-22 08:03:42 +00:00
|
|
|
fn fold_ident(&mut self, mut ident: Ident) -> Ident {
|
|
|
|
ident.ctxt = ident.ctxt.apply_mark(self.mark);
|
|
|
|
ident
|
|
|
|
}
|
|
|
|
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
|
|
|
noop_fold_mac(mac, self)
|
2016-05-17 21:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn new_span(&mut self, mut span: Span) -> Span {
|
|
|
|
if let Some(expn_id) = self.expn_id {
|
|
|
|
span.expn_id = expn_id;
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2016-05-17 21:20:09 +00:00
|
|
|
span
|
2013-07-09 22:56:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// apply a given mark to the given token trees. Used prior to expansion of a macro.
|
2016-06-22 08:03:42 +00:00
|
|
|
fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
|
2016-05-17 21:20:09 +00:00
|
|
|
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2014-07-09 23:41:13 +00:00
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
#[cfg(test)]
|
2015-04-24 15:30:41 +00:00
|
|
|
mod tests {
|
2016-06-26 04:12:31 +00:00
|
|
|
use super::{expand_crate, ExpansionConfig};
|
2013-02-25 19:11:21 +00:00
|
|
|
use ast;
|
2016-06-02 01:14:33 +00:00
|
|
|
use ext::base::{ExtCtxt, DummyMacroLoader};
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2014-03-09 14:54:34 +00:00
|
|
|
use util::parser_testing::{string_to_parser};
|
2013-06-29 12:59:08 +00:00
|
|
|
use visit;
|
2013-10-28 08:20:26 +00:00
|
|
|
use visit::Visitor;
|
|
|
|
|
|
|
|
// a visitor that extracts the paths
|
|
|
|
// from a given thingy and puts them in a mutable
|
|
|
|
// array (passed in to the traversal)
|
2015-01-04 03:54:18 +00:00
|
|
|
#[derive(Clone)]
|
2014-06-25 00:03:49 +00:00
|
|
|
struct PathExprFinderContext {
|
2014-02-28 21:09:09 +00:00
|
|
|
path_accumulator: Vec<ast::Path> ,
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
|
2016-06-12 07:51:31 +00:00
|
|
|
impl Visitor for PathExprFinderContext {
|
2014-09-12 10:10:30 +00:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2016-02-08 15:05:05 +00:00
|
|
|
if let ast::ExprKind::Path(None, ref p) = expr.node {
|
2015-02-17 17:29:13 +00:00
|
|
|
self.path_accumulator.push(p.clone());
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2015-02-17 17:29:13 +00:00
|
|
|
visit::walk_expr(self, expr);
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// these following tests are quite fragile, in that they don't test what
|
|
|
|
// *kind* of failure occurs.
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
fn test_ecfg() -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig::default("test".to_string())
|
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape fns
|
2015-01-31 23:08:25 +00:00
|
|
|
#[should_panic]
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_cant_escape_fns_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
2016-08-04 00:55:05 +00:00
|
|
|
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
|
|
|
expand_crate(&mut ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape modules
|
2015-01-31 23:08:25 +00:00
|
|
|
#[should_panic]
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_cant_escape_mods_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
2016-08-04 00:55:05 +00:00
|
|
|
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
|
|
|
expand_crate(&mut ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 04:48:26 +00:00
|
|
|
// macro_use modules should allow macros to escape
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_can_escape_flattened_mods_test () {
|
2014-12-19 04:48:26 +00:00
|
|
|
let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
2016-08-04 00:55:05 +00:00
|
|
|
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
|
|
|
expand_crate(&mut ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-22 23:57:53 +00:00
|
|
|
fn expand_crate_str(crate_str: String) -> ast::Crate {
|
2015-05-13 20:00:17 +00:00
|
|
|
let ps = parse::ParseSess::new();
|
2015-03-28 21:58:51 +00:00
|
|
|
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
|
2013-06-25 18:40:51 +00:00
|
|
|
// the cfg argument actually does matter, here...
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
2016-08-04 00:55:05 +00:00
|
|
|
let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
|
|
|
|
expand_crate(&mut ecx, vec![], crate_ast)
|
2013-07-09 23:00:41 +00:00
|
|
|
}
|
|
|
|
|
2013-07-09 22:10:16 +00:00
|
|
|
#[test] fn macro_tokens_should_match(){
|
2014-05-07 23:33:43 +00:00
|
|
|
expand_crate_str(
|
2014-11-14 17:18:10 +00:00
|
|
|
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
|
2013-07-09 22:10:16 +00:00
|
|
|
}
|
|
|
|
|
2014-07-08 00:43:09 +00:00
|
|
|
// should be able to use a bound identifier as a literal in a macro definition:
|
|
|
|
#[test] fn self_macro_parsing(){
|
|
|
|
expand_crate_str(
|
2015-01-17 23:55:21 +00:00
|
|
|
"macro_rules! foo ((zz) => (287;));
|
|
|
|
fn f(zz: i32) {foo!(zz);}".to_string()
|
2014-07-08 00:43:09 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-11-12 01:35:25 +00:00
|
|
|
// create a really evil test case where a $x appears inside a binding of $x
|
2014-07-03 01:27:07 +00:00
|
|
|
// but *shouldn't* bind because it was inserted by a different macro....
|
2013-11-12 01:35:25 +00:00
|
|
|
// can't write this test case until we have macro-generating macros.
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|