Rollup merge of - jseyfried:node_ids_in_expansion, r=nrc

Assign node ids during macro expansion

After this PR,
 - The `ExtCtxt` can access `resolve`'s `Resolver` through the trait object `ext::base::Resolver`.
  - The `Resolver` trait object can load macros and replaces today's `MacroLoader` trait object.
  - The macro expander uses the `Resolver` trait object to resolve macro invocations.
 - The macro expander assigns node ids and builds the `Resolver`'s `macros_at_scope` map.
   - This is groundwork for merging import resolution and expansion.
 - Performance of expansion together with node id assignment improves by ~5%.

**EDIT:** Since Github is reordering the commits, here is `git log`:
 - b54e1e399741579612f13e2df98a25ea9447989d: Differentiate between monotonic and non-monotonic expansion and only assign node ids during monotonic expansion.
 - 78c00398780db6f59ebf43e765fa9368dad436d2: Expand generated test harnesses and macro registries.
 - f3c2dca3539e6edc745f9c91898cb97d281865c1: Remove scope placeholders from the crate root.
 - c86c8d41a26b2037e80c9fd028a59313a78b3a66: Perform node id assignment and `macros_at_scope` construction during the `InvocationCollector` and `PlaceholderExpander` folds.
 - 72a636975fc5d0bb4af45af7bdd97987cc722a6a: Move macro resolution into `librustc_resolve`.
 - 20b43b23230ce063ccf99a4841d85790ad311bdf: Rewrite the unit tests in `ext/expand.rs` as a `compile-fail` test.
 - a9821e1658240bb2c056f260a4b6bc9789301fae: Refactor `ExtCtxt` to use a `Resolver` instead of a `MacroLoader`.
 - 60440b226d2f70bdae803443ff7ad2e2af2c9b10: Refactor `noop_fold_stmt_kind` out of `noop_fold_stmt`.
 - 50f94f6c95c944f08c4af264f48260e42efefd47: Avoid needless reexpansions.

r? @nrc
This commit is contained in:
Manish Goregaokar 2016-09-15 18:16:21 +05:30 committed by GitHub
commit bab9238a1e
23 changed files with 693 additions and 719 deletions
src
librustc
middle
session
librustc_driver
librustc_metadata
librustc_resolve
libsyntax
libsyntax_ext
test
compile-fail-fulldeps
compile-fail
run-fail-fulldeps
run-pass-fulldeps

@ -39,6 +39,7 @@ use std::rc::Rc;
use std::path::PathBuf;
use syntax::ast;
use syntax::attr;
use syntax::ext::base::LoadedMacro;
use syntax::ptr::P;
use syntax::parse::token::InternedString;
use syntax_pos::Span;
@ -492,6 +493,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") }
}
pub trait MacroLoader {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
}
/// Metadata encoding and decoding can make use of thread-local encoding and
/// decoding contexts. These allow implementers of serialize::Encodable and

@ -21,7 +21,7 @@ use util::nodemap::{NodeMap, FnvHashMap};
use util::common::duration_to_secs_str;
use mir::transform as mir_pass;
use syntax::ast::{NodeId, Name};
use syntax::ast::NodeId;
use errors::{self, DiagnosticBuilder};
use errors::emitter::{Emitter, EmitterWriter};
use syntax::json::JsonEmitter;
@ -39,7 +39,7 @@ use llvm;
use std::path::{Path, PathBuf};
use std::cell::{self, Cell, RefCell};
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::env;
use std::ffi::CString;
use std::rc::Rc;
@ -96,10 +96,6 @@ pub struct Session {
pub injected_allocator: Cell<Option<ast::CrateNum>>,
pub injected_panic_runtime: Cell<Option<ast::CrateNum>>,
/// Names of all bang-style macros and syntax extensions
/// available in this crate
pub available_macros: RefCell<HashSet<Name>>,
/// Map from imported macro spans (which consist of
/// the localized span for the macro body) to the
/// macro name and defintion span in the source crate.
@ -552,7 +548,6 @@ pub fn build_session_(sopts: config::Options,
next_node_id: Cell::new(1),
injected_allocator: Cell::new(None),
injected_panic_runtime: Cell::new(None),
available_macros: RefCell::new(HashSet::new()),
imported_macro_spans: RefCell::new(HashMap::new()),
incr_comp_session: RefCell::new(IncrCompSession::NotInitialized),
perf_stats: PerfStats {

@ -50,6 +50,7 @@ use std::io::{self, Write};
use std::path::{Path, PathBuf};
use syntax::{ast, diagnostics, visit};
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::parse::{self, PResult, token};
use syntax::util::node_count::NodeCounter;
use syntax;
@ -638,6 +639,13 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
}
sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?;
let mut macro_loader =
macro_import::MacroLoader::new(sess, &cstore, crate_name, krate.config.clone());
let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas);
syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote);
krate = time(time_passes, "expansion", || {
// Windows dlls do not have rpaths, so they don't know how to find their
// dependencies. It's up to us to tell the system where to find all the
@ -672,25 +680,17 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
trace_mac: sess.opts.debugging_opts.trace_macros,
should_test: sess.opts.test,
};
let mut loader = macro_import::MacroLoader::new(sess,
&cstore,
crate_name,
krate.config.clone());
let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
krate.config.clone(),
cfg,
&mut loader);
syntax_ext::register_builtins(&mut ecx.syntax_env);
let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver);
let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate);
if cfg!(windows) {
env::set_var("PATH", &old_path);
}
*sess.available_macros.borrow_mut() = ecx.syntax_env.names;
ret
});
krate = time(time_passes, "maybe building test harness", || {
syntax::test::modify_for_testing(&sess.parse_sess,
&mut resolver,
sess.opts.test,
krate,
sess.diagnostic())
@ -701,6 +701,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro);
let num_crate_types = crate_types.len();
syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess,
&mut resolver,
krate,
is_rustc_macro_crate,
num_crate_types,
@ -708,11 +709,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
&sess.features.borrow())
});
let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);
let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate));
if sess.opts.debugging_opts.input_stats {
println!("Post-expansion node count: {}", count_nodes(&krate));
}

@ -18,6 +18,7 @@ use creader::{CrateReader, Macros};
use cstore::CStore;
use rustc::hir::def_id::DefIndex;
use rustc::middle;
use rustc::session::Session;
use rustc::util::nodemap::FnvHashMap;
use rustc_back::dynamic_lib::DynamicLibrary;
@ -26,7 +27,6 @@ use rustc_macro::__internal::Registry;
use syntax::ast;
use syntax::attr;
use syntax::ext::base::LoadedMacro;
use syntax::ext;
use syntax::parse::token;
use syntax_ext::deriving::custom::CustomDerive;
use syntax_pos::Span;
@ -55,7 +55,7 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
pub type MacroSelection = FnvHashMap<token::InternedString, Span>;
impl<'a> ext::base::MacroLoader for MacroLoader<'a> {
impl<'a> middle::cstore::MacroLoader for MacroLoader<'a> {
fn load_crate(&mut self,
extern_crate: &ast::Item,
allows_macros: bool) -> Vec<LoadedMacro> {

@ -1,92 +0,0 @@
// Copyright 2016 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.
use Resolver;
use rustc::session::Session;
use rustc::util::nodemap::FnvHashMap;
use syntax::ast;
use syntax::ext::hygiene::Mark;
use syntax::fold::{self, Folder};
use syntax::ptr::P;
use syntax::util::move_map::MoveMap;
use syntax::util::small_vector::SmallVector;
use std::mem;
impl<'a> Resolver<'a> {
pub fn assign_node_ids(&mut self, krate: ast::Crate) -> ast::Crate {
NodeIdAssigner {
sess: self.session,
macros_at_scope: &mut self.macros_at_scope,
}.fold_crate(krate)
}
}
struct NodeIdAssigner<'a> {
sess: &'a Session,
macros_at_scope: &'a mut FnvHashMap<ast::NodeId, Vec<Mark>>,
}
impl<'a> Folder for NodeIdAssigner<'a> {
fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.sess.next_node_id()
}
fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
block.map(|mut block| {
block.id = self.new_id(block.id);
let stmt = block.stmts.pop();
let mut macros = Vec::new();
block.stmts = block.stmts.move_flat_map(|stmt| {
if let ast::StmtKind::Item(ref item) = stmt.node {
if let ast::ItemKind::Mac(..) = item.node {
macros.push(item.ident.ctxt.data().outer_mark);
return None;
}
}
let stmt = self.fold_stmt(stmt).pop().unwrap();
if !macros.is_empty() {
self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
}
Some(stmt)
});
stmt.and_then(|mut stmt| {
// Avoid wasting a node id on a trailing expression statement,
// which shares a HIR node with the expression itself.
if let ast::StmtKind::Expr(expr) = stmt.node {
let expr = self.fold_expr(expr);
stmt.id = expr.id;
stmt.node = ast::StmtKind::Expr(expr);
Some(stmt)
} else {
self.fold_stmt(stmt).pop()
}
}).map(|stmt| {
if !macros.is_empty() {
self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new()));
}
block.stmts.push(stmt);
});
block
})
}
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
match item.node {
ast::ItemKind::Mac(..) => SmallVector::zero(),
_ => fold::noop_fold_item(item, self),
}
}
}

@ -45,6 +45,7 @@ use self::ParentLink::*;
use rustc::hir::map::Definitions;
use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr};
use rustc::middle::cstore::MacroLoader;
use rustc::session::Session;
use rustc::lint;
use rustc::hir::def::*;
@ -79,10 +80,10 @@ use resolve_imports::{ImportDirective, NameResolution};
// registered before they are used.
mod diagnostics;
mod macros;
mod check_unused;
mod build_reduced_graph;
mod resolve_imports;
mod assign_ids;
enum SuggestionType {
Macro(String),
@ -1068,6 +1069,12 @@ pub struct Resolver<'a> {
arenas: &'a ResolverArenas<'a>,
dummy_binding: &'a NameBinding<'a>,
new_import_semantics: bool, // true if `#![feature(item_like_imports)]`
macro_loader: &'a mut MacroLoader,
macro_names: FnvHashSet<Name>,
// Maps the `Mark` of an expansion to its containing module or block.
expansion_data: Vec<macros::ExpansionData>,
}
pub struct ResolverArenas<'a> {
@ -1166,7 +1173,10 @@ impl Named for hir::PathSegment {
}
impl<'a> Resolver<'a> {
pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>)
pub fn new(session: &'a Session,
make_glob_map: MakeGlobMap,
macro_loader: &'a mut MacroLoader,
arenas: &'a ResolverArenas<'a>)
-> Resolver<'a> {
let root_def_id = DefId::local(CRATE_DEF_INDEX);
let graph_root =
@ -1227,6 +1237,10 @@ impl<'a> Resolver<'a> {
vis: ty::Visibility::Public,
}),
new_import_semantics: session.features.borrow().item_like_imports,
macro_loader: macro_loader,
macro_names: FnvHashSet(),
expansion_data: vec![macros::ExpansionData::default()],
}
}
@ -2768,8 +2782,7 @@ impl<'a> Resolver<'a> {
}
fn find_best_match(&mut self, name: &str) -> SuggestionType {
if let Some(macro_name) = self.session.available_macros
.borrow().iter().find(|n| n.as_str() == name) {
if let Some(macro_name) = self.macro_names.iter().find(|n| n.as_str() == name) {
return SuggestionType::Macro(format!("{}!", macro_name));
}

@ -0,0 +1,217 @@
// Copyright 2016 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.
use Resolver;
use rustc::util::nodemap::FnvHashMap;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;
use syntax::ast::{self, Name};
use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{self, LoadedMacro, MultiModifier, MultiDecorator};
use syntax::ext::base::{NormalTT, SyntaxExtension};
use syntax::ext::expand::{Expansion, Invocation, InvocationKind};
use syntax::ext::hygiene::Mark;
use syntax::parse::token::intern;
use syntax::util::lev_distance::find_best_match_for_name;
use syntax::visit::{self, Visitor};
#[derive(Clone, Default)]
pub struct ExpansionData {
module: Rc<ModuleData>,
}
// FIXME(jseyfried): merge with `::ModuleS`.
#[derive(Default)]
struct ModuleData {
parent: Option<Rc<ModuleData>>,
macros: RefCell<FnvHashMap<Name, Rc<SyntaxExtension>>>,
macros_escape: bool,
}
impl<'a> base::Resolver for Resolver<'a> {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro> {
self.macro_loader.load_crate(extern_crate, allows_macros)
}
fn next_node_id(&mut self) -> ast::NodeId {
self.session.next_node_id()
}
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) {
expansion.visit_with(&mut ExpansionVisitor {
current_module: self.expansion_data[mark.as_u32() as usize].module.clone(),
resolver: self,
});
}
fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>) {
if let NormalTT(..) = *ext {
self.macro_names.insert(ident.name);
}
let mut module = self.expansion_data[scope.as_u32() as usize].module.clone();
while module.macros_escape {
module = module.parent.clone().unwrap();
}
module.macros.borrow_mut().insert(ident.name, ext);
}
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>) {
self.macros_at_scope.insert(id, macros);
}
fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
for i in 0..attrs.len() {
let name = intern(&attrs[i].name());
match self.expansion_data[0].module.macros.borrow().get(&name) {
Some(ext) => match **ext {
MultiModifier(..) | MultiDecorator(..) => return Some(attrs.remove(i)),
_ => {}
},
None => {}
}
}
None
}
fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>> {
let (name, span) = match invoc.kind {
InvocationKind::Bang { ref mac, .. } => {
let path = &mac.node.path;
if path.segments.len() > 1 || path.global ||
!path.segments[0].parameters.is_empty() {
self.session.span_err(path.span,
"expected macro name without module separators");
return None;
}
(path.segments[0].identifier.name, path.span)
}
InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span),
};
let mut module = self.expansion_data[invoc.mark().as_u32() as usize].module.clone();
loop {
if let Some(ext) = module.macros.borrow().get(&name) {
return Some(ext.clone());
}
match module.parent.clone() {
Some(parent) => module = parent,
None => break,
}
}
let mut err =
self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name));
self.suggest_macro_name(&name.as_str(), &mut err);
err.emit();
None
}
}
impl<'a> Resolver<'a> {
fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) {
if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) {
if suggestion != name {
err.help(&format!("did you mean `{}!`?", suggestion));
} else {
err.help(&format!("have you added the `#[macro_use]` on the module/import?"));
}
}
}
}
struct ExpansionVisitor<'b, 'a: 'b> {
resolver: &'b mut Resolver<'a>,
current_module: Rc<ModuleData>,
}
impl<'a, 'b> ExpansionVisitor<'a, 'b> {
fn visit_invoc(&mut self, id: ast::NodeId) {
assert_eq!(id, self.resolver.expansion_data.len() as u32);
self.resolver.expansion_data.push(ExpansionData {
module: self.current_module.clone(),
});
}
// does this attribute list contain "macro_use"?
fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
for attr in attrs {
if attr.check_name("macro_escape") {
let msg = "macro_escape is a deprecated synonym for macro_use";
let mut err = self.resolver.session.struct_span_warn(attr.span, msg);
if let ast::AttrStyle::Inner = attr.node.style {
err.help("consider an outer attribute, #[macro_use] mod ...").emit();
} else {
err.emit();
}
} else if !attr.check_name("macro_use") {
continue;
}
if !attr.is_word() {
self.resolver.session.span_err(attr.span,
"arguments to macro_use are not allowed here");
}
return true;
}
false
}
}
macro_rules! method {
($visit:ident: $ty:ty, $invoc:path, $walk:ident) => {
fn $visit(&mut self, node: &$ty) {
match node.node {
$invoc(..) => self.visit_invoc(node.id),
_ => visit::$walk(self, node),
}
}
}
}
impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> {
method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item);
method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item);
method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt);
method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr);
method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat);
method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty);
fn visit_item(&mut self, item: &ast::Item) {
match item.node {
ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder
ast::ItemKind::Mac(..) => self.visit_invoc(item.id),
ast::ItemKind::Mod(..) => {
let module_data = ModuleData {
parent: Some(self.current_module.clone()),
macros: RefCell::new(FnvHashMap()),
macros_escape: self.contains_macro_use(&item.attrs),
};
let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
visit::walk_item(self, item);
self.current_module = orig_module;
}
_ => visit::walk_item(self, item),
}
}
fn visit_block(&mut self, block: &ast::Block) {
let module_data = ModuleData {
parent: Some(self.current_module.clone()),
macros: RefCell::new(FnvHashMap()),
macros_escape: false,
};
let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data));
visit::walk_block(self, block);
self.current_module = orig_module;
}
}

@ -10,27 +10,25 @@
pub use self::SyntaxExtension::*;
use ast;
use ast::{Name, PatKind};
use ast::{self, Attribute, Name, PatKind};
use attr::HasAttrs;
use codemap::{self, CodeMap, ExpnInfo};
use codemap::{self, CodeMap, ExpnInfo, Spanned, respan};
use syntax_pos::{Span, ExpnId, NO_EXPANSION};
use errors::DiagnosticBuilder;
use ext;
use ext::expand;
use ext::expand::{self, Invocation, Expansion};
use ext::hygiene::Mark;
use ext::tt::macro_rules;
use parse;
use parse::parser;
use parse::token;
use parse::token::{InternedString, intern, str_to_ident};
use parse::token::{InternedString, str_to_ident};
use ptr::P;
use std_inject;
use util::small_vector::SmallVector;
use util::lev_distance::find_best_match_for_name;
use fold::Folder;
use feature_gate;
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use tokenstream;
@ -44,7 +42,7 @@ pub enum Annotatable {
}
impl HasAttrs for Annotatable {
fn attrs(&self) -> &[ast::Attribute] {
fn attrs(&self) -> &[Attribute] {
match *self {
Annotatable::Item(ref item) => &item.attrs,
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
@ -52,7 +50,7 @@ impl HasAttrs for Annotatable {
}
}
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
match self {
Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
@ -464,91 +462,16 @@ pub enum SyntaxExtension {
pub type NamedSyntaxExtension = (Name, SyntaxExtension);
/// The base map of methods for expanding syntax extension
/// AST nodes into full ASTs
fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
-> SyntaxEnv {
// utility function to simplify creating NormalTT syntax extensions
fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
NormalTT(Box::new(f), None, false)
}
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;
let mut syntax_expanders = SyntaxEnv::new();
syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
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>);
if ecfg.enable_quotes() {
// Quasi-quoting expanders
syntax_expanders.insert(intern("quote_tokens"),
builtin_normal_expander(
ext::quote::expand_quote_tokens));
syntax_expanders.insert(intern("quote_expr"),
builtin_normal_expander(
ext::quote::expand_quote_expr));
syntax_expanders.insert(intern("quote_ty"),
builtin_normal_expander(
ext::quote::expand_quote_ty));
syntax_expanders.insert(intern("quote_item"),
builtin_normal_expander(
ext::quote::expand_quote_item));
syntax_expanders.insert(intern("quote_pat"),
builtin_normal_expander(
ext::quote::expand_quote_pat));
syntax_expanders.insert(intern("quote_arm"),
builtin_normal_expander(
ext::quote::expand_quote_arm));
syntax_expanders.insert(intern("quote_stmt"),
builtin_normal_expander(
ext::quote::expand_quote_stmt));
syntax_expanders.insert(intern("quote_matcher"),
builtin_normal_expander(
ext::quote::expand_quote_matcher));
syntax_expanders.insert(intern("quote_attr"),
builtin_normal_expander(
ext::quote::expand_quote_attr));
syntax_expanders.insert(intern("quote_arg"),
builtin_normal_expander(
ext::quote::expand_quote_arg));
syntax_expanders.insert(intern("quote_block"),
builtin_normal_expander(
ext::quote::expand_quote_block));
syntax_expanders.insert(intern("quote_meta_item"),
builtin_normal_expander(
ext::quote::expand_quote_meta_item));
syntax_expanders.insert(intern("quote_path"),
builtin_normal_expander(
ext::quote::expand_quote_path));
}
syntax_expanders.insert(intern("line"),
builtin_normal_expander(
ext::source_util::expand_line));
syntax_expanders.insert(intern("column"),
builtin_normal_expander(
ext::source_util::expand_column));
syntax_expanders.insert(intern("file"),
builtin_normal_expander(
ext::source_util::expand_file));
syntax_expanders.insert(intern("stringify"),
builtin_normal_expander(
ext::source_util::expand_stringify));
syntax_expanders.insert(intern("include"),
builtin_normal_expander(
ext::source_util::expand_include));
syntax_expanders.insert(intern("include_str"),
builtin_normal_expander(
ext::source_util::expand_include_str));
syntax_expanders.insert(intern("include_bytes"),
builtin_normal_expander(
ext::source_util::expand_include_bytes));
syntax_expanders.insert(intern("module_path"),
builtin_normal_expander(
ext::source_util::expand_mod));
syntax_expanders
}
pub trait MacroLoader {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool)
-> Vec<LoadedMacro>;
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>>;
}
pub enum LoadedMacro {
@ -556,11 +479,35 @@ pub enum LoadedMacro {
CustomDerive(String, Box<MultiItemModifier>),
}
pub struct DummyMacroLoader;
impl MacroLoader for DummyMacroLoader {
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<LoadedMacro> {
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,
}
/// One of these is made during expansion and incrementally updated as we go;
@ -569,63 +516,68 @@ impl MacroLoader for DummyMacroLoader {
pub struct ExtCtxt<'a> {
pub parse_sess: &'a parse::ParseSess,
pub cfg: ast::CrateConfig,
pub backtrace: ExpnId,
pub ecfg: expand::ExpansionConfig<'a>,
pub crate_root: Option<&'static str>,
pub loader: &'a mut MacroLoader,
pub resolver: &'a mut Resolver,
pub exported_macros: Vec<ast::MacroDef>,
pub syntax_env: SyntaxEnv,
pub derive_modes: HashMap<InternedString, Box<MultiItemModifier>>,
pub recursion_count: usize,
pub current_expansion: ExpansionData,
}
impl<'a> ExtCtxt<'a> {
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
ecfg: expand::ExpansionConfig<'a>,
loader: &'a mut MacroLoader)
resolver: &'a mut Resolver)
-> ExtCtxt<'a> {
ExtCtxt {
syntax_env: initial_syntax_expander_table(&ecfg),
parse_sess: parse_sess,
cfg: cfg,
backtrace: NO_EXPANSION,
ecfg: ecfg,
crate_root: None,
exported_macros: Vec::new(),
loader: loader,
resolver: resolver,
derive_modes: HashMap::new(),
recursion_count: 0,
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, false)
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() }
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 {
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.backtrace }
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 expn_id = self.backtrace();
let mut last_macro = None;
loop {
if self.codemap().with_expn_info(expn_id, |info| {
@ -646,15 +598,15 @@ impl<'a> ExtCtxt<'a> {
}
pub fn bt_push(&mut self, ei: ExpnInfo) {
if self.recursion_count > self.ecfg.recursion_limit {
if self.current_expansion.depth > self.ecfg.recursion_limit {
self.span_fatal(ei.call_site,
&format!("recursion limit reached while expanding the macro `{}`",
ei.callee.name()));
}
let mut call_site = ei.call_site;
call_site.expn_id = self.backtrace;
self.backtrace = self.codemap().record_expansion(ExpnInfo {
call_site.expn_id = self.backtrace();
self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo {
call_site: call_site,
callee: ei.callee
});
@ -667,14 +619,11 @@ impl<'a> ExtCtxt<'a> {
}
if def.use_locally {
let ext = macro_rules::compile(self, &def);
self.syntax_env.insert(def.ident.name, ext);
self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext));
}
}
pub fn insert_custom_derive(&mut self,
name: &str,
ext: Box<MultiItemModifier>,
sp: Span) {
pub fn insert_custom_derive(&mut self, name: &str, ext: Box<MultiItemModifier>, sp: Span) {
if !self.ecfg.enable_rustc_macro() {
feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic,
"rustc_macro",
@ -685,8 +634,7 @@ impl<'a> ExtCtxt<'a> {
}
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));
self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name));
}
}
@ -765,20 +713,6 @@ impl<'a> ExtCtxt<'a> {
token::intern(st)
}
pub fn suggest_macro_name(&mut self,
name: &str,
err: &mut DiagnosticBuilder<'a>) {
let names = &self.syntax_env.names;
if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) {
if suggestion != name {
err.help(&format!("did you mean `{}!`?", suggestion));
} else {
err.help(&format!("have you added the `#[macro_use]` on the \
module/import?"));
}
}
}
pub fn initialize(&mut self, user_exts: Vec<NamedSyntaxExtension>, krate: &ast::Crate) {
if std_inject::no_core(&krate) {
self.crate_root = None;
@ -789,27 +723,27 @@ impl<'a> ExtCtxt<'a> {
}
for (name, extension) in user_exts {
self.syntax_env.insert(name, extension);
let ident = ast::Ident::with_empty_ctxt(name);
self.resolver.add_macro(Mark::root(), ident, Rc::new(extension));
}
self.syntax_env.current_module = Module(0);
let mut paths = ModulePaths {
let mut module = ModuleData {
mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)],
directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)),
};
paths.directory.pop();
self.syntax_env.module_data[0].paths = Rc::new(paths);
module.directory.pop();
self.current_expansion.module = Rc::new(module);
}
}
/// 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.
pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
-> Option<(InternedString, ast::StrStyle)> {
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.span.expn_id = cx.backtrace();
expr
});
@ -817,7 +751,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
let expr = cx.expander().fold_expr(expr);
match expr.node {
ast::ExprKind::Lit(ref l) => match l.node {
ast::LitKind::Str(ref s, style) => return Some(((*s).clone(), style)),
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)
@ -825,6 +759,11 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
None
}
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
@ -851,7 +790,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
cx.span_err(sp, &format!("{} takes 1 argument", name));
return None
}
let ret = cx.expander().fold_expr(panictry!(p.parse_expr()));
let ret = panictry!(p.parse_expr());
if p.token != token::Eof {
cx.span_err(sp, &format!("{} takes 1 argument", name));
}
@ -879,104 +818,3 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt,
}
Some(es)
}
/// In order to have some notion of scoping for macros,
/// we want to implement the notion of a transformation
/// environment.
///
/// This environment maps Names to SyntaxExtensions.
pub struct SyntaxEnv {
module_data: Vec<ModuleData>,
pub current_module: Module,
/// All bang-style macro/extension names
/// encountered so far; to be used for diagnostics in resolve
pub names: HashSet<Name>,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Module(u32);
struct ModuleData {
parent: Module,
paths: Rc<ModulePaths>,
macros: HashMap<Name, Rc<SyntaxExtension>>,
macros_escape: bool,
in_block: bool,
}
#[derive(Clone)]
pub struct ModulePaths {
pub mod_path: Vec<ast::Ident>,
pub directory: PathBuf,
}
impl SyntaxEnv {
fn new() -> SyntaxEnv {
let mut env = SyntaxEnv {
current_module: Module(0),
module_data: Vec::new(),
names: HashSet::new(),
};
let paths = Rc::new(ModulePaths { mod_path: Vec::new(), directory: PathBuf::new() });
env.add_module(false, false, paths);
env
}
fn data(&self, module: Module) -> &ModuleData {
&self.module_data[module.0 as usize]
}
pub fn paths(&self) -> Rc<ModulePaths> {
self.data(self.current_module).paths.clone()
}
pub fn in_block(&self) -> bool {
self.data(self.current_module).in_block
}
pub fn add_module(&mut self, macros_escape: bool, in_block: bool, paths: Rc<ModulePaths>)
-> Module {
let data = ModuleData {
parent: self.current_module,
paths: paths,
macros: HashMap::new(),
macros_escape: macros_escape,
in_block: in_block,
};
self.module_data.push(data);
Module(self.module_data.len() as u32 - 1)
}
pub fn find(&self, name: Name) -> Option<Rc<SyntaxExtension>> {
let mut module = self.current_module;
let mut module_data;
loop {
module_data = self.data(module);
if let Some(ext) = module_data.macros.get(&name) {
return Some(ext.clone());
}
if module == module_data.parent {
return None;
}
module = module_data.parent;
}
}
pub fn insert(&mut self, name: Name, ext: SyntaxExtension) {
if let NormalTT(..) = ext {
self.names.insert(name);
}
let mut module = self.current_module;
while self.data(module).macros_escape {
module = self.data(module).parent;
}
self.module_data[module.0 as usize].macros.insert(name, Rc::new(ext));
}
pub fn is_crate_root(&mut self) -> bool {
self.current_module == Module(0)
}
}

@ -25,6 +25,7 @@ use parse::token::{intern, keywords};
use ptr::P;
use tokenstream::TokenTree;
use util::small_vector::SmallVector;
use visit::Visitor;
use std::mem;
use std::path::PathBuf;
@ -32,7 +33,8 @@ use std::rc::Rc;
macro_rules! expansions {
($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident,
$(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => {
$(.$fold:ident)* $(lift .$fold_elt:ident)*,
$(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => {
#[derive(Copy, Clone)]
pub enum ExpansionKind { OptExpr, $( $kind, )* }
pub enum Expansion { OptExpr(Option<P<ast::Expr>>), $( $kind($ty), )* }
@ -77,6 +79,17 @@ macro_rules! expansions {
}, )*)*
}
}
pub fn visit_with<V: Visitor>(&self, visitor: &mut V) {
match *self {
Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
Expansion::OptExpr(None) => {}
$($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)*
$($( Expansion::$kind(ref ast) => for ast in ast.as_slice() {
visitor.$visit_elt(ast);
}, )*)*
}
}
}
impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
@ -94,17 +107,17 @@ macro_rules! expansions {
}
expansions! {
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;
Expr: P<ast::Expr> [], "expression", .make_expr, .fold_expr, .visit_expr;
Pat: P<ast::Pat> [], "pattern", .make_pat, .fold_pat, .visit_pat;
Ty: P<ast::Ty> [], "type", .make_ty, .fold_ty, .visit_ty;
Stmts: SmallVector<ast::Stmt> [SmallVector, ast::Stmt],
"statement", .make_stmts, lift .fold_stmt;
"statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
Items: SmallVector<P<ast::Item>> [SmallVector, P<ast::Item>],
"item", .make_items, lift .fold_item;
"item", .make_items, lift .fold_item, lift .visit_item;
TraitItems: SmallVector<ast::TraitItem> [SmallVector, ast::TraitItem],
"trait item", .make_trait_items, lift .fold_trait_item;
"trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
ImplItems: SmallVector<ast::ImplItem> [SmallVector, ast::ImplItem],
"impl item", .make_impl_items, lift .fold_impl_item;
"impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item;
}
impl ExpansionKind {
@ -127,15 +140,12 @@ impl ExpansionKind {
}
pub struct Invocation {
kind: InvocationKind,
pub kind: InvocationKind,
expansion_kind: ExpansionKind,
mark: Mark,
module: Module,
backtrace: ExpnId,
depth: usize,
expansion_data: ExpansionData,
}
enum InvocationKind {
pub enum InvocationKind {
Bang {
attrs: Vec<ast::Attribute>,
mac: ast::Mac,
@ -148,29 +158,53 @@ enum InvocationKind {
},
}
impl Invocation {
fn span(&self) -> Span {
match self.kind {
InvocationKind::Bang { span, .. } => span,
InvocationKind::Attr { ref attr, .. } => attr.span,
}
}
pub fn mark(&self) -> Mark {
self.expansion_data.mark
}
}
pub struct MacroExpander<'a, 'b:'a> {
pub cx: &'a mut ExtCtxt<'b>,
pub single_step: bool,
pub keep_macs: bool,
monotonic: bool, // c.f. `cx.monotonic_expander()`
}
impl<'a, 'b> MacroExpander<'a, 'b> {
pub fn new(cx: &'a mut ExtCtxt<'b>,
single_step: bool,
keep_macs: bool) -> MacroExpander<'a, 'b> {
pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
MacroExpander {
cx: cx,
single_step: single_step,
keep_macs: keep_macs
monotonic: monotonic,
single_step: false,
keep_macs: false,
}
}
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();
let mut krate_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
.make_items().pop().unwrap().unwrap();
krate_item.node = ast::ItemKind::Mod(krate.module);
let krate_item = Expansion::Items(SmallVector::one(P(krate_item)));
krate.module = match self.expand(krate_item).make_items().pop().unwrap().unwrap().node {
ast::ItemKind::Mod(module) => module,
_ => unreachable!(),
};
krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new());
for def in &mut krate.exported_macros {
def.id = self.cx.resolver.next_node_id()
}
if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
self.cx.parse_sess.span_diagnostic.abort_if_errors();
@ -181,21 +215,23 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
// Fully expand all the invocations in `expansion`.
fn expand(&mut self, expansion: Expansion) -> Expansion {
self.cx.recursion_count = 0;
let orig_expansion_data = self.cx.current_expansion.clone();
self.cx.current_expansion.depth = 0;
let (expansion, mut invocations) = self.collect_invocations(expansion);
invocations.reverse();
let mut expansions = vec![vec![(0, expansion)]];
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 ExpansionData { depth, mark, .. } = invoc.expansion_data;
self.cx.current_expansion = invoc.expansion_data.clone();
let expansion = self.expand_invoc(invoc);
let expansion = match self.cx.resolver.resolve_invoc(&invoc) {
Some(ext) => self.expand_invoc(invoc, ext),
None => invoc.expansion_kind.dummy(invoc.span()),
};
self.cx.syntax_env.current_module = module;
self.cx.recursion_count = depth + 1;
self.cx.current_expansion.depth = depth + 1;
let (expansion, new_invocations) = self.collect_invocations(expansion);
if expansions.len() == depth {
@ -207,7 +243,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
let mut placeholder_expander = PlaceholderExpander::new();
self.cx.current_expansion = orig_expansion_data;
let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
while let Some(expansions) = expansions.pop() {
for (mark, expansion) in expansions.into_iter().rev() {
let expansion = expansion.fold_with(&mut placeholder_expander);
@ -230,33 +268,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
},
cx: self.cx,
invocations: Vec::new(),
monotonic: self.monotonic,
};
(expansion.fold_with(&mut collector), collector.invocations)
};
self.cx.cfg = crate_config;
let mark = self.cx.current_expansion.mark;
self.cx.resolver.visit_expansion(mark, &result.0);
result
}
fn expand_invoc(&mut self, invoc: Invocation) -> Expansion {
fn expand_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
match invoc.kind {
InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc),
InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc),
InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext),
InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext),
}
}
fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion {
fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> 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,
@ -267,7 +303,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
});
match *extension {
match *ext {
MultiModifier(ref mac) => {
let item = mac.expand(self.cx, attr.span, &attr.node.value, item);
kind.expect_from_annotatables(item)
@ -284,8 +320,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
/// 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;
fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc<SyntaxExtension>) -> Expansion {
let (mark, kind) = (invoc.mark(), invoc.expansion_kind);
let (attrs, mac, ident, span) = match invoc.kind {
InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span),
_ => unreachable!(),
@ -306,19 +342,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
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 {
let opt_expanded = match *ext {
NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
if ident.name != keywords::Invalid.name() {
let msg =
@ -425,6 +451,7 @@ struct InvocationCollector<'a, 'b: 'a> {
cx: &'a mut ExtCtxt<'b>,
cfg: StripUnconfigured<'a>,
invocations: Vec<Invocation>,
monotonic: bool,
}
macro_rules! fully_configure {
@ -442,10 +469,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
self.invocations.push(Invocation {
kind: kind,
expansion_kind: expansion_kind,
mark: mark,
module: self.cx.syntax_env.current_module,
backtrace: self.cx.backtrace,
depth: self.cx.recursion_count,
expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() },
});
placeholder(expansion_kind, mark.as_u32())
}
@ -462,50 +486,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
// 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>) {
fn classify_item<T: HasAttrs>(&mut 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;
}
_ => {}
}
}
}
attr = self.cx.resolver.find_attr_invoc(&mut attrs);
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
}
fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
self.cfg.configure(node)
}
@ -554,9 +543,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
None => return SmallVector::zero(),
};
let (mac, style, attrs) = match stmt.node {
StmtKind::Mac(mac) => mac.unwrap(),
_ => return noop_fold_stmt(stmt, self),
let (mac, style, attrs) = if let StmtKind::Mac(mac) = stmt.node {
mac.unwrap()
} else {
// The placeholder expander gives ids to statements, so we avoid folding the id here.
let ast::Stmt { id, node, span } = stmt;
return noop_fold_stmt_kind(node, self).into_iter().map(|node| {
ast::Stmt { id: id, node: node, span: span }
}).collect()
};
let mut placeholder =
@ -574,11 +568,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
}
fn fold_block(&mut self, block: P<Block>) -> P<Block> {
let paths = self.cx.syntax_env.paths();
let module = self.cx.syntax_env.add_module(false, true, paths);
let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module);
let orig_in_block = mem::replace(&mut self.cx.current_expansion.in_block, true);
let result = noop_fold_block(block, self);
self.cx.syntax_env.current_module = orig_module;
self.cx.current_expansion.in_block = orig_in_block;
result
}
@ -613,8 +605,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
})
}
ast::ItemKind::Mod(ast::Mod { inner, .. }) => {
let mut paths = (*self.cx.syntax_env.paths()).clone();
paths.mod_path.push(item.ident);
if item.ident == keywords::Invalid.ident() {
return noop_fold_item(item, self);
}
let mut module = (*self.cx.current_expansion.module).clone();
module.mod_path.push(item.ident);
// Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`).
// In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`).
@ -622,29 +618,27 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP;
if inline_module {
paths.directory.push(&*{
module.directory.push(&*{
::attr::first_attr_value_str_by_name(&item.attrs, "path")
.unwrap_or(item.ident.name.as_str())
});
} else {
paths.directory =
module.directory =
PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner));
paths.directory.pop();
module.directory.pop();
}
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));
let module = mem::replace(&mut self.cx.syntax_env.current_module, module);
let orig_module =
mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
let result = noop_fold_item(item, self);
self.cx.syntax_env.current_module = module;
result
},
self.cx.current_expansion.module = orig_module;
return result;
}
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) {
let is_crate_root = self.cx.current_expansion.module.mod_path.len() == 1;
for def in self.cx.resolver.load_crate(&*item, is_crate_root) {
match def {
LoadedMacro::Def(def) => self.cx.insert_macro(def),
LoadedMacro::CustomDerive(name, ext) => {
@ -652,7 +646,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
}
}
}
SmallVector::one(item)
noop_fold_item(item, self)
},
_ => noop_fold_item(item, self),
}
@ -715,6 +709,15 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
noop_fold_item_kind(self.cfg.configure_item_kind(item), self)
}
fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId {
if self.monotonic {
assert_eq!(id, ast::DUMMY_NODE_ID);
self.cx.resolver.next_node_id()
} else {
id
}
}
}
pub struct ExpansionConfig<'feat> {
@ -766,7 +769,7 @@ pub fn expand_crate(cx: &mut ExtCtxt,
user_exts: Vec<NamedSyntaxExtension>,
c: Crate) -> Crate {
cx.initialize(user_exts, &c);
cx.expander().expand_crate(c)
cx.monotonic_expander().expand_crate(c)
}
// Expands crate using supplied MacroExpander - allows for
@ -803,110 +806,3 @@ impl Folder for Marker {
fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec<TokenTree> {
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
}
#[cfg(test)]
mod tests {
use super::{expand_crate, ExpansionConfig};
use ast;
use ext::base::{ExtCtxt, DummyMacroLoader};
use parse;
use util::parser_testing::{string_to_parser};
use visit;
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)
#[derive(Clone)]
struct PathExprFinderContext {
path_accumulator: Vec<ast::Path> ,
}
impl Visitor for PathExprFinderContext {
fn visit_expr(&mut self, expr: &ast::Expr) {
if let ast::ExprKind::Path(None, ref p) = expr.node {
self.path_accumulator.push(p.clone());
}
visit::walk_expr(self, expr);
}
}
// these following tests are quite fragile, in that they don't test what
// *kind* of failure occurs.
fn test_ecfg() -> ExpansionConfig<'static> {
ExpansionConfig::default("test".to_string())
}
// make sure that macros can't escape fns
#[should_panic]
#[test] fn macros_cant_escape_fns_test () {
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
fn inty() -> i32 { z!() }".to_string();
let sess = parse::ParseSess::new();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
src,
Vec::new(), &sess).unwrap();
// should fail:
let mut loader = DummyMacroLoader;
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
expand_crate(&mut ecx, vec![], crate_ast);
}
// make sure that macros can't escape modules
#[should_panic]
#[test] fn macros_cant_escape_mods_test () {
let src = "mod foo {macro_rules! z (() => (3+4));}\
fn inty() -> i32 { z!() }".to_string();
let sess = parse::ParseSess::new();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
src,
Vec::new(), &sess).unwrap();
let mut loader = DummyMacroLoader;
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
expand_crate(&mut ecx, vec![], crate_ast);
}
// macro_use modules should allow macros to escape
#[test] fn macros_can_escape_flattened_mods_test () {
let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
fn inty() -> i32 { z!() }".to_string();
let sess = parse::ParseSess::new();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
src,
Vec::new(), &sess).unwrap();
let mut loader = DummyMacroLoader;
let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
expand_crate(&mut ecx, vec![], crate_ast);
}
fn expand_crate_str(crate_str: String) -> ast::Crate {
let ps = parse::ParseSess::new();
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
// the cfg argument actually does matter, here...
let mut loader = DummyMacroLoader;
let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
expand_crate(&mut ecx, vec![], crate_ast)
}
#[test] fn macro_tokens_should_match(){
expand_crate_str(
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
}
// should be able to use a bound identifier as a literal in a macro definition:
#[test] fn self_macro_parsing(){
expand_crate_str(
"macro_rules! foo ((zz) => (287;));
fn f(zz: i32) {foo!(zz);}".to_string()
);
}
// create a really evil test case where a $x appears inside a binding of $x
// but *shouldn't* bind because it was inserted by a different macro....
// can't write this test case until we have macro-generating macros.
}

@ -29,7 +29,7 @@ pub struct SyntaxContextData {
pub prev_ctxt: SyntaxContext,
}
/// A mark represents a unique id associated with a macro expansion.
/// A mark is a unique id associated with a macro expansion.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct Mark(u32);
@ -41,6 +41,11 @@ impl Mark {
})
}
/// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST.
pub fn root() -> Self {
Mark(0)
}
pub fn as_u32(&self) -> u32 {
self.0
}
@ -56,8 +61,8 @@ impl HygieneData {
fn new() -> Self {
HygieneData {
syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark(0), // the null mark
prev_ctxt: SyntaxContext(0), // the empty context
outer_mark: Mark::root(),
prev_ctxt: SyntaxContext::empty(),
}],
markings: HashMap::new(),
next_mark: Mark(1),

@ -10,13 +10,16 @@
use ast;
use codemap::{DUMMY_SP, dummy_spanned};
use ext::base::ExtCtxt;
use ext::expand::{Expansion, ExpansionKind};
use fold::*;
use parse::token::keywords;
use ptr::P;
use util::move_map::MoveMap;
use util::small_vector::SmallVector;
use std::collections::HashMap;
use std::mem;
pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion {
fn mac_placeholder() -> ast::Mac {
@ -69,14 +72,18 @@ pub fn macro_scope_placeholder() -> Expansion {
placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
}
pub struct PlaceholderExpander {
pub struct PlaceholderExpander<'a, 'b: 'a> {
expansions: HashMap<ast::NodeId, Expansion>,
cx: &'a mut ExtCtxt<'b>,
monotonic: bool,
}
impl PlaceholderExpander {
pub fn new() -> Self {
impl<'a, 'b> PlaceholderExpander<'a, 'b> {
pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
PlaceholderExpander {
cx: cx,
expansions: HashMap::new(),
monotonic: monotonic,
}
}
@ -89,7 +96,7 @@ impl PlaceholderExpander {
}
}
impl Folder for PlaceholderExpander {
impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> {
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
match item.node {
// Scope placeholder
@ -155,6 +162,56 @@ impl Folder for PlaceholderExpander {
_ => noop_fold_ty(ty, self),
}
}
fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
noop_fold_block(block, self).map(|mut block| {
let mut macros = Vec::new();
let mut remaining_stmts = block.stmts.len();
block.stmts = block.stmts.move_flat_map(|mut stmt| {
remaining_stmts -= 1;
// Scope placeholder
if let ast::StmtKind::Item(ref item) = stmt.node {
if let ast::ItemKind::Mac(..) = item.node {
macros.push(item.ident.ctxt.data().outer_mark);
return None;
}
}
match stmt.node {
// Avoid wasting a node id on a trailing expression statement,
// which shares a HIR node with the expression itself.
ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id,
_ if self.monotonic => {
assert_eq!(stmt.id, ast::DUMMY_NODE_ID);
stmt.id = self.cx.resolver.next_node_id();
}
_ => {}
}
if self.monotonic && !macros.is_empty() {
let macros = mem::replace(&mut macros, Vec::new());
self.cx.resolver.add_expansions_at_stmt(stmt.id, macros);
}
Some(stmt)
});
block
})
}
fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
let mut module = noop_fold_mod(module, self);
module.items = module.items.move_flat_map(|item| match item.node {
ast::ItemKind::Mac(_) => None, // remove scope placeholders from modules
_ => Some(item),
});
module
}
}
pub fn reconstructed_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion {

@ -74,8 +74,8 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTre
pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
-> Box<base::MacResult+'static> {
base::check_zero_tts(cx, sp, tts, "module_path!");
let paths = cx.syntax_env.paths();
let string = paths.mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
let mod_path = &cx.current_expansion.module.mod_path;
let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
base::MacEager::expr(cx.expr_str(
sp,

@ -211,8 +211,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
imported_from,
rhs);
let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr));
p.directory = cx.syntax_env.paths().directory.clone();
p.restrictions = match cx.syntax_env.in_block() {
p.directory = cx.current_expansion.module.directory.clone();
p.restrictions = match cx.current_expansion.in_block {
true => Restrictions::NO_NONINLINE_MOD,
false => Restrictions::empty(),
};

@ -1320,51 +1320,27 @@ pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Exp
es.move_flat_map(|e| folder.fold_opt_expr(e))
}
pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T)
-> SmallVector<Stmt> {
pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T) -> SmallVector<Stmt> {
let id = folder.new_id(id);
let span = folder.new_span(span);
noop_fold_stmt_kind(node, folder).into_iter().map(|node| {
Stmt { id: id, node: node, span: span }
}).collect()
}
pub fn noop_fold_stmt_kind<T: Folder>(node: StmtKind, folder: &mut T) -> SmallVector<StmtKind> {
match node {
StmtKind::Local(local) => SmallVector::one(Stmt {
id: id,
node: StmtKind::Local(folder.fold_local(local)),
span: span,
}),
StmtKind::Item(item) => folder.fold_item(item).into_iter().map(|item| Stmt {
id: id,
node: StmtKind::Item(item),
span: span,
}).collect(),
StmtKind::Local(local) => SmallVector::one(StmtKind::Local(folder.fold_local(local))),
StmtKind::Item(item) => folder.fold_item(item).into_iter().map(StmtKind::Item).collect(),
StmtKind::Expr(expr) => {
if let Some(expr) = folder.fold_opt_expr(expr) {
SmallVector::one(Stmt {
id: id,
node: StmtKind::Expr(expr),
span: span,
})
} else {
SmallVector::zero()
}
folder.fold_opt_expr(expr).into_iter().map(StmtKind::Expr).collect()
}
StmtKind::Semi(expr) => {
if let Some(expr) = folder.fold_opt_expr(expr) {
SmallVector::one(Stmt {
id: id,
node: StmtKind::Semi(expr),
span: span,
})
} else {
SmallVector::zero()
}
folder.fold_opt_expr(expr).into_iter().map(StmtKind::Semi).collect()
}
StmtKind::Mac(mac) => SmallVector::one(Stmt {
id: id,
node: StmtKind::Mac(mac.map(|(mac, semi, attrs)| {
(folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into())
})),
span: span,
})
StmtKind::Mac(mac) => SmallVector::one(StmtKind::Mac(mac.map(|(mac, semi, attrs)| {
(folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into())
}))),
}
}

@ -28,7 +28,7 @@ use errors;
use errors::snippet::{SnippetData};
use config;
use entry::{self, EntryPointType};
use ext::base::{ExtCtxt, DummyMacroLoader};
use ext::base::{ExtCtxt, Resolver};
use ext::build::AstBuilder;
use ext::expand::ExpansionConfig;
use fold::Folder;
@ -70,6 +70,7 @@ struct TestCtxt<'a> {
// Traverse the crate, collecting all the test functions, eliding any
// existing main functions, and synthesizing a main test harness
pub fn modify_for_testing(sess: &ParseSess,
resolver: &mut Resolver,
should_test: bool,
krate: ast::Crate,
span_diagnostic: &errors::Handler) -> ast::Crate {
@ -82,7 +83,7 @@ pub fn modify_for_testing(sess: &ParseSess,
"reexport_test_harness_main");
if should_test {
generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic)
generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic)
} else {
krate
}
@ -248,27 +249,28 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
}).chain(tested_submods.into_iter().map(|(r, sym)| {
let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]);
cx.ext_cx.item_use_simple_(DUMMY_SP, ast::Visibility::Public, r, path)
}));
})).collect();
let reexport_mod = ast::Mod {
inner: DUMMY_SP,
items: items.collect(),
items: items,
};
let sym = token::gensym_ident("__test_reexports");
let it = P(ast::Item {
let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item {
ident: sym.clone(),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: ast::ItemKind::Mod(reexport_mod),
vis: ast::Visibility::Public,
span: DUMMY_SP,
});
})).pop().unwrap();
(it, sym)
}
fn generate_test_harness(sess: &ParseSess,
resolver: &mut Resolver,
reexport_test_harness_main: Option<InternedString>,
krate: ast::Crate,
sd: &errors::Handler) -> ast::Crate {
@ -276,13 +278,10 @@ fn generate_test_harness(sess: &ParseSess,
let mut cleaner = EntryPointCleaner { depth: 0 };
let krate = cleaner.fold_crate(krate);
let mut loader = DummyMacroLoader;
let mut cx: TestCtxt = TestCtxt {
sess: sess,
span_diagnostic: sd,
ext_cx: ExtCtxt::new(sess, vec![],
ExpansionConfig::default("test".to_string()),
&mut loader),
ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), resolver),
path: Vec::new(),
testfns: Vec::new(),
reexport_test_harness_main: reexport_test_harness_main,
@ -511,16 +510,17 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<P<ast::Item>>) {
items: vec![import, mainfn, tests],
};
let item_ = ast::ItemKind::Mod(testmod);
let mod_ident = token::gensym_ident("__test");
let item = P(ast::Item {
let mut expander = cx.ext_cx.monotonic_expander();
let item = expander.fold_item(P(ast::Item {
id: ast::DUMMY_NODE_ID,
ident: mod_ident,
attrs: vec![],
node: item_,
vis: ast::Visibility::Public,
span: DUMMY_SP,
});
})).pop().unwrap();
let reexport = cx.reexport_test_harness_main.as_ref().map(|s| {
// building `use <ident> = __test::main`
let reexport_ident = token::str_to_ident(&s);
@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<P<ast::Item>>) {
nospan(ast::ViewPathSimple(reexport_ident,
path_node(vec![mod_ident, token::str_to_ident("main")])));
P(ast::Item {
expander.fold_item(P(ast::Item {
id: ast::DUMMY_NODE_ID,
ident: keywords::Invalid.ident(),
attrs: vec![],
node: ast::ItemKind::Use(P(use_path)),
vis: ast::Visibility::Inherited,
span: DUMMY_SP
})
})).pop().unwrap()
});
debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item));

@ -11,8 +11,7 @@
//! The compiler code necessary to implement the `#[derive]` extensions.
use syntax::ast::{self, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv};
use syntax::ext::base::MultiModifier;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::feature_gate;
use syntax::codemap;
@ -89,7 +88,7 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
}
}
fn expand_derive(cx: &mut ExtCtxt,
pub fn expand_derive(cx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
annotatable: Annotatable)
@ -243,10 +242,6 @@ fn expand_derive(cx: &mut ExtCtxt,
macro_rules! derive_traits {
($( $name:expr => $func:path, )+) => {
pub fn register_all(env: &mut SyntaxEnv) {
env.insert(intern("derive"), MultiModifier(Box::new(expand_derive)));
}
pub fn is_builtin_trait(name: &str) -> bool {
match name {
$( $name )|+ => true,

@ -17,7 +17,6 @@ use syntax::ast;
use syntax::ext::base::*;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::parse::token::{self, keywords};
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
@ -702,10 +701,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect();
let macsp = ecx.call_site();
// Expand the format literal so that efmt.span will have a backtrace. This
// is essential for locating a bug when the format literal is generated in
// a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
let efmt = ecx.expander().fold_expr(efmt);
let msg = "format argument must be a string literal.";
let fmt = match expr_to_spanned_string(ecx, efmt, msg) {
Some(fmt) => fmt,
None => return DummyResult::raw_expr(sp),
};
let mut cx = Context {
ecx: ecx,
args: args,
@ -723,14 +724,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt,
str_pieces: Vec::new(),
all_pieces_simple: true,
macsp: macsp,
fmtsp: efmt.span,
};
let fmt = match expr_to_string(cx.ecx, efmt, "format argument must be a string literal.") {
Some((fmt, _)) => fmt,
None => return DummyResult::raw_expr(sp),
fmtsp: fmt.span,
};
let mut parser = parse::Parser::new(&fmt);
let mut parser = parse::Parser::new(&fmt.node.0);
let mut pieces = vec![];
loop {

@ -34,11 +34,6 @@ extern crate syntax_pos;
extern crate rustc_macro;
extern crate rustc_errors as errors;
use syntax::ext::base::{MacroExpanderFn, NormalTT};
use syntax::ext::base::{SyntaxEnv, SyntaxExtension};
use syntax::parse::token::intern;
mod asm;
mod cfg;
mod concat;
@ -53,28 +48,67 @@ pub mod rustc_macro_registrar;
// for custom_derive
pub mod deriving;
pub fn register_builtins(env: &mut SyntaxEnv) {
// utility function to simplify creating NormalTT syntax extensions
fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension {
NormalTT(Box::new(f), None, false)
use std::rc::Rc;
use syntax::ast;
use syntax::ext::base::{MacroExpanderFn, MacroRulesTT, NormalTT, MultiModifier};
use syntax::ext::hygiene::Mark;
use syntax::parse::token::intern;
pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) {
let mut register = |name, ext| {
resolver.add_macro(Mark::root(), ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext));
};
register("macro_rules", MacroRulesTT);
macro_rules! register {
($( $name:ident: $f:expr, )*) => { $(
register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false));
)* }
}
env.insert(intern("asm"), builtin_normal_expander(asm::expand_asm));
env.insert(intern("cfg"), builtin_normal_expander(cfg::expand_cfg));
env.insert(intern("concat"),
builtin_normal_expander(concat::expand_syntax_ext));
env.insert(intern("concat_idents"),
builtin_normal_expander(concat_idents::expand_syntax_ext));
env.insert(intern("env"), builtin_normal_expander(env::expand_env));
env.insert(intern("option_env"),
builtin_normal_expander(env::expand_option_env));
env.insert(intern("format_args"),
// format_args uses `unstable` things internally.
NormalTT(Box::new(format::expand_format_args), None, true));
env.insert(intern("log_syntax"),
builtin_normal_expander(log_syntax::expand_syntax_ext));
env.insert(intern("trace_macros"),
builtin_normal_expander(trace_macros::expand_trace_macros));
if enable_quotes {
use syntax::ext::quote::*;
register! {
quote_tokens: expand_quote_tokens,
quote_expr: expand_quote_expr,
quote_ty: expand_quote_ty,
quote_item: expand_quote_item,
quote_pat: expand_quote_pat,
quote_arm: expand_quote_arm,
quote_stmt: expand_quote_stmt,
quote_matcher: expand_quote_matcher,
quote_attr: expand_quote_attr,
quote_arg: expand_quote_arg,
quote_block: expand_quote_block,
quote_meta_item: expand_quote_meta_item,
quote_path: expand_quote_path,
}
}
deriving::register_all(env);
use syntax::ext::source_util::*;
register! {
line: expand_line,
column: expand_column,
file: expand_file,
stringify: expand_stringify,
include: expand_include,
include_str: expand_include_str,
include_bytes: expand_include_bytes,
module_path: expand_mod,
asm: asm::expand_asm,
cfg: cfg::expand_cfg,
concat: concat::expand_syntax_ext,
concat_idents: concat_idents::expand_syntax_ext,
env: env::expand_env,
option_env: env::expand_option_env,
log_syntax: log_syntax::expand_syntax_ext,
trace_macros: trace_macros::expand_trace_macros,
}
// format_args uses `unstable` things internally.
register("format_args", NormalTT(Box::new(format::expand_format_args), None, true));
register("derive", MultiModifier(Box::new(deriving::expand_derive)));
}

@ -13,12 +13,13 @@ use std::mem;
use errors;
use syntax::ast::{self, Ident, NodeId};
use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
use syntax::ext::base::{ExtCtxt, DummyMacroLoader};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::parse::ParseSess;
use syntax::parse::token::{self, InternedString};
use syntax::feature_gate::Features;
use syntax::fold::Folder;
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
use syntax::visit::{self, Visitor};
@ -39,16 +40,14 @@ struct CollectCustomDerives<'a> {
}
pub fn modify(sess: &ParseSess,
resolver: &mut ::syntax::ext::base::Resolver,
mut krate: ast::Crate,
is_rustc_macro_crate: bool,
num_crate_types: usize,
handler: &errors::Handler,
features: &Features) -> ast::Crate {
let mut loader = DummyMacroLoader;
let mut cx = ExtCtxt::new(sess,
Vec::new(),
ExpansionConfig::default("rustc_macro".to_string()),
&mut loader);
let ecfg = ExpansionConfig::default("rustc_macro".to_string());
let mut cx = ExtCtxt::new(sess, Vec::new(), ecfg, resolver);
let mut collect = CollectCustomDerives {
derives: Vec::new(),
@ -268,13 +267,11 @@ fn mk_registrar(cx: &mut ExtCtxt,
i.vis = ast::Visibility::Public;
i
});
let module = cx.item_mod(span,
span,
ast::Ident::with_empty_ctxt(token::gensym("registrar")),
Vec::new(),
vec![krate, func]);
module.map(|mut i| {
let ident = ast::Ident::with_empty_ctxt(token::gensym("registrar"));
let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| {
i.vis = ast::Visibility::Public;
i
})
});
cx.monotonic_expander().fold_item(module).pop().unwrap()
}

@ -22,11 +22,11 @@ use syntax_pos::DUMMY_SP;
fn main() {
let ps = syntax::parse::ParseSess::new();
let mut loader = syntax::ext::base::DummyMacroLoader;
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
&mut loader);
&mut resolver);
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {

@ -0,0 +1,46 @@
// Copyright 2016 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.
mod macros_cant_escape_fns {
fn f() {
macro_rules! m { () => { 3 + 4 } }
}
fn g() -> i32 { m!() } //~ ERROR macro undefined
}
mod macros_cant_escape_mods {
mod f {
macro_rules! m { () => { 3 + 4 } }
}
fn g() -> i32 { m!() } //~ ERROR macro undefined
}
mod macros_can_escape_flattened_mods_test {
#[macro_use]
mod f {
macro_rules! m { () => { 3 + 4 } }
}
fn g() -> i32 { m!() }
}
fn macro_tokens_should_match() {
macro_rules! m { (a) => { 13 } }
m!(a);
}
// should be able to use a bound identifier as a literal in a macro definition:
fn self_macro_parsing() {
macro_rules! foo { (zz) => { 287; } }
fn f(zz: i32) {
foo!(zz);
}
}
fn main() {}

@ -25,11 +25,11 @@ use syntax_pos::DUMMY_SP;
fn main() {
let ps = syntax::parse::ParseSess::new();
let mut loader = syntax::ext::base::DummyMacroLoader;
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
&mut loader);
&mut resolver);
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {

@ -21,11 +21,11 @@ use syntax_pos::DUMMY_SP;
fn main() {
let ps = syntax::parse::ParseSess::new();
let mut loader = syntax::ext::base::DummyMacroLoader;
let mut resolver = syntax::ext::base::DummyResolver;
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
&mut loader);
&mut resolver);
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {