Skip to content

Commit dc77c5e

Browse files
committed
Auto merge of #34032 - jseyfried:load_macros_in_expansion, r=nrc
Support `#[macro_use]` on macro-expanded crates This PR loads macros from `#[macro_use]` crates during expansion so that - macro-expanded `#[macro_use]` crates work (fixes #33936, fixes #28071), and - macros imported from crates have the same scope as macros imported from modules. This is a [breaking-change]. For example, this will break: ```rust macro_rules! m { () => { #[macro_use(foo)] extern crate core; } //~ ERROR imported macro not found } m!(); ``` Also, this will break: ```rust macro_rules! try { () => {} } // #[macro_use] mod bar { macro_rules! try { ... } } //< ... just like this would ... fn main() { try!(); } //< ... making this an error ``` r? @nrc
2 parents bb4b3fb + dbf0326 commit dc77c5e

File tree

9 files changed

+99
-97
lines changed

9 files changed

+99
-97
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

+22-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,10 @@ 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+
// The first frame is pushed in `SyntaxEnv::new()` and the second frame is
946+
// pushed when folding the crate root pseudo-module (c.f. noop_fold_crate).
947+
self.chain.len() == 2
948+
}
928949
}

src/libsyntax/ext/expand.rs

+27-26
Original file line numberDiff line numberDiff line change
@@ -726,13 +726,11 @@ fn expand_annotatable(a: Annotatable,
726726
let new_items: SmallVector<Annotatable> = match a {
727727
Annotatable::Item(it) => match it.node {
728728
ast::ItemKind::Mac(..) => {
729-
let new_items: SmallVector<P<ast::Item>> = it.and_then(|it| match it.node {
729+
it.and_then(|it| match it.node {
730730
ItemKind::Mac(mac) =>
731731
expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
732732
_ => unreachable!(),
733-
});
734-
735-
new_items.into_iter().map(|i| Annotatable::Item(i)).collect()
733+
})
736734
}
737735
ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
738736
let valid_ident =
@@ -748,10 +746,19 @@ fn expand_annotatable(a: Annotatable,
748746
if valid_ident {
749747
fld.cx.mod_pop();
750748
}
751-
result.into_iter().map(|i| Annotatable::Item(i)).collect()
749+
result
752750
},
753-
_ => noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect(),
754-
},
751+
ast::ItemKind::ExternCrate(_) => {
752+
// We need to error on `#[macro_use] extern crate` when it isn't at the
753+
// crate root, because `$crate` won't work properly.
754+
let allows_macros = fld.cx.syntax_env.is_crate_root();
755+
for def in fld.cx.loader.load_crate(&it, allows_macros) {
756+
fld.cx.insert_macro(def);
757+
}
758+
SmallVector::one(it)
759+
},
760+
_ => noop_fold_item(it, fld),
761+
}.into_iter().map(|i| Annotatable::Item(i)).collect(),
755762

756763
Annotatable::TraitItem(it) => match it.node {
757764
ast::TraitItemKind::Method(_, Some(_)) => {
@@ -1137,8 +1144,6 @@ impl<'feat> ExpansionConfig<'feat> {
11371144
}
11381145

11391146
pub fn expand_crate(mut cx: ExtCtxt,
1140-
// these are the macros being imported to this crate:
1141-
imported_macros: Vec<ast::MacroDef>,
11421147
user_exts: Vec<NamedSyntaxExtension>,
11431148
c: Crate) -> (Crate, HashSet<Name>) {
11441149
if std_inject::no_core(&c) {
@@ -1151,10 +1156,6 @@ pub fn expand_crate(mut cx: ExtCtxt,
11511156
let ret = {
11521157
let mut expander = MacroExpander::new(&mut cx);
11531158

1154-
for def in imported_macros {
1155-
expander.cx.insert_macro(def);
1156-
}
1157-
11581159
for (name, extension) in user_exts {
11591160
expander.cx.syntax_env.insert(name, extension);
11601161
}
@@ -1220,7 +1221,7 @@ mod tests {
12201221
use ast;
12211222
use ast::Name;
12221223
use codemap;
1223-
use ext::base::ExtCtxt;
1224+
use ext::base::{ExtCtxt, DummyMacroLoader};
12241225
use ext::mtwt;
12251226
use fold::Folder;
12261227
use parse;
@@ -1291,9 +1292,9 @@ mod tests {
12911292
src,
12921293
Vec::new(), &sess).unwrap();
12931294
// 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);
1295+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1296+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1297+
expand_crate(ecx, vec![], crate_ast);
12971298
}
12981299

12991300
// make sure that macros can't escape modules
@@ -1306,9 +1307,9 @@ mod tests {
13061307
"<test>".to_string(),
13071308
src,
13081309
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);
1310+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1311+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1312+
expand_crate(ecx, vec![], crate_ast);
13121313
}
13131314

13141315
// macro_use modules should allow macros to escape
@@ -1320,18 +1321,18 @@ mod tests {
13201321
"<test>".to_string(),
13211322
src,
13221323
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);
1324+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1325+
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1326+
expand_crate(ecx, vec![], crate_ast);
13261327
}
13271328

13281329
fn expand_crate_str(crate_str: String) -> ast::Crate {
13291330
let ps = parse::ParseSess::new();
13301331
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
13311332
// 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
1333+
let (mut gated_cfgs, mut loader) = (vec![], DummyMacroLoader);
1334+
let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut gated_cfgs, &mut loader);
1335+
expand_crate(ecx, vec![], crate_ast).0
13351336
}
13361337

13371338
// 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,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(rustc_private)]
12+
macro_rules! m {
13+
() => { #[macro_use] extern crate syntax; }
14+
}
15+
m!();
16+
17+
fn main() {
18+
help!(); //~ ERROR unexpected end of macro invocation
19+
}

0 commit comments

Comments
 (0)