Move macro resolution into librustc_resolve.
This commit is contained in:
@@ -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, 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,15 @@ 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)
|
||||
}
|
||||
|
||||
let mut syntax_expanders = SyntaxEnv::new();
|
||||
syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
|
||||
|
||||
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 Resolver {
|
||||
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool)
|
||||
-> Vec<LoadedMacro>;
|
||||
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
|
||||
|
||||
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion);
|
||||
fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>);
|
||||
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
|
||||
|
||||
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
|
||||
fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>>;
|
||||
}
|
||||
|
||||
pub enum LoadedMacro {
|
||||
@@ -558,9 +480,31 @@ pub enum LoadedMacro {
|
||||
|
||||
pub struct DummyResolver;
|
||||
impl Resolver for DummyResolver {
|
||||
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<LoadedMacro> {
|
||||
fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec<LoadedMacro> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
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,16 +513,12 @@ impl Resolver for DummyResolver {
|
||||
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 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> {
|
||||
@@ -587,16 +527,20 @@ impl<'a> ExtCtxt<'a> {
|
||||
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(),
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,23 +553,22 @@ impl<'a> ExtCtxt<'a> {
|
||||
-> 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 +589,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 +610,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 +625,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 +704,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,16 +714,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,7 +734,7 @@ pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &st
|
||||
-> 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
|
||||
});
|
||||
|
||||
@@ -884,104 +809,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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user