Skip to content

Commit cdd6346

Browse files
committed
auto merge of #15601 : jbclements/rust/disable-default-macro-behavior, r=alexcrichton
Our AST definition can include macro invocations, which can expand into all kinds of things. Macro invocations are expanded away during expansion time, and the rest of the compiler doesn't have to deal with them. However, we have no way of enforcing this. This patch adds two protective mechanisms. First, it adds a (quick) explicit check that ensures there are no macro invocations remaining in the AST after expansion. Second, it updates the visit and fold mechanisms so that by default, they will not traverse macro invocations. It's easy enough to add this, if desired (it's documented in the source, and examples appear, e.g. in the IdentFinder. Along the way, I also consulted with @sfackler to refactor the macro export mechanism so that it stores macro text spans in a side table, rather than leaving them in the AST.
2 parents 767f4a7 + c253b36 commit cdd6346

File tree

13 files changed

+136
-55
lines changed

13 files changed

+136
-55
lines changed

src/librustc/driver/driver.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
270270
}
271271
);
272272

273+
// JBC: make CFG processing part of expansion to avoid this problem:
274+
273275
// strip again, in case expansion added anything with a #[cfg].
274276
krate = time(time_passes, "configuration 2", krate, |krate|
275277
front::config::strip_unconfigured_items(krate));
@@ -290,6 +292,9 @@ pub fn phase_2_configure_and_expand(sess: &Session,
290292
krate.encode(&mut json).unwrap();
291293
}
292294

295+
time(time_passes, "checking that all macro invocations are gone", &krate, |krate|
296+
syntax::ext::expand::check_for_macros(&sess.parse_sess, krate));
297+
293298
Some((krate, map))
294299
}
295300

@@ -302,14 +307,14 @@ pub struct CrateAnalysis {
302307
pub name: String,
303308
}
304309

310+
305311
/// Run the resolution, typechecking, region checking and other
306312
/// miscellaneous analysis passes on the crate. Return various
307313
/// structures carrying the results of the analysis.
308314
pub fn phase_3_run_analysis_passes(sess: Session,
309315
krate: &ast::Crate,
310316
ast_map: syntax::ast_map::Map,
311317
name: String) -> CrateAnalysis {
312-
313318
let time_passes = sess.time_passes();
314319

315320
time(time_passes, "external crate/lib resolution", (), |_|

src/librustc/driver/session.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ use syntax::{ast, codemap};
2929
use std::os;
3030
use std::cell::{Cell, RefCell};
3131

32-
32+
// Represents the data associated with a compilation
33+
// session for a single crate.
3334
pub struct Session {
3435
pub targ_cfg: config::Config,
3536
pub opts: config::Options,

src/librustc/front/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use syntax::codemap;
1414

1515
use std::gc::{Gc, GC};
1616

17+
/// A folder that strips out items that do not belong in the current
18+
/// configuration.
1719
struct Context<'a> {
1820
in_cfg: |attrs: &[ast::Attribute]|: 'a -> bool,
1921
}
@@ -41,6 +43,9 @@ impl<'a> fold::Folder for Context<'a> {
4143
fn fold_expr(&mut self, expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
4244
fold_expr(self, expr)
4345
}
46+
fn fold_mac(&mut self, mac: &ast::Mac) -> ast::Mac {
47+
fold::fold_mac(mac, self)
48+
}
4449
}
4550

4651
pub fn strip_items(krate: ast::Crate,

src/librustc/metadata/encoder.rs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,37 +1588,25 @@ fn encode_plugin_registrar_fn(ecx: &EncodeContext, ebml_w: &mut Encoder) {
15881588
}
15891589
}
15901590

1591-
struct MacroDefVisitor<'a, 'b, 'c> {
1592-
ecx: &'a EncodeContext<'b>,
1593-
ebml_w: &'a mut Encoder<'c>
1594-
}
1595-
1596-
impl<'a, 'b, 'c> Visitor<()> for MacroDefVisitor<'a, 'b, 'c> {
1597-
fn visit_item(&mut self, item: &Item, _: ()) {
1598-
match item.node {
1599-
ItemMac(..) => {
1600-
let def = self.ecx.tcx.sess.codemap().span_to_snippet(item.span)
1601-
.expect("Unable to find source for macro");
1602-
self.ebml_w.start_tag(tag_macro_def);
1603-
self.ebml_w.wr_str(def.as_slice());
1604-
self.ebml_w.end_tag();
1605-
}
1606-
_ => {}
1607-
}
1608-
visit::walk_item(self, item, ());
1609-
}
1591+
/// Given a span, write the text of that span into the output stream
1592+
/// as an exported macro
1593+
fn encode_macro_def(ecx: &EncodeContext,
1594+
ebml_w: &mut Encoder,
1595+
span: &syntax::codemap::Span) {
1596+
let def = ecx.tcx.sess.codemap().span_to_snippet(*span)
1597+
.expect("Unable to find source for macro");
1598+
ebml_w.start_tag(tag_macro_def);
1599+
ebml_w.wr_str(def.as_slice());
1600+
ebml_w.end_tag();
16101601
}
16111602

1612-
fn encode_macro_defs<'a>(ecx: &'a EncodeContext,
1613-
krate: &Crate,
1614-
ebml_w: &'a mut Encoder) {
1603+
/// Serialize the text of the exported macros
1604+
fn encode_macro_defs(ecx: &EncodeContext,
1605+
krate: &Crate,
1606+
ebml_w: &mut Encoder) {
16151607
ebml_w.start_tag(tag_exported_macros);
1616-
{
1617-
let mut visitor = MacroDefVisitor {
1618-
ecx: ecx,
1619-
ebml_w: ebml_w,
1620-
};
1621-
visit::walk_crate(&mut visitor, krate, ());
1608+
for span in krate.exported_macros.iter() {
1609+
encode_macro_def(ecx, ebml_w, span);
16221610
}
16231611
ebml_w.end_tag();
16241612
}

src/librustc/plugin/load.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate) -> Plugins {
7272
loader.plugins
7373
}
7474

75+
// note that macros aren't expanded yet, and therefore macros can't add plugins.
7576
impl<'a> Visitor<()> for PluginLoader<'a> {
7677
fn visit_view_item(&mut self, vi: &ast::ViewItem, _: ()) {
7778
match vi.node {
@@ -109,6 +110,10 @@ impl<'a> Visitor<()> for PluginLoader<'a> {
109110
_ => (),
110111
}
111112
}
113+
fn visit_mac(&mut self, _: &ast::Mac, _:()) {
114+
// bummer... can't see plugins inside macros.
115+
// do nothing.
116+
}
112117
}
113118

114119
impl<'a> PluginLoader<'a> {

src/libsyntax/ast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ pub struct Crate {
249249
pub attrs: Vec<Attribute>,
250250
pub config: CrateConfig,
251251
pub span: Span,
252+
pub exported_macros: Vec<Span>
252253
}
253254

254255
pub type MetaItem = Spanned<MetaItem_>;
@@ -1245,6 +1246,7 @@ mod test {
12451246
hi: BytePos(20),
12461247
expn_info: None,
12471248
},
1249+
exported_macros: Vec::new(),
12481250
};
12491251
// doesn't matter which encoder we use....
12501252
let _f = &e as &serialize::Encodable<json::Encoder, io::IoError>;

src/libsyntax/ast_map.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub enum Node {
112112
NodeLifetime(Gc<Lifetime>),
113113
}
114114

115+
/// Represents an entry and its parent Node ID
115116
/// The odd layout is to bring down the total size.
116117
#[deriving(Clone)]
117118
enum MapEntry {
@@ -184,6 +185,8 @@ impl MapEntry {
184185
}
185186
}
186187

188+
/// Represents a mapping from Node IDs to AST elements and their parent
189+
/// Node IDs
187190
pub struct Map {
188191
/// NodeIds are sequential integers from 0, so we can be
189192
/// super-compact by storing them in a vector. Not everything with
@@ -430,6 +433,8 @@ pub trait FoldOps {
430433
}
431434
}
432435

436+
/// A Folder that walks over an AST and constructs a Node ID Map. Its
437+
/// fold_ops argument has the opportunity to replace Node IDs and spans.
433438
pub struct Ctx<'a, F> {
434439
map: &'a Map,
435440
/// The node in which we are currently mapping (an item or a method).
@@ -584,6 +589,10 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
584589
self.insert(lifetime.id, EntryLifetime(self.parent, box(GC) lifetime));
585590
lifetime
586591
}
592+
593+
fn fold_mac(&mut self, mac: &Mac) -> Mac {
594+
fold::fold_mac(mac, self)
595+
}
587596
}
588597

589598
pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {

src/libsyntax/ext/base.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ pub struct BasicMacroExpander {
4848
pub span: Option<Span>
4949
}
5050

51-
pub trait MacroExpander {
51+
/// Represents a thing that maps token trees to Macro Results
52+
pub trait TTMacroExpander {
5253
fn expand(&self,
5354
ecx: &mut ExtCtxt,
5455
span: Span,
@@ -60,7 +61,7 @@ pub type MacroExpanderFn =
6061
fn(ecx: &mut ExtCtxt, span: codemap::Span, token_tree: &[ast::TokenTree])
6162
-> Box<MacResult>;
6263

63-
impl MacroExpander for BasicMacroExpander {
64+
impl TTMacroExpander for BasicMacroExpander {
6465
fn expand(&self,
6566
ecx: &mut ExtCtxt,
6667
span: Span,
@@ -259,7 +260,7 @@ pub enum SyntaxExtension {
259260
/// A normal, function-like syntax extension.
260261
///
261262
/// `bytes!` is a `NormalTT`.
262-
NormalTT(Box<MacroExpander + 'static>, Option<Span>),
263+
NormalTT(Box<TTMacroExpander + 'static>, Option<Span>),
263264

264265
/// A function-like syntax extension that has an extra ident before
265266
/// the block.
@@ -409,6 +410,7 @@ pub struct ExtCtxt<'a> {
409410

410411
pub mod_path: Vec<ast::Ident> ,
411412
pub trace_mac: bool,
413+
pub exported_macros: Vec<codemap::Span>
412414
}
413415

414416
impl<'a> ExtCtxt<'a> {
@@ -420,7 +422,8 @@ impl<'a> ExtCtxt<'a> {
420422
backtrace: None,
421423
mod_path: Vec::new(),
422424
ecfg: ecfg,
423-
trace_mac: false
425+
trace_mac: false,
426+
exported_macros: Vec::new(),
424427
}
425428
}
426429

@@ -538,6 +541,9 @@ impl<'a> ExtCtxt<'a> {
538541
pub fn name_of(&self, st: &str) -> ast::Name {
539542
token::intern(st)
540543
}
544+
pub fn push_exported_macro(&mut self, span: codemap::Span) {
545+
self.exported_macros.push(span);
546+
}
541547
}
542548

543549
/// Extract a string literal from the macro expanded version of `expr`,

src/libsyntax/ext/expand.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,10 +518,9 @@ fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander)
518518
// create issue to recommend refactoring here?
519519
fld.extsbox.insert(intern(name.as_slice()), ext);
520520
if attr::contains_name(it.attrs.as_slice(), "macro_export") {
521-
SmallVector::one(it)
522-
} else {
523-
SmallVector::zero()
521+
fld.cx.push_exported_macro(it.span);
524522
}
523+
SmallVector::zero()
525524
}
526525
None => {
527526
match expanded.make_items() {
@@ -754,7 +753,6 @@ impl Visitor<()> for PatIdentFinder {
754753
_ => visit::walk_pat(self, pattern, ())
755754
}
756755
}
757-
758756
}
759757

760758
/// find the PatIdent paths in a pattern
@@ -903,6 +901,9 @@ impl<'a> Folder for IdentRenamer<'a> {
903901
ctxt: mtwt::apply_renames(self.renames, id.ctxt),
904902
}
905903
}
904+
fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
905+
fold::fold_mac(macro, self)
906+
}
906907
}
907908

908909
/// A tree-folder that applies every rename in its list to
@@ -932,6 +933,9 @@ impl<'a> Folder for PatIdentRenamer<'a> {
932933
_ => noop_fold_pat(pat, self)
933934
}
934935
}
936+
fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
937+
fold::fold_mac(macro, self)
938+
}
935939
}
936940

937941
// expand a method
@@ -1039,6 +1043,7 @@ pub struct ExportedMacros {
10391043

10401044
pub fn expand_crate(parse_sess: &parse::ParseSess,
10411045
cfg: ExpansionConfig,
1046+
// these are the macros being imported to this crate:
10421047
macros: Vec<ExportedMacros>,
10431048
user_exts: Vec<NamedSyntaxExtension>,
10441049
c: Crate) -> Crate {
@@ -1066,7 +1071,8 @@ pub fn expand_crate(parse_sess: &parse::ParseSess,
10661071
expander.extsbox.insert(name, extension);
10671072
}
10681073

1069-
let ret = expander.fold_crate(c);
1074+
let mut ret = expander.fold_crate(c);
1075+
ret.exported_macros = expander.cx.exported_macros.clone();
10701076
parse_sess.span_diagnostic.handler().abort_if_errors();
10711077
return ret;
10721078
}
@@ -1145,6 +1151,25 @@ fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
11451151
return einfo;
11461152
}
11471153

1154+
/// Check that there are no macro invocations left in the AST:
1155+
pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
1156+
visit::walk_crate(&mut MacroExterminator{sess:sess}, krate, ());
1157+
}
1158+
1159+
/// A visitor that ensures that no macro invocations remain in an AST.
1160+
struct MacroExterminator<'a>{
1161+
sess: &'a parse::ParseSess
1162+
}
1163+
1164+
impl<'a> visit::Visitor<()> for MacroExterminator<'a> {
1165+
fn visit_mac(&mut self, macro: &ast::Mac, _:()) {
1166+
self.sess.span_diagnostic.span_bug(macro.span,
1167+
"macro exterminator: expected AST \
1168+
with no macro invocations");
1169+
}
1170+
}
1171+
1172+
11481173
#[cfg(test)]
11491174
mod test {
11501175
use super::{pattern_bindings, expand_crate, contains_macro_escape};

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use ast::{TTDelim};
1313
use ast;
1414
use codemap::{Span, Spanned, DUMMY_SP};
1515
use ext::base::{ExtCtxt, MacResult, MacroDef};
16-
use ext::base::{NormalTT, MacroExpander};
16+
use ext::base::{NormalTT, TTMacroExpander};
1717
use ext::base;
1818
use ext::tt::macro_parser::{Success, Error, Failure};
1919
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
@@ -95,7 +95,7 @@ struct MacroRulesMacroExpander {
9595
rhses: Vec<Rc<NamedMatch>>,
9696
}
9797

98-
impl MacroExpander for MacroRulesMacroExpander {
98+
impl TTMacroExpander for MacroRulesMacroExpander {
9999
fn expand(&self,
100100
cx: &mut ExtCtxt,
101101
sp: Span,

0 commit comments

Comments
 (0)