Skip to content

Commit 51499b6

Browse files
committed
Load macros from extern crates during expansion.
1 parent 0d531bf commit 51499b6

File tree

8 files changed

+73
-90
lines changed

8 files changed

+73
-90
lines changed

src/librustc_driver/driver.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -604,10 +604,6 @@ pub fn phase_2_configure_and_expand<'a>(sess: &Session,
604604
syntax::std_inject::maybe_inject_crates_ref(krate, sess.opts.alt_std_name.clone())
605605
});
606606

607-
let macros = time(time_passes,
608-
"macro loading",
609-
|| macro_import::read_macro_defs(sess, &cstore, &krate, crate_name));
610-
611607
let mut addl_plugins = Some(addl_plugins);
612608
let registrars = time(time_passes, "plugin loading", || {
613609
plugin::load::load_plugins(sess,
@@ -696,13 +692,14 @@ pub fn phase_2_configure_and_expand<'a>(sess: &Session,
696692
recursion_limit: sess.recursion_limit.get(),
697693
trace_mac: sess.opts.debugging_opts.trace_macros,
698694
};
695+
let mut loader = macro_import::MacroLoader::new(sess, &cstore, crate_name);
699696
let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
700697
krate.config.clone(),
701698
cfg,
702-
&mut feature_gated_cfgs);
699+
&mut feature_gated_cfgs,
700+
&mut loader);
703701
syntax_ext::register_builtins(&mut ecx.syntax_env);
704702
let (ret, macro_names) = syntax::ext::expand::expand_crate(ecx,
705-
macros,
706703
syntax_exts,
707704
krate);
708705
if cfg!(windows) {

src/librustc_metadata/macro_import.rs

+18-57
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,19 @@ use syntax::codemap::Span;
2020
use syntax::parse::token;
2121
use syntax::ast;
2222
use syntax::attr;
23-
use syntax::visit;
24-
use syntax::visit::Visitor;
2523
use syntax::attr::AttrMetaMethods;
24+
use syntax::ext;
2625

27-
struct MacroLoader<'a> {
26+
pub struct MacroLoader<'a> {
2827
sess: &'a Session,
29-
span_whitelist: HashSet<Span>,
3028
reader: CrateReader<'a>,
31-
macros: Vec<ast::MacroDef>,
3229
}
3330

3431
impl<'a> MacroLoader<'a> {
35-
fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
32+
pub fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
3633
MacroLoader {
3734
sess: sess,
38-
span_whitelist: HashSet::new(),
3935
reader: CrateReader::new(sess, cstore, crate_name),
40-
macros: vec![],
4136
}
4237
}
4338
}
@@ -46,48 +41,15 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
4641
span_err!(a, b, E0467, "bad macro reexport");
4742
}
4843

49-
/// Read exported macros.
50-
pub fn read_macro_defs(sess: &Session,
51-
cstore: &CStore,
52-
krate: &ast::Crate,
53-
crate_name: &str)
54-
-> Vec<ast::MacroDef>
55-
{
56-
let mut loader = MacroLoader::new(sess, cstore, crate_name);
57-
58-
// We need to error on `#[macro_use] extern crate` when it isn't at the
59-
// crate root, because `$crate` won't work properly. Identify these by
60-
// spans, because the crate map isn't set up yet.
61-
for item in &krate.module.items {
62-
if let ast::ItemKind::ExternCrate(_) = item.node {
63-
loader.span_whitelist.insert(item.span);
64-
}
65-
}
66-
67-
visit::walk_crate(&mut loader, krate);
68-
69-
loader.macros
70-
}
71-
7244
pub type MacroSelection = HashMap<token::InternedString, Span>;
7345

74-
// note that macros aren't expanded yet, and therefore macros can't add macro imports.
75-
impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
76-
fn visit_item(&mut self, item: &ast::Item) {
77-
// We're only interested in `extern crate`.
78-
match item.node {
79-
ast::ItemKind::ExternCrate(_) => {}
80-
_ => {
81-
visit::walk_item(self, item);
82-
return;
83-
}
84-
}
85-
46+
impl<'a> ext::base::MacroLoader for MacroLoader<'a> {
47+
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef> {
8648
// Parse the attributes relating to macros.
8749
let mut import = Some(HashMap::new()); // None => load all
8850
let mut reexport = HashMap::new();
8951

90-
for attr in &item.attrs {
52+
for attr in &extern_crate.attrs {
9153
let mut used = true;
9254
match &attr.name()[..] {
9355
"macro_use" => {
@@ -130,36 +92,33 @@ impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
13092
}
13193
}
13294

133-
self.load_macros(item, import, reexport)
134-
}
135-
136-
fn visit_mac(&mut self, _: &ast::Mac) {
137-
// bummer... can't see macro imports inside macros.
138-
// do nothing.
95+
self.load_macros(extern_crate, allows_macros, import, reexport)
13996
}
14097
}
14198

14299
impl<'a> MacroLoader<'a> {
143100
fn load_macros<'b>(&mut self,
144101
vi: &ast::Item,
102+
allows_macros: bool,
145103
import: Option<MacroSelection>,
146-
reexport: MacroSelection) {
104+
reexport: MacroSelection)
105+
-> Vec<ast::MacroDef> {
147106
if let Some(sel) = import.as_ref() {
148107
if sel.is_empty() && reexport.is_empty() {
149-
return;
108+
return Vec::new();
150109
}
151110
}
152111

153-
if !self.span_whitelist.contains(&vi.span) {
112+
if !allows_macros {
154113
span_err!(self.sess, vi.span, E0468,
155114
"an `extern crate` loading macros must be at the crate root");
156-
return;
115+
return Vec::new();
157116
}
158117

159-
let macros = self.reader.read_exported_macros(vi);
118+
let mut macros = Vec::new();
160119
let mut seen = HashSet::new();
161120

162-
for mut def in macros {
121+
for mut def in self.reader.read_exported_macros(vi) {
163122
let name = def.ident.name.as_str();
164123

165124
def.use_locally = match import.as_ref() {
@@ -170,7 +129,7 @@ impl<'a> MacroLoader<'a> {
170129
def.allow_internal_unstable = attr::contains_name(&def.attrs,
171130
"allow_internal_unstable");
172131
debug!("load_macros: loaded: {:?}", def);
173-
self.macros.push(def);
132+
macros.push(def);
174133
seen.insert(name);
175134
}
176135

@@ -189,5 +148,7 @@ impl<'a> MacroLoader<'a> {
189148
"reexported macro not found");
190149
}
191150
}
151+
152+
macros
192153
}
193154
}

src/libsyntax/ext/base.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,17 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
536536
syntax_expanders
537537
}
538538

539+
pub trait MacroLoader {
540+
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef>;
541+
}
542+
543+
pub struct DummyMacroLoader;
544+
impl MacroLoader for DummyMacroLoader {
545+
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<ast::MacroDef> {
546+
Vec::new()
547+
}
548+
}
549+
539550
/// One of these is made during expansion and incrementally updated as we go;
540551
/// when a macro expansion occurs, the resulting nodes have the backtrace()
541552
/// -> expn_info of their expansion context stored into their span.
@@ -546,6 +557,7 @@ pub struct ExtCtxt<'a> {
546557
pub ecfg: expand::ExpansionConfig<'a>,
547558
pub crate_root: Option<&'static str>,
548559
pub feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>,
560+
pub loader: &'a mut MacroLoader,
549561

550562
pub mod_path: Vec<ast::Ident> ,
551563
pub exported_macros: Vec<ast::MacroDef>,
@@ -561,7 +573,9 @@ pub struct ExtCtxt<'a> {
561573
impl<'a> ExtCtxt<'a> {
562574
pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
563575
ecfg: expand::ExpansionConfig<'a>,
564-
feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>) -> ExtCtxt<'a> {
576+
feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>,
577+
loader: &'a mut MacroLoader)
578+
-> ExtCtxt<'a> {
565579
let env = initial_syntax_expander_table(&ecfg);
566580
ExtCtxt {
567581
parse_sess: parse_sess,
@@ -572,6 +586,7 @@ impl<'a> ExtCtxt<'a> {
572586
crate_root: None,
573587
feature_gated_cfgs: feature_gated_cfgs,
574588
exported_macros: Vec::new(),
589+
loader: loader,
575590
syntax_env: env,
576591
recursion_count: 0,
577592

@@ -925,4 +940,8 @@ impl SyntaxEnv {
925940
let last_chain_index = self.chain.len() - 1;
926941
&mut self.chain[last_chain_index].info
927942
}
943+
944+
pub fn is_crate_root(&mut self) -> bool {
945+
self.chain.len() == 2
946+
}
928947
}

src/libsyntax/ext/expand.rs

+22-19
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,15 @@ fn expand_annotatable(a: Annotatable,
750750
}
751751
result.into_iter().map(|i| Annotatable::Item(i)).collect()
752752
},
753+
ast::ItemKind::ExternCrate(_) => {
754+
// We need to error on `#[macro_use] extern crate` when it isn't at the
755+
// crate root, because `$crate` won't work properly.
756+
let allows_macros = fld.cx.syntax_env.is_crate_root();
757+
for def in fld.cx.loader.load_crate(&it, allows_macros) {
758+
fld.cx.insert_macro(def);
759+
}
760+
SmallVector::one(Annotatable::Item(it))
761+
},
753762
_ => noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect(),
754763
},
755764

@@ -1137,8 +1146,6 @@ impl<'feat> ExpansionConfig<'feat> {
11371146
}
11381147

11391148
pub fn expand_crate(mut cx: ExtCtxt,
1140-
// these are the macros being imported to this crate:
1141-
imported_macros: Vec<ast::MacroDef>,
11421149
user_exts: Vec<NamedSyntaxExtension>,
11431150
c: Crate) -> (Crate, HashSet<Name>) {
11441151
if std_inject::no_core(&c) {
@@ -1151,10 +1158,6 @@ pub fn expand_crate(mut cx: ExtCtxt,
11511158
let ret = {
11521159
let mut expander = MacroExpander::new(&mut cx);
11531160

1154-
for def in imported_macros {
1155-
expander.cx.insert_macro(def);
1156-
}
1157-
11581161
for (name, extension) in user_exts {
11591162
expander.cx.syntax_env.insert(name, extension);
11601163
}
@@ -1220,7 +1223,7 @@ mod tests {
12201223
use ast;
12211224
use ast::Name;
12221225
use codemap;
1223-
use ext::base::ExtCtxt;
1226+
use ext::base::{ExtCtxt, DummyMacroLoader};
12241227
use ext::mtwt;
12251228
use fold::Folder;
12261229
use parse;
@@ -1291,9 +1294,9 @@ mod tests {
12911294
src,
12921295
Vec::new(), &sess).unwrap();
12931296
// should fail:
1294-
let mut gated_cfgs = vec![];
1295-
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
1296-
expand_crate(ecx, vec![], vec![], crate_ast);
1297+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1298+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1299+
expand_crate(ecx, vec![], crate_ast);
12971300
}
12981301

12991302
// make sure that macros can't escape modules
@@ -1306,9 +1309,9 @@ mod tests {
13061309
"<test>".to_string(),
13071310
src,
13081311
Vec::new(), &sess).unwrap();
1309-
let mut gated_cfgs = vec![];
1310-
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
1311-
expand_crate(ecx, vec![], vec![], crate_ast);
1312+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1313+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1314+
expand_crate(ecx, vec![], crate_ast);
13121315
}
13131316

13141317
// macro_use modules should allow macros to escape
@@ -1320,18 +1323,18 @@ mod tests {
13201323
"<test>".to_string(),
13211324
src,
13221325
Vec::new(), &sess).unwrap();
1323-
let mut gated_cfgs = vec![];
1324-
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
1325-
expand_crate(ecx, vec![], vec![], crate_ast);
1326+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1327+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1328+
expand_crate(ecx, vec![], crate_ast);
13261329
}
13271330

13281331
fn expand_crate_str(crate_str: String) -> ast::Crate {
13291332
let ps = parse::ParseSess::new();
13301333
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
13311334
// the cfg argument actually does matter, here...
1332-
let mut gated_cfgs = vec![];
1333-
let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut gated_cfgs);
1334-
expand_crate(ecx, vec![], vec![], crate_ast).0
1335+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1336+
let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1337+
expand_crate(ecx, vec![], crate_ast).0
13351338
}
13361339

13371340
// find the pat_ident paths in a crate

src/libsyntax/test.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use codemap;
2525
use errors;
2626
use config;
2727
use entry::{self, EntryPointType};
28-
use ext::base::ExtCtxt;
28+
use ext::base::{ExtCtxt, DummyMacroLoader};
2929
use ext::build::AstBuilder;
3030
use ext::expand::ExpansionConfig;
3131
use fold::Folder;
@@ -271,12 +271,14 @@ fn generate_test_harness(sess: &ParseSess,
271271
let krate = cleaner.fold_crate(krate);
272272

273273
let mut feature_gated_cfgs = vec![];
274+
let mut loader = DummyMacroLoader;
274275
let mut cx: TestCtxt = TestCtxt {
275276
sess: sess,
276277
span_diagnostic: sd,
277278
ext_cx: ExtCtxt::new(sess, vec![],
278279
ExpansionConfig::default("test".to_string()),
279-
&mut feature_gated_cfgs),
280+
&mut feature_gated_cfgs,
281+
&mut loader),
280282
path: Vec::new(),
281283
testfns: Vec::new(),
282284
reexport_test_harness_main: reexport_test_harness_main,

src/test/compile-fail-fulldeps/qquote.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ use syntax::print::pprust;
2121

2222
fn main() {
2323
let ps = syntax::parse::ParseSess::new();
24+
let mut loader = syntax::ext::base::DummyMacroLoader;
2425
let mut cx = syntax::ext::base::ExtCtxt::new(
2526
&ps, vec![],
2627
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
27-
&mut Vec::new());
28+
&mut Vec::new(), &mut loader);
2829
cx.bt_push(syntax::codemap::ExpnInfo {
2930
call_site: DUMMY_SP,
3031
callee: syntax::codemap::NameAndSpan {

src/test/run-fail-fulldeps/qquote.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ use syntax::print::pprust;
2323

2424
fn main() {
2525
let ps = syntax::parse::ParseSess::new();
26-
let mut feature_gated_cfgs = vec![];
26+
let (mut feature_gated_cfgs, mut loader) = (vec![], syntax::ext::base::DummyMacroLoader);
2727
let mut cx = syntax::ext::base::ExtCtxt::new(
2828
&ps, vec![],
2929
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
30-
&mut feature_gated_cfgs);
30+
&mut feature_gated_cfgs, &mut loader);
3131
cx.bt_push(syntax::codemap::ExpnInfo {
3232
call_site: DUMMY_SP,
3333
callee: syntax::codemap::NameAndSpan {

src/test/run-pass-fulldeps/qquote.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ use syntax::parse::token::intern;
2020

2121
fn main() {
2222
let ps = syntax::parse::ParseSess::new();
23-
let mut feature_gated_cfgs = vec![];
23+
let (mut feature_gated_cfgs, mut loader) = (vec![], syntax::ext::base::DummyMacroLoader);
2424
let mut cx = syntax::ext::base::ExtCtxt::new(
2525
&ps, vec![],
2626
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
27-
&mut feature_gated_cfgs);
27+
&mut feature_gated_cfgs, &mut loader);
2828
cx.bt_push(syntax::codemap::ExpnInfo {
2929
call_site: DUMMY_SP,
3030
callee: syntax::codemap::NameAndSpan {

0 commit comments

Comments
 (0)