From e1e5c14bad0e8989523cf5974d98a8257badb88d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 31 Aug 2016 09:02:45 +0000 Subject: [PATCH 01/19] In `Parser` and `ExtCtxt`, replace fields `filename` and `mod_path_stack` with a single field `directory: PathBuf`. --- src/libsyntax/ext/base.rs | 7 ++--- src/libsyntax/ext/expand.rs | 43 ++++++++++++----------------- src/libsyntax/ext/tt/macro_rules.rs | 3 +- src/libsyntax/parse/parser.rs | 42 +++++++++++----------------- 4 files changed, 38 insertions(+), 57 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3b1c01319c4..b2e4aeabd95 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -30,6 +30,7 @@ use feature_gate; use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; use std::rc::Rc; use tokenstream; @@ -602,8 +603,7 @@ pub struct ExtCtxt<'a> { pub derive_modes: HashMap>, pub recursion_count: usize, - pub filename: Option, - pub mod_path_stack: Vec, + pub directory: PathBuf, pub in_block: bool, } @@ -626,8 +626,7 @@ pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, derive_modes: HashMap::new(), recursion_count: 0, - filename: None, - mod_path_stack: Vec::new(), + directory: PathBuf::new(), in_block: false, } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d06b77a5b05..5c548533e93 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -28,6 +28,8 @@ use visit::Visitor; use std_inject; +use std::path::PathBuf; + // A trait for AST nodes and AST node lists into which macro invocations may expand. trait MacroGenerable: Sized { // Expand the given MacResult using its appropriate `make_*` method. @@ -566,7 +568,9 @@ fn visit_block(&mut self, block: &ast::Block) { impl<'a, 'b> Folder for MacroExpander<'a, 'b> { fn fold_crate(&mut self, c: Crate) -> Crate { - self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span)); + let mut directory = PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(c.span)); + directory.pop(); + self.cx.directory = directory; noop_fold_crate(c, self) } @@ -591,18 +595,22 @@ fn fold_item(&mut self, item: P) -> SmallVector> { let result; if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node { if item.span.contains(inner) { - self.push_mod_path(item.ident, &item.attrs); + let directory = self.cx.directory.clone(); + self.cx.directory.push(&*{ + ::attr::first_attr_value_str_by_name(&item.attrs, "path") + .unwrap_or(item.ident.name.as_str()) + }); result = expand_item(item, self); - self.pop_mod_path(); + self.cx.directory = directory; } else { - let filename = if inner != syntax_pos::DUMMY_SP { - Some(self.cx.parse_sess.codemap().span_to_filename(inner)) - } else { None }; - let orig_filename = replace(&mut self.cx.filename, filename); - let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new()); + let mut directory = match inner { + syntax_pos::DUMMY_SP => PathBuf::new(), + _ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)), + }; + directory.pop(); + let directory = replace(&mut self.cx.directory, directory); result = expand_item(item, self); - self.cx.filename = orig_filename; - self.cx.mod_path_stack = orig_mod_path_stack; + self.cx.directory = directory; } } else { result = expand_item(item, self); @@ -636,21 +644,6 @@ fn fold_ty(&mut self, ty: P) -> P { } } -impl<'a, 'b> MacroExpander<'a, 'b> { - fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) { - let default_path = id.name.as_str(); - let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") { - Some(d) => d, - None => default_path, - }; - self.cx.mod_path_stack.push(file_path) - } - - fn pop_mod_path(&mut self) { - self.cx.mod_path_stack.pop().unwrap(); - } -} - pub struct ExpansionConfig<'feat> { pub crate_name: String, pub features: Option<&'feat Features>, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index d197741e9a3..a4be84dfbf0 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -211,8 +211,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); - p.filename = cx.filename.clone(); - p.mod_path_stack = cx.mod_path_stack.clone(); + p.directory = cx.directory.clone(); p.restrictions = match cx.in_block { true => Restrictions::NO_NONINLINE_MOD, false => Restrictions::empty(), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ec9dc1bae5a..6a0e40edded 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -264,8 +264,7 @@ pub struct Parser<'a> { /// extra detail when the same error is seen twice pub obsolete_set: HashSet, /// Used to determine the path to externally loaded source files - pub filename: Option, - pub mod_path_stack: Vec, + pub directory: PathBuf, /// Stack of open delimiters and their spans. Used for error message. pub open_braces: Vec<(token::DelimToken, Span)>, /// Flag if this parser "owns" the directory that it is currently parsing @@ -346,9 +345,11 @@ pub fn new(sess: &'a ParseSess, { let tok0 = rdr.real_token(); let span = tok0.sp; - let filename = if span != syntax_pos::DUMMY_SP { - Some(sess.codemap().span_to_filename(span)) - } else { None }; + let mut directory = match span { + syntax_pos::DUMMY_SP => PathBuf::new(), + _ => PathBuf::from(sess.codemap().span_to_filename(span)), + }; + directory.pop(); let placeholder = TokenAndSpan { tok: token::Underscore, sp: span, @@ -377,8 +378,7 @@ pub fn new(sess: &'a ParseSess, quote_depth: 0, parsing_token_tree: false, obsolete_set: HashSet::new(), - mod_path_stack: Vec::new(), - filename: filename, + directory: directory, open_braces: Vec::new(), owns_directory: true, root_module_name: None, @@ -5306,27 +5306,24 @@ fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> let (m, attrs) = self.eval_src_mod(id, &outer_attrs, id_span)?; Ok((id, m, Some(attrs))) } else { - self.push_mod_path(id, &outer_attrs); + let directory = self.directory.clone(); + self.push_directory(id, &outer_attrs); self.expect(&token::OpenDelim(token::Brace))?; let mod_inner_lo = self.span.lo; let attrs = self.parse_inner_attributes()?; let m = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?; - self.pop_mod_path(); + self.directory = directory; Ok((id, ItemKind::Mod(m), Some(attrs))) } } - fn push_mod_path(&mut self, id: Ident, attrs: &[Attribute]) { + fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) { let default_path = self.id_to_interned_str(id); let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") { Some(d) => d, None => default_path, }; - self.mod_path_stack.push(file_path) - } - - fn pop_mod_path(&mut self) { - self.mod_path_stack.pop().unwrap(); + self.directory.push(&*file_path) } pub fn submod_path_from_attr(attrs: &[ast::Attribute], dir_path: &Path) -> Option { @@ -5374,18 +5371,11 @@ fn submod_path(&mut self, id: ast::Ident, outer_attrs: &[ast::Attribute], id_sp: Span) -> PResult<'a, ModulePathSuccess> { - let mut prefix = PathBuf::from(self.filename.as_ref().unwrap()); - prefix.pop(); - let mut dir_path = prefix; - for part in &self.mod_path_stack { - dir_path.push(&**part); - } - - if let Some(p) = Parser::submod_path_from_attr(outer_attrs, &dir_path) { + if let Some(p) = Parser::submod_path_from_attr(outer_attrs, &self.directory) { return Ok(ModulePathSuccess { path: p, owns_directory: true }); } - let paths = Parser::default_submod_path(id, &dir_path, self.sess.codemap()); + let paths = Parser::default_submod_path(id, &self.directory, self.sess.codemap()); if self.restrictions.contains(Restrictions::NO_NONINLINE_MOD) { let msg = @@ -5400,8 +5390,8 @@ fn submod_path(&mut self, } else if !self.owns_directory { let mut err = self.diagnostic().struct_span_err(id_sp, "cannot declare a new module at this location"); - let this_module = match self.mod_path_stack.last() { - Some(name) => name.to_string(), + let this_module = match self.directory.file_name() { + Some(file_name) => file_name.to_str().unwrap().to_owned(), None => self.root_module_name.as_ref().unwrap().clone(), }; err.span_note(id_sp, From 234d68b7d35687999c36bc39ffd414bca7373a48 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 30 Aug 2016 07:05:25 +0000 Subject: [PATCH 02/19] Improve `expand_type`. --- src/libsyntax/ext/expand.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 5c548533e93..6f8af66632f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -489,14 +489,17 @@ fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander) } pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { - let t = match t.node.clone() { + let t = match t.node { + ast::TyKind::Mac(_) => t.unwrap(), + _ => return fold::noop_fold_ty(t, fld), + }; + + match t.node { ast::TyKind::Mac(mac) => { expand_mac_invoc(mac, None, Vec::new(), t.span, fld) } - _ => t - }; - - fold::noop_fold_ty(t, fld) + _ => unreachable!(), + } } /// A tree-folder that performs macro expansion From 9b3bc7a9e9e35537823bc2a10fc078c1222ee7fd Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 31 Aug 2016 23:39:16 +0000 Subject: [PATCH 03/19] Remove `syntax::config::strip_unconfigured`, add `syntax::config::features`. --- src/librustc_driver/driver.rs | 20 +++-------- src/libsyntax/config.rs | 63 +++++++++++++++++++---------------- src/libsyntax/ext/expand.rs | 5 +-- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 065ef9e0ce1..8ed5b579ffc 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -551,7 +551,7 @@ pub struct ExpansionResult<'a> { /// Returns `None` if we're aborting after handling -W help. pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, cstore: &CStore, - mut krate: ast::Crate, + krate: ast::Crate, registry: Option, crate_name: &'a str, addl_plugins: Option>, @@ -562,21 +562,9 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, { let time_passes = sess.time_passes(); - // strip before anything else because crate metadata may use #[cfg_attr] - // and so macros can depend on configuration variables, such as - // - // #[macro_use] #[cfg(foo)] - // mod bar { macro_rules! baz!(() => {{}}) } - // - // baz! should not use this definition unless foo is enabled. - - krate = time(time_passes, "configuration", || { - let (krate, features) = - syntax::config::strip_unconfigured_items(krate, &sess.parse_sess, sess.opts.test); - // these need to be set "early" so that expansion sees `quote` if enabled. - *sess.features.borrow_mut() = features; - krate - }); + let (mut krate, features) = syntax::config::features(krate, &sess.parse_sess, sess.opts.test); + // these need to be set "early" so that expansion sees `quote` if enabled. + *sess.features.borrow_mut() = features; *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs); *sess.crate_disambiguator.borrow_mut() = diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 69a97917652..7f6395997ab 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -10,7 +10,6 @@ use attr::HasAttrs; use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; -use fold::Folder; use {fold, attr}; use ast; use codemap::{Spanned, respan}; @@ -27,6 +26,40 @@ pub struct StripUnconfigured<'a> { pub features: Option<&'a Features>, } +// `cfg_attr`-process the crate's attributes and compute the crate's features. +pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool) + -> (ast::Crate, Features) { + let features; + { + let mut strip_unconfigured = StripUnconfigured { + config: &krate.config.clone(), + should_test: should_test, + sess: sess, + features: None, + }; + + let unconfigured_attrs = krate.attrs.clone(); + let err_count = sess.span_diagnostic.err_count(); + if let Some(attrs) = strip_unconfigured.configure(krate.attrs) { + krate.attrs = attrs; + } else { // the entire crate is unconfigured + krate.attrs = Vec::new(); + krate.module.items = Vec::new(); + return (krate, Features::new()); + } + + features = get_features(&sess.span_diagnostic, &krate.attrs); + + // Avoid reconfiguring malformed `cfg_attr`s + if err_count == sess.span_diagnostic.err_count() { + strip_unconfigured.features = Some(&features); + strip_unconfigured.configure(unconfigured_attrs); + } + } + + (krate, features) +} + impl<'a> StripUnconfigured<'a> { fn configure(&mut self, node: T) -> Option { let node = self.process_cfg_attrs(node); @@ -125,34 +158,6 @@ fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) { } } -// Support conditional compilation by transforming the AST, stripping out -// any items that do not belong in the current configuration -pub fn strip_unconfigured_items(mut krate: ast::Crate, sess: &ParseSess, should_test: bool) - -> (ast::Crate, Features) { - let features; - { - let mut strip_unconfigured = StripUnconfigured { - config: &krate.config.clone(), - should_test: should_test, - sess: sess, - features: None, - }; - - let err_count = sess.span_diagnostic.err_count(); - let krate_attrs = strip_unconfigured.configure(krate.attrs.clone()).unwrap_or_default(); - features = get_features(&sess.span_diagnostic, &krate_attrs); - if err_count < sess.span_diagnostic.err_count() { - krate.attrs = krate_attrs.clone(); // Avoid reconfiguring malformed `cfg_attr`s - } - - strip_unconfigured.features = Some(&features); - krate = strip_unconfigured.fold_crate(krate); - krate.attrs = krate_attrs; - } - - (krate, features) -} - impl<'a> fold::Folder for StripUnconfigured<'a> { fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { ast::ForeignMod { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6f8af66632f..92d053fd21b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -719,8 +719,9 @@ pub fn expand_crate_with_expander(expander: &mut MacroExpander, } let items = SmallVector::many(c.module.items); - expander.load_macros(&items); - c.module.items = items.into(); + let configured = items.fold_with(&mut expander.strip_unconfigured()); + expander.load_macros(&configured); + c.module.items = configured.into(); let err_count = expander.cx.parse_sess.span_diagnostic.err_count(); let mut ret = expander.fold_crate(c); From de2e67836e23405e5bdc27cefa510fa562c5298f Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 27 Aug 2016 05:27:59 +0000 Subject: [PATCH 04/19] Add `Invocation` and `Expansion`, remove `MacroGenerable`. --- src/libsyntax/ext/expand.rs | 490 +++++++++++++++++++----------------- 1 file changed, 259 insertions(+), 231 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 92d053fd21b..485bd3ce10b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -30,249 +30,230 @@ use std::path::PathBuf; -// A trait for AST nodes and AST node lists into which macro invocations may expand. -trait MacroGenerable: Sized { - // Expand the given MacResult using its appropriate `make_*` method. - fn make_with<'a>(result: Box) -> Option; +macro_rules! expansions { + ($($kind:ident: $ty:ty, $kind_name:expr, .$make:ident, + $(.$fold:ident)* $(lift .$fold_elt:ident)*, + $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { + #[derive(Copy, Clone)] + enum ExpansionKind { OptExpr, $( $kind, )* } + enum Expansion { OptExpr(Option>), $( $kind($ty), )* } - // Fold this node or list of nodes using the given folder. - fn fold_with(self, folder: &mut F) -> Self; - fn visit_with(&self, visitor: &mut V); + impl ExpansionKind { + fn name(self) -> &'static str { + match self { + ExpansionKind::OptExpr => "expression", + $( ExpansionKind::$kind => $kind_name, )* + } + } - // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics. - fn kind_name() -> &'static str; + fn make_from<'a>(self, result: Box) -> Option { + match self { + ExpansionKind::OptExpr => result.make_expr().map(Some).map(Expansion::OptExpr), + $( ExpansionKind::$kind => result.$make().map(Expansion::$kind), )* + } + } + } - // Return a placeholder expansion to allow compilation to continue after an erroring expansion. - fn dummy(span: Span) -> Self { - Self::make_with(DummyResult::any(span)).unwrap() + impl Expansion { + fn make_opt_expr(self) -> Option> { + match self { + Expansion::OptExpr(expr) => expr, + _ => panic!("Expansion::make_* called on the wrong kind of expansion"), + } + } + $( fn $make(self) -> $ty { + match self { + Expansion::$kind(ast) => ast, + _ => panic!("Expansion::make_* called on the wrong kind of expansion"), + } + } )* + + fn fold_with(self, folder: &mut F) -> Self { + use self::Expansion::*; + match self { + OptExpr(expr) => OptExpr(expr.and_then(|expr| folder.fold_opt_expr(expr))), + $($( $kind(ast) => $kind(folder.$fold(ast)), )*)* + $($( $kind(ast) => { + $kind(ast.into_iter().flat_map(|ast| folder.$fold_elt(ast)).collect()) + }, )*)* + } + } + + fn visit_with(&self, visitor: &mut V) { + match *self { + Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), + $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)* + $($( Expansion::$kind(ref ast) => for ast in ast.as_slice() { + visitor.$visit_elt(ast); + }, )*)* + _ => {} + } + } + } } } -macro_rules! impl_macro_generable { - ($($ty:ty: $kind_name:expr, .$make:ident, - $(.$fold:ident)* $(lift .$fold_elt:ident)*, - $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $( - impl MacroGenerable for $ty { - fn kind_name() -> &'static str { $kind_name } - fn make_with<'a>(result: Box) -> Option { result.$make() } - fn fold_with(self, folder: &mut F) -> Self { - $( folder.$fold(self) )* - $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )* - } - fn visit_with(&self, visitor: &mut V) { - $( visitor.$visit(self) )* - $( for item in self.as_slice() { visitor. $visit_elt (item) } )* - } - } - )* } -} - -impl_macro_generable! { - P: "expression", .make_expr, .fold_expr, .visit_expr; - P: "pattern", .make_pat, .fold_pat, .visit_pat; - P: "type", .make_ty, .fold_ty, .visit_ty; - SmallVector: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; - SmallVector>: "item", .make_items, lift .fold_item, lift .visit_item; - SmallVector: +expansions! { + Expr: P, "expression", .make_expr, .fold_expr, .visit_expr; + Pat: P, "pattern", .make_pat, .fold_pat, .visit_pat; + Ty: P, "type", .make_ty, .fold_ty, .visit_ty; + Stmts: SmallVector, "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; + Items: SmallVector>, "item", .make_items, lift .fold_item, lift .visit_item; + TraitItems: SmallVector, "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; - SmallVector: + ImplItems: SmallVector, "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; } -impl MacroGenerable for Option> { - fn kind_name() -> &'static str { "expression" } - fn make_with<'a>(result: Box) -> Option { - result.make_expr().map(Some) - } - fn fold_with(self, folder: &mut F) -> Self { - self.and_then(|expr| folder.fold_opt_expr(expr)) - } - fn visit_with(&self, visitor: &mut V) { - self.as_ref().map(|expr| visitor.visit_expr(expr)); +impl ExpansionKind { + fn dummy(self, span: Span) -> Expansion { + self.make_from(DummyResult::any(span)).unwrap() } } +pub struct Invocation { + span: Span, + attrs: Vec, + mac: ast::Mac, + ident: Option, + mark: Mark, + kind: ExpansionKind, +} + pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P { - match expr.node { - // expr_mac should really be expr_ext or something; it's the - // entry-point for all syntax extensions. - ast::ExprKind::Mac(mac) => { - return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld); - } - _ => P(noop_fold_expr(expr, fld)), - } -} - -struct MacroScopePlaceholder; -impl MacResult for MacroScopePlaceholder { - fn make_items(self: Box) -> Option>> { - Some(SmallVector::one(P(ast::Item { - ident: keywords::Invalid.ident(), - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mac(dummy_spanned(ast::Mac_ { - path: ast::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() }, - tts: Vec::new(), - })), - vis: ast::Visibility::Inherited, - span: syntax_pos::DUMMY_SP, - }))) + if let ast::ExprKind::Mac(mac) = expr.node { + let invoc = fld.new_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); + expand_mac_invoc(invoc, fld).make_expr() + } else { + P(noop_fold_expr(expr, fld)) } } /// Expand a macro invocation. Returns the result of expansion. -fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec, span: Span, - fld: &mut MacroExpander) -> T - where T: MacroGenerable, -{ - // It would almost certainly be cleaner to pass the whole macro invocation in, - // rather than pulling it apart and marking the tts and the ctxt separately. +fn expand_mac_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { + let Invocation { span, attrs, mac, ident, mark, kind } = invoc; let Mac_ { path, tts, .. } = mac.node; - let mark = Mark::fresh(); - fn mac_result<'a>(path: &ast::Path, ident: Option, tts: Vec, mark: Mark, - attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) - -> Option> { - // Detect use of feature-gated or invalid attributes on macro invoations - // since they will not be detected after macro expansion. - for attr in attrs.iter() { - feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic, - &fld.cx.parse_sess.codemap(), - &fld.cx.ecfg.features.unwrap()); - } - - if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() { - fld.cx.span_err(path.span, "expected macro name without module separators"); - return None; - } - - let extname = path.segments[0].identifier.name; - let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) { - extension - } else { - let mut err = fld.cx.struct_span_err(path.span, - &format!("macro undefined: '{}!'", &extname)); - fld.cx.suggest_macro_name(&extname.as_str(), &mut err); - err.emit(); - return None; - }; - - let ident = ident.unwrap_or(keywords::Invalid.ident()); - let marked_tts = mark_tts(&tts, mark); - match *extension { - NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { - if ident.name != keywords::Invalid.name() { - let msg = - format!("macro {}! expects no ident argument, given '{}'", extname, ident); - fld.cx.span_err(path.span, &msg); - return None; - } - - fld.cx.bt_push(ExpnInfo { - call_site: call_site, - callee: NameAndSpan { - format: MacroBang(extname), - span: exp_span, - allow_internal_unstable: allow_internal_unstable, - }, - }); - - Some(expandfun.expand(fld.cx, call_site, &marked_tts)) - } - - IdentTT(ref expander, tt_span, allow_internal_unstable) => { - if ident.name == keywords::Invalid.name() { - fld.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", extname)); - return None; - }; - - fld.cx.bt_push(ExpnInfo { - call_site: call_site, - callee: NameAndSpan { - format: MacroBang(extname), - span: tt_span, - allow_internal_unstable: allow_internal_unstable, - } - }); - - Some(expander.expand(fld.cx, call_site, ident, marked_tts)) - } - - MacroRulesTT => { - if ident.name == keywords::Invalid.name() { - fld.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", extname)); - return None; - }; - - fld.cx.bt_push(ExpnInfo { - call_site: call_site, - callee: NameAndSpan { - format: MacroBang(extname), - span: None, - // `macro_rules!` doesn't directly allow unstable - // (this is orthogonal to whether the macro it creates allows it) - allow_internal_unstable: false, - } - }); - - let def = ast::MacroDef { - ident: ident, - id: ast::DUMMY_NODE_ID, - span: call_site, - imported_from: None, - use_locally: true, - body: marked_tts, - export: attr::contains_name(&attrs, "macro_export"), - allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), - attrs: attrs, - }; - - fld.cx.insert_macro(def.clone()); - - // macro_rules! has a side effect, but expands to nothing. - // If keep_macs is true, expands to a MacEager::items instead. - if fld.keep_macs { - Some(MacEager::items(SmallVector::one(P(ast::Item { - ident: def.ident, - attrs: def.attrs.clone(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mac(ast::Mac { - span: def.span, - node: ast::Mac_ { - path: path.clone(), - tts: def.body.clone(), - } - }), - vis: ast::Visibility::Inherited, - span: def.span, - })))) - } else { - Some(Box::new(MacroScopePlaceholder)) - } - } - - MultiDecorator(..) | MultiModifier(..) => { - fld.cx.span_err(path.span, - &format!("`{}` can only be used in attributes", extname)); - None - } - } + // Detect use of feature-gated or invalid attributes on macro invoations + // since they will not be detected after macro expansion. + for attr in attrs.iter() { + feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic, + &fld.cx.parse_sess.codemap(), + &fld.cx.ecfg.features.unwrap()); } - let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) { - Some(result) => result, - None => return T::dummy(span), - }); + if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() { + fld.cx.span_err(path.span, "expected macro name without module separators"); + return kind.dummy(span); + } + + let extname = path.segments[0].identifier.name; + let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) { + extension + } else { + let mut err = + fld.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname)); + fld.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 { + NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { + if ident.name != keywords::Invalid.name() { + let msg = + format!("macro {}! expects no ident argument, given '{}'", extname, ident); + fld.cx.span_err(path.span, &msg); + return kind.dummy(span); + } + + fld.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: exp_span, + allow_internal_unstable: allow_internal_unstable, + }, + }); + + kind.make_from(expandfun.expand(fld.cx, span, &marked_tts)) + } + + IdentTT(ref expander, tt_span, allow_internal_unstable) => { + if ident.name == keywords::Invalid.name() { + fld.cx.span_err(path.span, + &format!("macro {}! expects an ident argument", extname)); + return kind.dummy(span); + }; + + fld.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: tt_span, + allow_internal_unstable: allow_internal_unstable, + } + }); + + kind.make_from(expander.expand(fld.cx, span, ident, marked_tts)) + } + + MacroRulesTT => { + if ident.name == keywords::Invalid.name() { + fld.cx.span_err(path.span, + &format!("macro {}! expects an ident argument", extname)); + return kind.dummy(span); + }; + + fld.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: None, + // `macro_rules!` doesn't directly allow unstable + // (this is orthogonal to whether the macro it creates allows it) + allow_internal_unstable: false, + } + }); + + let def = ast::MacroDef { + ident: ident, + id: ast::DUMMY_NODE_ID, + span: span, + imported_from: None, + use_locally: true, + body: marked_tts, + export: attr::contains_name(&attrs, "macro_export"), + allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), + attrs: attrs, + }; + + fld.cx.insert_macro(def.clone()); + + // If keep_macs is true, expands to a MacEager::items instead. + if fld.keep_macs { + Some(reconstruct_macro_rules(&def, &path)) + } else { + Some(macro_scope_placeholder()) + } + } + + MultiDecorator(..) | MultiModifier(..) => { + fld.cx.span_err(path.span, + &format!("`{}` can only be used in attributes", extname)); + return kind.dummy(span); + } + }; let expanded = if let Some(expanded) = opt_expanded { expanded } else { let msg = format!("non-{kind} macro in {kind} position: {name}", - name = path.segments[0].identifier.name, kind = T::kind_name()); + name = path.segments[0].identifier.name, kind = kind.name()); fld.cx.span_err(path.span, &msg); - return T::dummy(span); + return kind.dummy(span); }; let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) }); @@ -342,8 +323,8 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { _ => return noop_fold_stmt(stmt, fld) }; - let mut fully_expanded: SmallVector = - expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld); + let invoc = fld.new_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); + let mut fully_expanded = expand_mac_invoc(invoc, fld).make_stmts(); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -361,11 +342,12 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { PatKind::Mac(_) => {} _ => return noop_fold_pat(p, fld) } - p.and_then(|ast::Pat {node, span, ..}| { - match node { - PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld), - _ => unreachable!() + p.and_then(|p| match p.node { + PatKind::Mac(mac) => { + let invoc = fld.new_invoc(mac, Vec::new(), p.span, ExpansionKind::Pat); + expand_mac_invoc(invoc, fld).make_pat() } + _ => unreachable!(), }) } @@ -380,8 +362,11 @@ fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector return SmallVector::one(Annotatable::Item(it)); } it.and_then(|it| match it.node { - ItemKind::Mac(mac) => - expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld), + ItemKind::Mac(mac) => { + let mut invoc = fld.new_invoc(mac, it.attrs, it.span, ExpansionKind::Items); + invoc.ident = Some(it.ident); + expand_mac_invoc(invoc, fld).make_items() + } _ => unreachable!(), }) } @@ -472,7 +457,8 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) -> SmallVector { match ii.node { ast::ImplItemKind::Macro(mac) => { - expand_mac_invoc(mac, None, ii.attrs, ii.span, fld) + let invoc = fld.new_invoc(mac, ii.attrs, ii.span, ExpansionKind::ImplItems); + expand_mac_invoc(invoc, fld).make_impl_items() } _ => fold::noop_fold_impl_item(ii, fld) } @@ -482,7 +468,8 @@ fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander) -> SmallVector { match ti.node { ast::TraitItemKind::Macro(mac) => { - expand_mac_invoc(mac, None, ti.attrs, ti.span, fld) + let invoc = fld.new_invoc(mac, ti.attrs, ti.span, ExpansionKind::TraitItems); + expand_mac_invoc(invoc, fld).make_trait_items() } _ => fold::noop_fold_trait_item(ti, fld) } @@ -496,7 +483,8 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { match t.node { ast::TyKind::Mac(mac) => { - expand_mac_invoc(mac, None, Vec::new(), t.span, fld) + let invoc = fld.new_invoc(mac, Vec::new(), t.span, ExpansionKind::Ty); + expand_mac_invoc(invoc, fld).make_ty() } _ => unreachable!(), } @@ -529,7 +517,7 @@ fn strip_unconfigured(&mut self) -> StripUnconfigured { } } - fn load_macros(&mut self, node: &T) { + fn load_macros(&mut self, node: &Expansion) { struct MacroLoadingVisitor<'a, 'b: 'a>{ cx: &'a mut ExtCtxt<'b>, at_crate_root: bool, @@ -567,6 +555,12 @@ fn visit_block(&mut self, block: &ast::Block) { cx: self.cx, }); } + + fn new_invoc(&self, mac: ast::Mac, attrs: Vec, span: Span, kind: ExpansionKind) + -> Invocation { + let mark = Mark::fresh(); + Invocation { span: span, attrs: attrs, mac: mac, mark: mark, kind: kind, ident: None } + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -583,8 +577,11 @@ fn fold_expr(&mut self, expr: P) -> P { fn fold_opt_expr(&mut self, expr: P) -> Option> { expr.and_then(|expr| match expr.node { - ast::ExprKind::Mac(mac) => - expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self), + ast::ExprKind::Mac(mac) => { + let invoc = + self.new_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); + expand_mac_invoc(invoc, self).make_opt_expr() + } _ => Some(expand_expr(expr, self)), }) } @@ -647,6 +644,37 @@ fn fold_ty(&mut self, ty: P) -> P { } } +fn macro_scope_placeholder() -> Expansion { + Expansion::Items(SmallVector::one(P(ast::Item { + ident: keywords::Invalid.ident(), + attrs: Vec::new(), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Mac(dummy_spanned(ast::Mac_ { + path: ast::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() }, + tts: Vec::new(), + })), + vis: ast::Visibility::Inherited, + span: syntax_pos::DUMMY_SP, + }))) +} + +fn reconstruct_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion { + Expansion::Items(SmallVector::one(P(ast::Item { + ident: def.ident, + attrs: def.attrs.clone(), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Mac(ast::Mac { + span: def.span, + node: ast::Mac_ { + path: path.clone(), + tts: def.body.clone(), + } + }), + vis: ast::Visibility::Inherited, + span: def.span, + }))) +} + pub struct ExpansionConfig<'feat> { pub crate_name: String, pub features: Option<&'feat Features>, @@ -718,10 +746,10 @@ pub fn expand_crate_with_expander(expander: &mut MacroExpander, expander.cx.syntax_env.insert(name, extension); } - let items = SmallVector::many(c.module.items); + let items = Expansion::Items(SmallVector::many(c.module.items)); let configured = items.fold_with(&mut expander.strip_unconfigured()); expander.load_macros(&configured); - c.module.items = configured.into(); + c.module.items = configured.make_items().into(); let err_count = expander.cx.parse_sess.span_diagnostic.err_count(); let mut ret = expander.fold_crate(c); From 3cba93f9933fe96fb77d625a480eb4cdddeed91f Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 27 Aug 2016 06:27:35 +0000 Subject: [PATCH 05/19] Refactor `with_exts_frame` from a macro to a function. --- src/libsyntax/ext/expand.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 485bd3ce10b..481278eb257 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -270,18 +270,6 @@ fn expand_mac_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { fully_expanded } -// eval $e with a new exts frame. -// must be a macro so that $e isn't evaluated too early. -macro_rules! with_exts_frame { - ($extsboxexpr:expr,$macros_escape:expr,$e:expr) => - ({$extsboxexpr.push_frame(); - $extsboxexpr.info().macros_escape = $macros_escape; - let result = $e; - $extsboxexpr.pop_frame(); - result - }) -} - // When we enter a module, record it, for the sake of `module!` pub fn expand_item(it: P, fld: &mut MacroExpander) -> SmallVector> { @@ -378,9 +366,7 @@ fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector fld.cx.mod_push(it.ident); } let macro_use = contains_macro_use(fld, &it.attrs); - let result = with_exts_frame!(fld.cx.syntax_env, - macro_use, - noop_fold_item(it, fld)); + let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(it, fld)); if valid_ident { fld.cx.mod_pop(); } @@ -561,6 +547,14 @@ fn new_invoc(&self, mac: ast::Mac, attrs: Vec, span: Span, kind: let mark = Mark::fresh(); Invocation { span: span, attrs: attrs, mac: mac, mark: mark, kind: kind, ident: None } } + + fn with_exts_frame T>(&mut self, macros_escape: bool, f: F) -> T { + self.cx.syntax_env.push_frame(); + self.cx.syntax_env.info().macros_escape = macros_escape; + let result = f(self); + self.cx.syntax_env.pop_frame(); + result + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -624,7 +618,7 @@ fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { fn fold_block(&mut self, block: P) -> P { let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true); - let result = with_exts_frame!(self.cx.syntax_env, false, noop_fold_block(block, self)); + let result = self.with_exts_frame(false, |this| noop_fold_block(block, this)); self.cx.in_block = was_in_block; result } From fca80c983d486632d2fe12aed270af332ce01598 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 06:14:38 +0000 Subject: [PATCH 06/19] Generalize `Invocation` to include modifiers/decorators. --- src/libsyntax/ext/base.rs | 10 -- src/libsyntax/ext/expand.rs | 223 ++++++++++++++++++++++-------------- 2 files changed, 138 insertions(+), 95 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index b2e4aeabd95..769a5af0262 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -91,16 +91,6 @@ pub fn expect_impl_item(self) -> ast::ImplItem { _ => panic!("expected Item") } } - - pub fn fold_with(self, folder: &mut F) -> SmallVector { - match self { - Annotatable::Item(item) => folder.fold_item(item).map(Annotatable::Item), - Annotatable::ImplItem(item) => - folder.fold_impl_item(item.unwrap()).map(|item| Annotatable::ImplItem(P(item))), - Annotatable::TraitItem(item) => - folder.fold_trait_item(item.unwrap()).map(|item| Annotatable::TraitItem(P(item))), - } - } } // A more flexible ItemDecorator. diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 481278eb257..66a766a666b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -109,29 +109,104 @@ impl ExpansionKind { fn dummy(self, span: Span) -> Expansion { self.make_from(DummyResult::any(span)).unwrap() } + + fn expect_from_annotatables>(self, items: I) -> Expansion { + let items = items.into_iter(); + match self { + ExpansionKind::Items => + Expansion::Items(items.map(Annotatable::expect_item).collect()), + ExpansionKind::ImplItems => + Expansion::ImplItems(items.map(Annotatable::expect_impl_item).collect()), + ExpansionKind::TraitItems => + Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()), + _ => unreachable!(), + } + } } pub struct Invocation { - span: Span, - attrs: Vec, - mac: ast::Mac, - ident: Option, + kind: InvocationKind, + expansion_kind: ExpansionKind, mark: Mark, - kind: ExpansionKind, +} + +enum InvocationKind { + Bang { + attrs: Vec, + mac: ast::Mac, + ident: Option, + span: Span, + }, + Attr { + attr: ast::Attribute, + item: Annotatable, + }, } pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P { if let ast::ExprKind::Mac(mac) = expr.node { - let invoc = fld.new_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); - expand_mac_invoc(invoc, fld).make_expr() + let invoc = fld.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); + expand_invoc(invoc, fld).make_expr() } else { P(noop_fold_expr(expr, fld)) } } +fn expand_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { + match invoc.kind { + InvocationKind::Bang { .. } => expand_bang_invoc(invoc, fld), + InvocationKind::Attr { .. } => expand_attr_invoc(invoc, fld), + } +} + +fn expand_attr_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { + let Invocation { expansion_kind: kind, .. } = invoc; + let (attr, item) = match invoc.kind { + InvocationKind::Attr { attr, item } => (attr, item), + _ => unreachable!(), + }; + + let extension = match fld.cx.syntax_env.find(intern(&attr.name())) { + Some(extension) => extension, + None => unreachable!(), + }; + + attr::mark_used(&attr); + fld.cx.bt_push(ExpnInfo { + call_site: attr.span, + callee: NameAndSpan { + format: MacroAttribute(intern(&attr.name())), + span: Some(attr.span), + allow_internal_unstable: false, + } + }); + + let modified = match *extension { + MultiModifier(ref mac) => { + kind.expect_from_annotatables(mac.expand(fld.cx, attr.span, &attr.node.value, item)) + } + MultiDecorator(ref mac) => { + let mut items = Vec::new(); + mac.expand(fld.cx, attr.span, &attr.node.value, &item, &mut |item| items.push(item)); + items.push(item); + kind.expect_from_annotatables(items) + } + _ => unreachable!(), + }; + + fld.cx.bt_pop(); + + let configured = modified.fold_with(&mut fld.strip_unconfigured()); + configured.fold_with(fld) +} + /// Expand a macro invocation. Returns the result of expansion. -fn expand_mac_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { - let Invocation { span, attrs, mac, ident, mark, kind } = invoc; +fn expand_bang_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { + let Invocation { mark, expansion_kind: kind, .. } = invoc; + let (attrs, mac, ident, span) = match invoc.kind { + InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), + _ => unreachable!(), + }; let Mac_ { path, tts, .. } = mac.node; // Detect use of feature-gated or invalid attributes on macro invoations @@ -270,11 +345,8 @@ fn expand_mac_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { fully_expanded } -// When we enter a module, record it, for the sake of `module!` -pub fn expand_item(it: P, fld: &mut MacroExpander) - -> SmallVector> { - expand_annotatable(Annotatable::Item(it), fld) - .into_iter().map(|i| i.expect_item()).collect() +pub fn expand_item(it: P, fld: &mut MacroExpander) -> SmallVector> { + expand_annotatable(Annotatable::Item(it), fld).make_items() } // does this attribute list contain "macro_use" ? @@ -311,8 +383,8 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { _ => return noop_fold_stmt(stmt, fld) }; - let invoc = fld.new_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); - let mut fully_expanded = expand_mac_invoc(invoc, fld).make_stmts(); + let invoc = fld.new_bang_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); + let mut fully_expanded = expand_invoc(invoc, fld).make_stmts(); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -332,14 +404,14 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { } p.and_then(|p| match p.node { PatKind::Mac(mac) => { - let invoc = fld.new_invoc(mac, Vec::new(), p.span, ExpansionKind::Pat); - expand_mac_invoc(invoc, fld).make_pat() + let invoc = fld.new_bang_invoc(mac, Vec::new(), p.span, ExpansionKind::Pat); + expand_invoc(invoc, fld).make_pat() } _ => unreachable!(), }) } -fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector { +fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> Expansion { match a { Annotatable::Item(it) => match it.node { ast::ItemKind::Mac(..) => { @@ -347,13 +419,15 @@ fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), _ => unreachable!(), } { - return SmallVector::one(Annotatable::Item(it)); + return Expansion::Items(SmallVector::one(it)); } it.and_then(|it| match it.node { ItemKind::Mac(mac) => { - let mut invoc = fld.new_invoc(mac, it.attrs, it.span, ExpansionKind::Items); - invoc.ident = Some(it.ident); - expand_mac_invoc(invoc, fld).make_items() + let invoc = + fld.new_invoc(ExpansionKind::Items, InvocationKind::Bang { + mac: mac, attrs: it.attrs, ident: Some(it.ident), span: it.span, + }); + expand_invoc(invoc, fld) } _ => unreachable!(), }) @@ -370,31 +444,24 @@ fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector if valid_ident { fld.cx.mod_pop(); } - result + Expansion::Items(result) }, - _ => noop_fold_item(it, fld), - }.into_iter().map(|i| Annotatable::Item(i)).collect(), + _ => Expansion::Items(noop_fold_item(it, fld)), + }, - Annotatable::TraitItem(it) => { - expand_trait_item(it.unwrap(), fld).into_iter(). - map(|it| Annotatable::TraitItem(P(it))).collect() - } - - Annotatable::ImplItem(ii) => { - expand_impl_item(ii.unwrap(), fld).into_iter(). - map(|ii| Annotatable::ImplItem(P(ii))).collect() - } + Annotatable::TraitItem(it) => Expansion::TraitItems(expand_trait_item(it.unwrap(), fld)), + Annotatable::ImplItem(ii) => Expansion::ImplItems(expand_impl_item(ii.unwrap(), fld)), } } -fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector { - let mut multi_modifier = None; +fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> Expansion { + let mut attr = None; item = item.map_attrs(|mut attrs| { for i in 0..attrs.len() { if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) { match *extension { MultiModifier(..) | MultiDecorator(..) => { - multi_modifier = Some((attrs.remove(i), extension)); + attr = Some(attrs.remove(i)); break; } _ => {} @@ -404,38 +471,16 @@ fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVe attrs }); - match multi_modifier { - None => expand_multi_modified(item, fld), - Some((attr, extension)) => { - attr::mark_used(&attr); - fld.cx.bt_push(ExpnInfo { - call_site: attr.span, - callee: NameAndSpan { - format: MacroAttribute(intern(&attr.name())), - span: Some(attr.span), - allow_internal_unstable: false, - } - }); - - let modified = match *extension { - MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item), - MultiDecorator(ref mac) => { - let mut items = Vec::new(); - mac.expand(fld.cx, attr.span, &attr.node.value, &item, - &mut |item| items.push(item)); - items.push(item); - items - } - _ => unreachable!(), - }; - - fld.cx.bt_pop(); - let configured = modified.into_iter().flat_map(|it| { - it.fold_with(&mut fld.strip_unconfigured()) - }).collect::>(); - - configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect() - } + if let Some(attr) = attr { + let kind = match item { + Annotatable::Item(_) => ExpansionKind::Items, + Annotatable::ImplItem(_) => ExpansionKind::ImplItems, + Annotatable::TraitItem(_) => ExpansionKind::TraitItems, + }; + let invoc = fld.new_invoc(kind, InvocationKind::Attr { attr: attr, item: item }); + expand_invoc(invoc, fld) + } else { + expand_multi_modified(item, fld) } } @@ -443,8 +488,8 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) -> SmallVector { match ii.node { ast::ImplItemKind::Macro(mac) => { - let invoc = fld.new_invoc(mac, ii.attrs, ii.span, ExpansionKind::ImplItems); - expand_mac_invoc(invoc, fld).make_impl_items() + let invoc = fld.new_bang_invoc(mac, ii.attrs, ii.span, ExpansionKind::ImplItems); + expand_invoc(invoc, fld).make_impl_items() } _ => fold::noop_fold_impl_item(ii, fld) } @@ -454,8 +499,8 @@ fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander) -> SmallVector { match ti.node { ast::TraitItemKind::Macro(mac) => { - let invoc = fld.new_invoc(mac, ti.attrs, ti.span, ExpansionKind::TraitItems); - expand_mac_invoc(invoc, fld).make_trait_items() + let invoc = fld.new_bang_invoc(mac, ti.attrs, ti.span, ExpansionKind::TraitItems); + expand_invoc(invoc, fld).make_trait_items() } _ => fold::noop_fold_trait_item(ti, fld) } @@ -469,8 +514,8 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { match t.node { ast::TyKind::Mac(mac) => { - let invoc = fld.new_invoc(mac, Vec::new(), t.span, ExpansionKind::Ty); - expand_mac_invoc(invoc, fld).make_ty() + let invoc = fld.new_bang_invoc(mac, Vec::new(), t.span, ExpansionKind::Ty); + expand_invoc(invoc, fld).make_ty() } _ => unreachable!(), } @@ -542,10 +587,20 @@ fn visit_block(&mut self, block: &ast::Block) { }); } - fn new_invoc(&self, mac: ast::Mac, attrs: Vec, span: Span, kind: ExpansionKind) + fn new_invoc(&self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Invocation { - let mark = Mark::fresh(); - Invocation { span: span, attrs: attrs, mac: mac, mark: mark, kind: kind, ident: None } + Invocation { mark: Mark::fresh(), kind: kind, expansion_kind: expansion_kind } + } + + fn new_bang_invoc( + &self, mac: ast::Mac, attrs: Vec, span: Span, kind: ExpansionKind, + ) -> Invocation { + self.new_invoc(kind, InvocationKind::Bang { + attrs: attrs, + mac: mac, + ident: None, + span: span, + }) } fn with_exts_frame T>(&mut self, macros_escape: bool, f: F) -> T { @@ -573,8 +628,8 @@ fn fold_opt_expr(&mut self, expr: P) -> Option> { expr.and_then(|expr| match expr.node { ast::ExprKind::Mac(mac) => { let invoc = - self.new_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); - expand_mac_invoc(invoc, self).make_opt_expr() + self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); + expand_invoc(invoc, self).make_opt_expr() } _ => Some(expand_expr(expr, self)), }) @@ -624,13 +679,11 @@ fn fold_block(&mut self, block: P) -> P { } fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector { - expand_annotatable(Annotatable::TraitItem(P(i)), self) - .into_iter().map(|i| i.expect_trait_item()).collect() + expand_annotatable(Annotatable::TraitItem(P(i)), self).make_trait_items() } fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector { - expand_annotatable(Annotatable::ImplItem(P(i)), self) - .into_iter().map(|i| i.expect_impl_item()).collect() + expand_annotatable(Annotatable::ImplItem(P(i)), self).make_impl_items() } fn fold_ty(&mut self, ty: P) -> P { From 8be8cf854017e3b41324158868e9097fa6cdcc4d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 28 Aug 2016 03:11:33 +0000 Subject: [PATCH 07/19] Refactor away `expand_item`. --- src/libsyntax/ext/expand.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 66a766a666b..687c5e2fdfb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -345,10 +345,6 @@ fn expand_bang_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { fully_expanded } -pub fn expand_item(it: P, fld: &mut MacroExpander) -> SmallVector> { - expand_annotatable(Annotatable::Item(it), fld).make_items() -} - // does this attribute list contain "macro_use" ? fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool { for attr in attrs { @@ -649,7 +645,7 @@ fn fold_item(&mut self, item: P) -> SmallVector> { ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); - result = expand_item(item, self); + result = expand_annotatable(Annotatable::Item(item), self).make_items(); self.cx.directory = directory; } else { let mut directory = match inner { @@ -658,11 +654,11 @@ fn fold_item(&mut self, item: P) -> SmallVector> { }; directory.pop(); let directory = replace(&mut self.cx.directory, directory); - result = expand_item(item, self); + result = expand_annotatable(Annotatable::Item(item), self).make_items(); self.cx.directory = directory; } } else { - result = expand_item(item, self); + result = expand_annotatable(Annotatable::Item(item), self).make_items(); } result } From 2a83574c6af52419af9f723bcc9a9427942a6be8 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 28 Aug 2016 03:16:03 +0000 Subject: [PATCH 08/19] Refactor out `expand_item` (with better semantics than before). --- src/libsyntax/ext/expand.rs | 75 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 687c5e2fdfb..1b9af0aa467 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -409,42 +409,7 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> Expansion { match a { - Annotatable::Item(it) => match it.node { - ast::ItemKind::Mac(..) => { - if match it.node { - ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), - _ => unreachable!(), - } { - return Expansion::Items(SmallVector::one(it)); - } - it.and_then(|it| match it.node { - ItemKind::Mac(mac) => { - let invoc = - fld.new_invoc(ExpansionKind::Items, InvocationKind::Bang { - mac: mac, attrs: it.attrs, ident: Some(it.ident), span: it.span, - }); - expand_invoc(invoc, fld) - } - _ => unreachable!(), - }) - } - ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => { - let valid_ident = - it.ident.name != keywords::Invalid.name(); - - if valid_ident { - fld.cx.mod_push(it.ident); - } - let macro_use = contains_macro_use(fld, &it.attrs); - let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(it, fld)); - if valid_ident { - fld.cx.mod_pop(); - } - Expansion::Items(result) - }, - _ => Expansion::Items(noop_fold_item(it, fld)), - }, - + Annotatable::Item(it) => Expansion::Items(expand_item(it, fld)), Annotatable::TraitItem(it) => Expansion::TraitItems(expand_trait_item(it.unwrap(), fld)), Annotatable::ImplItem(ii) => Expansion::ImplItems(expand_impl_item(ii.unwrap(), fld)), } @@ -480,6 +445,44 @@ fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> Expansi } } +fn expand_item(item: P, fld: &mut MacroExpander) -> SmallVector> { + match item.node { + ast::ItemKind::Mac(..) => { + if match item.node { + ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), + _ => unreachable!(), + } { + return SmallVector::one(item); + } + item.and_then(|item| match item.node { + ItemKind::Mac(mac) => { + let invoc = + fld.new_invoc(ExpansionKind::Items, InvocationKind::Bang { + mac: mac, attrs: item.attrs, ident: Some(item.ident), span: item.span, + }); + expand_invoc(invoc, fld).make_items() + } + _ => unreachable!(), + }) + } + ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => { + let valid_ident = + item.ident.name != keywords::Invalid.name(); + + if valid_ident { + fld.cx.mod_push(item.ident); + } + let macro_use = contains_macro_use(fld, &item.attrs); + let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(item, fld)); + if valid_ident { + fld.cx.mod_pop(); + } + result + }, + _ => noop_fold_item(item, fld), + } +} + fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) -> SmallVector { match ii.node { From 503a10b34a89995ebea6b7a28aa2465038c99627 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 28 Aug 2016 03:49:56 +0000 Subject: [PATCH 09/19] Clean up module processing. --- src/libsyntax/ext/expand.rs | 56 ++++++++++++++----------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 1b9af0aa467..3e169131ec8 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -465,18 +465,28 @@ fn expand_item(item: P, fld: &mut MacroExpander) -> SmallVector unreachable!(), }) } - ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => { - let valid_ident = - item.ident.name != keywords::Invalid.name(); - - if valid_ident { - fld.cx.mod_push(item.ident); - } + ast::ItemKind::Mod(ast::Mod { inner, .. }) => { + fld.cx.mod_push(item.ident); let macro_use = contains_macro_use(fld, &item.attrs); - let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(item, fld)); - if valid_ident { - fld.cx.mod_pop(); + + let directory = fld.cx.directory.clone(); + if item.span.contains(inner) { + fld.cx.directory.push(&*{ + ::attr::first_attr_value_str_by_name(&item.attrs, "path") + .unwrap_or(item.ident.name.as_str()) + }); + } else { + fld.cx.directory = match inner { + syntax_pos::DUMMY_SP => PathBuf::new(), + _ => PathBuf::from(fld.cx.parse_sess.codemap().span_to_filename(inner)), + }; + fld.cx.directory.pop(); } + + let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(item, fld)); + fld.cx.directory = directory; + + fld.cx.mod_pop(); result }, _ => noop_fold_item(item, fld), @@ -639,31 +649,7 @@ fn fold_pat(&mut self, pat: P) -> P { } fn fold_item(&mut self, item: P) -> SmallVector> { - use std::mem::replace; - let result; - if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node { - if item.span.contains(inner) { - let directory = self.cx.directory.clone(); - self.cx.directory.push(&*{ - ::attr::first_attr_value_str_by_name(&item.attrs, "path") - .unwrap_or(item.ident.name.as_str()) - }); - result = expand_annotatable(Annotatable::Item(item), self).make_items(); - self.cx.directory = directory; - } else { - let mut directory = match inner { - syntax_pos::DUMMY_SP => PathBuf::new(), - _ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)), - }; - directory.pop(); - let directory = replace(&mut self.cx.directory, directory); - result = expand_annotatable(Annotatable::Item(item), self).make_items(); - self.cx.directory = directory; - } - } else { - result = expand_annotatable(Annotatable::Item(item), self).make_items(); - } - result + expand_annotatable(Annotatable::Item(item), self).make_items() } fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { From 4ed2c0ea7cfc1cc952cc66e78e1b7117367de2c4 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 30 Aug 2016 23:03:52 +0000 Subject: [PATCH 10/19] Refactor `expand_*` into `expander.fold_*`. --- src/libsyntax/ext/expand.rs | 408 +++++++++++++++++------------------- 1 file changed, 194 insertions(+), 214 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3e169131ec8..d3f8618aace 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -9,7 +9,7 @@ // except according to those terms. use ast::{Block, Crate, Ident, Mac_, PatKind}; -use ast::{MacStmtStyle, Stmt, StmtKind, ItemKind}; +use ast::{MacStmtStyle, StmtKind, ItemKind}; use ast; use ext::hygiene::Mark; use attr::{self, HasAttrs}; @@ -143,15 +143,6 @@ enum InvocationKind { }, } -pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P { - if let ast::ExprKind::Mac(mac) = expr.node { - let invoc = fld.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); - expand_invoc(invoc, fld).make_expr() - } else { - P(noop_fold_expr(expr, fld)) - } -} - fn expand_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { match invoc.kind { InvocationKind::Bang { .. } => expand_bang_invoc(invoc, fld), @@ -345,191 +336,6 @@ fn expand_bang_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { fully_expanded } -// does this attribute list contain "macro_use" ? -fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool { - for attr in attrs { - let mut is_use = attr.check_name("macro_use"); - if attr.check_name("macro_escape") { - let mut err = - fld.cx.struct_span_warn(attr.span, - "macro_escape is a deprecated synonym for macro_use"); - 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() { - fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"); - } - return true; - } - } - false -} - -/// Expand a stmt -fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { - let (mac, style, attrs) = match stmt.node { - StmtKind::Mac(mac) => mac.unwrap(), - _ => return noop_fold_stmt(stmt, fld) - }; - - let invoc = fld.new_bang_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); - let mut fully_expanded = expand_invoc(invoc, fld).make_stmts(); - - // If this is a macro invocation with a semicolon, then apply that - // semicolon to the final statement produced by expansion. - if style == MacStmtStyle::Semicolon { - if let Some(stmt) = fully_expanded.pop() { - fully_expanded.push(stmt.add_trailing_semicolon()); - } - } - - fully_expanded -} - -fn expand_pat(p: P, fld: &mut MacroExpander) -> P { - match p.node { - PatKind::Mac(_) => {} - _ => return noop_fold_pat(p, fld) - } - p.and_then(|p| match p.node { - PatKind::Mac(mac) => { - let invoc = fld.new_bang_invoc(mac, Vec::new(), p.span, ExpansionKind::Pat); - expand_invoc(invoc, fld).make_pat() - } - _ => unreachable!(), - }) -} - -fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> Expansion { - match a { - Annotatable::Item(it) => Expansion::Items(expand_item(it, fld)), - Annotatable::TraitItem(it) => Expansion::TraitItems(expand_trait_item(it.unwrap(), fld)), - Annotatable::ImplItem(ii) => Expansion::ImplItems(expand_impl_item(ii.unwrap(), fld)), - } -} - -fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> Expansion { - let mut attr = None; - item = item.map_attrs(|mut attrs| { - for i in 0..attrs.len() { - if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) { - match *extension { - MultiModifier(..) | MultiDecorator(..) => { - attr = Some(attrs.remove(i)); - break; - } - _ => {} - } - } - } - attrs - }); - - if let Some(attr) = attr { - let kind = match item { - Annotatable::Item(_) => ExpansionKind::Items, - Annotatable::ImplItem(_) => ExpansionKind::ImplItems, - Annotatable::TraitItem(_) => ExpansionKind::TraitItems, - }; - let invoc = fld.new_invoc(kind, InvocationKind::Attr { attr: attr, item: item }); - expand_invoc(invoc, fld) - } else { - expand_multi_modified(item, fld) - } -} - -fn expand_item(item: P, fld: &mut MacroExpander) -> SmallVector> { - match item.node { - ast::ItemKind::Mac(..) => { - if match item.node { - ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), - _ => unreachable!(), - } { - return SmallVector::one(item); - } - item.and_then(|item| match item.node { - ItemKind::Mac(mac) => { - let invoc = - fld.new_invoc(ExpansionKind::Items, InvocationKind::Bang { - mac: mac, attrs: item.attrs, ident: Some(item.ident), span: item.span, - }); - expand_invoc(invoc, fld).make_items() - } - _ => unreachable!(), - }) - } - ast::ItemKind::Mod(ast::Mod { inner, .. }) => { - fld.cx.mod_push(item.ident); - let macro_use = contains_macro_use(fld, &item.attrs); - - let directory = fld.cx.directory.clone(); - if item.span.contains(inner) { - fld.cx.directory.push(&*{ - ::attr::first_attr_value_str_by_name(&item.attrs, "path") - .unwrap_or(item.ident.name.as_str()) - }); - } else { - fld.cx.directory = match inner { - syntax_pos::DUMMY_SP => PathBuf::new(), - _ => PathBuf::from(fld.cx.parse_sess.codemap().span_to_filename(inner)), - }; - fld.cx.directory.pop(); - } - - let result = fld.with_exts_frame(macro_use, |fld| noop_fold_item(item, fld)); - fld.cx.directory = directory; - - fld.cx.mod_pop(); - result - }, - _ => noop_fold_item(item, fld), - } -} - -fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) - -> SmallVector { - match ii.node { - ast::ImplItemKind::Macro(mac) => { - let invoc = fld.new_bang_invoc(mac, ii.attrs, ii.span, ExpansionKind::ImplItems); - expand_invoc(invoc, fld).make_impl_items() - } - _ => fold::noop_fold_impl_item(ii, fld) - } -} - -fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander) - -> SmallVector { - match ti.node { - ast::TraitItemKind::Macro(mac) => { - let invoc = fld.new_bang_invoc(mac, ti.attrs, ti.span, ExpansionKind::TraitItems); - expand_invoc(invoc, fld).make_trait_items() - } - _ => fold::noop_fold_trait_item(ti, fld) - } -} - -pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { - let t = match t.node { - ast::TyKind::Mac(_) => t.unwrap(), - _ => return fold::noop_fold_ty(t, fld), - }; - - match t.node { - ast::TyKind::Mac(mac) => { - let invoc = fld.new_bang_invoc(mac, Vec::new(), t.span, ExpansionKind::Ty); - expand_invoc(invoc, fld).make_ty() - } - _ => unreachable!(), - } -} - /// A tree-folder that performs macro expansion pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, @@ -612,6 +418,56 @@ fn new_bang_invoc( }) } + fn new_attr_invoc(&self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) + -> Invocation { + self.new_invoc(kind, InvocationKind::Attr { attr: attr, item: item }) + } + + // If `item` is an attr invocation, remove and return the macro attribute. + fn classify_item(&self, mut item: T) -> (T, Option) { + 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; + } + _ => {} + } + } + } + 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 with_exts_frame T>(&mut self, macros_escape: bool, f: F) -> T { self.cx.syntax_env.push_frame(); self.cx.syntax_env.info().macros_escape = macros_escape; @@ -630,30 +486,59 @@ fn fold_crate(&mut self, c: Crate) -> Crate { } fn fold_expr(&mut self, expr: P) -> P { - expr.and_then(|expr| expand_expr(expr, self)) + let expr = expr.unwrap(); + if let ast::ExprKind::Mac(mac) = expr.node { + let invoc = self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); + expand_invoc(invoc, self).make_expr() + } else { + P(noop_fold_expr(expr, self)) + } } fn fold_opt_expr(&mut self, expr: P) -> Option> { - expr.and_then(|expr| match expr.node { - ast::ExprKind::Mac(mac) => { - let invoc = - self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); - expand_invoc(invoc, self).make_opt_expr() - } - _ => Some(expand_expr(expr, self)), - }) + let expr = expr.unwrap(); + if let ast::ExprKind::Mac(mac) = expr.node { + let invoc = + self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); + expand_invoc(invoc, self).make_opt_expr() + } else { + Some(P(noop_fold_expr(expr, self))) + } } fn fold_pat(&mut self, pat: P) -> P { - expand_pat(pat, self) - } + match pat.node { + PatKind::Mac(_) => {} + _ => return noop_fold_pat(pat, self) + } - fn fold_item(&mut self, item: P) -> SmallVector> { - expand_annotatable(Annotatable::Item(item), self).make_items() + pat.and_then(|pat| match pat.node { + PatKind::Mac(mac) => { + let invoc = self.new_bang_invoc(mac, Vec::new(), pat.span, ExpansionKind::Pat); + expand_invoc(invoc, self).make_pat() + } + _ => unreachable!(), + }) } fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { - expand_stmt(stmt, self) + let (mac, style, attrs) = match stmt.node { + StmtKind::Mac(mac) => mac.unwrap(), + _ => return noop_fold_stmt(stmt, self) + }; + + let invoc = self.new_bang_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); + let mut fully_expanded = expand_invoc(invoc, self).make_stmts(); + + // If this is a macro invocation with a semicolon, then apply that + // semicolon to the final statement produced by expansion. + if style == MacStmtStyle::Semicolon { + if let Some(stmt) = fully_expanded.pop() { + fully_expanded.push(stmt.add_trailing_semicolon()); + } + } + + fully_expanded } fn fold_block(&mut self, block: P) -> P { @@ -663,16 +548,111 @@ fn fold_block(&mut self, block: P) -> P { result } - fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector { - expand_annotatable(Annotatable::TraitItem(P(i)), self).make_trait_items() + fn fold_item(&mut self, item: P) -> SmallVector> { + let (item, attr) = self.classify_item(item); + if let Some(attr) = attr { + let invoc = self.new_attr_invoc(attr, Annotatable::Item(item), ExpansionKind::Items); + return expand_invoc(invoc, self).make_items(); + } + + match item.node { + ast::ItemKind::Mac(..) => { + if match item.node { + ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(), + _ => unreachable!(), + } { + return SmallVector::one(item); + } + + item.and_then(|item| match item.node { + ItemKind::Mac(mac) => { + let invoc = self.new_invoc(ExpansionKind::Items, InvocationKind::Bang { + mac: mac, + attrs: item.attrs, + ident: Some(item.ident), + span: item.span, + }); + expand_invoc(invoc, self).make_items() + } + _ => unreachable!(), + }) + } + ast::ItemKind::Mod(ast::Mod { inner, .. }) => { + self.cx.mod_push(item.ident); + let macro_use = self.contains_macro_use(&item.attrs); + + let directory = self.cx.directory.clone(); + if item.span.contains(inner) { + self.cx.directory.push(&*{ + ::attr::first_attr_value_str_by_name(&item.attrs, "path") + .unwrap_or(item.ident.name.as_str()) + }); + } else { + self.cx.directory = match inner { + syntax_pos::DUMMY_SP => PathBuf::new(), + _ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)), + }; + self.cx.directory.pop(); + } + let result = self.with_exts_frame(macro_use, |this| noop_fold_item(item, this)); + self.cx.directory = directory; + + self.cx.mod_pop(); + result + }, + _ => noop_fold_item(item, self), + } } - fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector { - expand_annotatable(Annotatable::ImplItem(P(i)), self).make_impl_items() + fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { + let (item, attr) = self.classify_item(item); + if let Some(attr) = attr { + let item = Annotatable::TraitItem(P(item)); + let invoc = self.new_attr_invoc(attr, item, ExpansionKind::TraitItems); + return expand_invoc(invoc, self).make_trait_items(); + } + + match item.node { + ast::TraitItemKind::Macro(mac) => { + let ast::TraitItem { attrs, span, .. } = item; + let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::TraitItems); + expand_invoc(invoc, self).make_trait_items() + } + _ => fold::noop_fold_trait_item(item, self), + } + } + + fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector { + let (item, attr) = self.classify_item(item); + if let Some(attr) = attr { + let item = Annotatable::ImplItem(P(item)); + let invoc = self.new_attr_invoc(attr, item, ExpansionKind::ImplItems); + return expand_invoc(invoc, self).make_impl_items(); + } + + match item.node { + ast::ImplItemKind::Macro(mac) => { + let ast::ImplItem { attrs, span, .. } = item; + let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::ImplItems); + expand_invoc(invoc, self).make_impl_items() + } + _ => fold::noop_fold_impl_item(item, self) + } } fn fold_ty(&mut self, ty: P) -> P { - expand_type(ty, self) + let ty = match ty.node { + ast::TyKind::Mac(_) => ty.unwrap(), + _ => return fold::noop_fold_ty(ty, self), + }; + + match ty.node { + ast::TyKind::Mac(mac) => { + let invoc = self.new_bang_invoc(mac, Vec::new(), ty.span, ExpansionKind::Ty); + expand_invoc(invoc, self).make_ty() + } + _ => unreachable!(), + } } } From 79fa9eb643cfbb807813afed6f825f6654ee7662 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 1 Sep 2016 06:44:54 +0000 Subject: [PATCH 11/19] Refactor `SyntaxEnv`. --- src/libsyntax/ext/base.rs | 171 +++++++++++++++------------- src/libsyntax/ext/expand.rs | 64 ++++------- src/libsyntax/ext/source_util.rs | 8 +- src/libsyntax/ext/tt/macro_rules.rs | 4 +- 4 files changed, 120 insertions(+), 127 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 769a5af0262..fe2806891b8 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -24,6 +24,7 @@ use parse::token; use parse::token::{InternedString, intern, 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; @@ -463,19 +464,6 @@ pub enum SyntaxExtension { pub type NamedSyntaxExtension = (Name, SyntaxExtension); -pub struct BlockInfo { - /// Should macros escape from this scope? - pub macros_escape: bool, -} - -impl BlockInfo { - pub fn new() -> BlockInfo { - BlockInfo { - macros_escape: false, - } - } -} - /// The base map of methods for expanding syntax extension /// AST nodes into full ASTs fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) @@ -586,15 +574,11 @@ pub struct ExtCtxt<'a> { pub crate_root: Option<&'static str>, pub loader: &'a mut MacroLoader, - pub mod_path: Vec , pub exported_macros: Vec, pub syntax_env: SyntaxEnv, pub derive_modes: HashMap>, pub recursion_count: usize, - - pub directory: PathBuf, - pub in_block: bool, } impl<'a> ExtCtxt<'a> { @@ -602,22 +586,17 @@ pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, loader: &'a mut MacroLoader) -> ExtCtxt<'a> { - let env = initial_syntax_expander_table(&ecfg); ExtCtxt { + syntax_env: initial_syntax_expander_table(&ecfg), parse_sess: parse_sess, cfg: cfg, backtrace: NO_EXPANSION, - mod_path: Vec::new(), ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), loader: loader, - syntax_env: env, derive_modes: HashMap::new(), recursion_count: 0, - - directory: PathBuf::new(), - in_block: false, } } @@ -666,14 +645,6 @@ pub fn expansion_cause(&self) -> Span { last_macro.expect("missing expansion backtrace") } - pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); } - pub fn mod_pop(&mut self) { self.mod_path.pop().unwrap(); } - pub fn mod_path(&self) -> Vec { - let mut v = Vec::new(); - v.push(token::str_to_ident(&self.ecfg.crate_name)); - v.extend(self.mod_path.iter().cloned()); - return v; - } pub fn bt_push(&mut self, ei: ExpnInfo) { self.recursion_count += 1; if self.recursion_count > self.ecfg.recursion_limit { @@ -818,6 +789,30 @@ pub fn suggest_macro_name(&mut self, } } } + + pub fn initialize(&mut self, user_exts: Vec, krate: &ast::Crate) { + if std_inject::no_core(&krate) { + self.crate_root = None; + } else if std_inject::no_std(&krate) { + self.crate_root = Some("core"); + } else { + self.crate_root = Some("std"); + } + + // User extensions must be added before expander.load_macros is called, + // so that macros from external crates shadow user defined extensions. + for (name, extension) in user_exts { + self.syntax_env.insert(name, extension); + } + + self.syntax_env.current_module = Module(0); + let mut paths = ModulePaths { + 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); + } } /// Extract a string literal from the macro expanded version of `expr`, @@ -904,79 +899,103 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, /// /// This environment maps Names to SyntaxExtensions. pub struct SyntaxEnv { - chain: Vec, + module_data: Vec, + current_module: Module, + /// All bang-style macro/extension names /// encountered so far; to be used for diagnostics in resolve pub names: HashSet, } -// impl question: how to implement it? Initially, the -// env will contain only macros, so it might be painful -// to add an empty frame for every context. Let's just -// get it working, first.... +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Module(u32); -// NB! the mutability of the underlying maps means that -// if expansion is out-of-order, a deeper scope may be -// able to refer to a macro that was added to an enclosing -// scope lexically later than the deeper scope. +struct ModuleData { + parent: Module, + paths: Rc, + macros: HashMap>, + macros_escape: bool, + in_block: bool, +} -struct MapChainFrame { - info: BlockInfo, - map: HashMap>, +#[derive(Clone)] +pub struct ModulePaths { + pub mod_path: Vec, + pub directory: PathBuf, } impl SyntaxEnv { fn new() -> SyntaxEnv { - let mut map = SyntaxEnv { chain: Vec::new() , names: HashSet::new()}; - map.push_frame(); - map + 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 } - pub fn push_frame(&mut self) { - self.chain.push(MapChainFrame { - info: BlockInfo::new(), - map: HashMap::new(), - }); + fn data(&self, module: Module) -> &ModuleData { + &self.module_data[module.0 as usize] } - pub fn pop_frame(&mut self) { - assert!(self.chain.len() > 1, "too many pops on MapChain!"); - self.chain.pop(); + pub fn set_current_module(&mut self, module: Module) -> Module { + ::std::mem::replace(&mut self.current_module, module) } - fn find_escape_frame(&mut self) -> &mut MapChainFrame { - for (i, frame) in self.chain.iter_mut().enumerate().rev() { - if !frame.info.macros_escape || i == 0 { - return frame + pub fn paths(&self) -> Rc { + 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) + -> 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> { + 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()); } - } - unreachable!() - } - - pub fn find(&self, k: Name) -> Option> { - for frame in self.chain.iter().rev() { - if let Some(v) = frame.map.get(&k) { - return Some(v.clone()); + if module == module_data.parent { + return None; } + module = module_data.parent; } - None } - pub fn insert(&mut self, k: Name, v: SyntaxExtension) { - if let NormalTT(..) = v { - self.names.insert(k); + pub fn insert(&mut self, name: Name, ext: SyntaxExtension) { + if let NormalTT(..) = ext { + self.names.insert(name); } - self.find_escape_frame().map.insert(k, Rc::new(v)); - } - pub fn info(&mut self) -> &mut BlockInfo { - let last_chain_index = self.chain.len() - 1; - &mut self.chain[last_chain_index].info + 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 { // The first frame is pushed in `SyntaxEnv::new()` and the second frame is // pushed when folding the crate root pseudo-module (c.f. noop_fold_crate). - self.chain.len() <= 2 + self.current_module.0 <= 1 } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d3f8618aace..a713196032c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -26,9 +26,9 @@ use util::small_vector::SmallVector; use visit; use visit::Visitor; -use std_inject; use std::path::PathBuf; +use std::rc::Rc; macro_rules! expansions { ($($kind:ident: $ty:ty, $kind_name:expr, .$make:ident, @@ -467,24 +467,9 @@ fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { } false } - - fn with_exts_frame T>(&mut self, macros_escape: bool, f: F) -> T { - self.cx.syntax_env.push_frame(); - self.cx.syntax_env.info().macros_escape = macros_escape; - let result = f(self); - self.cx.syntax_env.pop_frame(); - result - } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { - fn fold_crate(&mut self, c: Crate) -> Crate { - let mut directory = PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(c.span)); - directory.pop(); - self.cx.directory = directory; - noop_fold_crate(c, self) - } - fn fold_expr(&mut self, expr: P) -> P { let expr = expr.unwrap(); if let ast::ExprKind::Mac(mac) = expr.node { @@ -542,9 +527,12 @@ fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { } fn fold_block(&mut self, block: P) -> P { - let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true); - let result = self.with_exts_frame(false, |this| noop_fold_block(block, this)); - self.cx.in_block = was_in_block; + let paths = self.cx.syntax_env.paths(); + let module = self.cx.syntax_env.add_module(false, true, paths); + let orig_module = self.cx.syntax_env.set_current_module(module); + + let result = noop_fold_block(block, self); + self.cx.syntax_env.set_current_module(orig_module); result } @@ -578,26 +566,27 @@ fn fold_item(&mut self, item: P) -> SmallVector> { }) } ast::ItemKind::Mod(ast::Mod { inner, .. }) => { - self.cx.mod_push(item.ident); - let macro_use = self.contains_macro_use(&item.attrs); - - let directory = self.cx.directory.clone(); + let mut paths = (*self.cx.syntax_env.paths()).clone(); + paths.mod_path.push(item.ident); if item.span.contains(inner) { - self.cx.directory.push(&*{ + paths.directory.push(&*{ ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); } else { - self.cx.directory = match inner { + paths.directory = match inner { syntax_pos::DUMMY_SP => PathBuf::new(), _ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)), }; - self.cx.directory.pop(); + paths.directory.pop(); } - let result = self.with_exts_frame(macro_use, |this| noop_fold_item(item, this)); - self.cx.directory = directory; - self.cx.mod_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 = self.cx.syntax_env.set_current_module(module); + let result = noop_fold_item(item, self); + self.cx.syntax_env.set_current_module(module); result }, _ => noop_fold_item(item, self), @@ -744,19 +733,7 @@ pub fn expand_crate(cx: &mut ExtCtxt, pub fn expand_crate_with_expander(expander: &mut MacroExpander, user_exts: Vec, mut c: Crate) -> Crate { - if std_inject::no_core(&c) { - expander.cx.crate_root = None; - } else if std_inject::no_std(&c) { - expander.cx.crate_root = Some("core"); - } else { - expander.cx.crate_root = Some("std"); - } - - // User extensions must be added before expander.load_macros is called, - // so that macros from external crates shadow user defined extensions. - for (name, extension) in user_exts { - expander.cx.syntax_env.insert(name, extension); - } + expander.cx.initialize(user_exts, &c); let items = Expansion::Items(SmallVector::many(c.module.items)); let configured = items.fold_with(&mut expander.strip_unconfigured()); @@ -765,12 +742,11 @@ pub fn expand_crate_with_expander(expander: &mut MacroExpander, let err_count = expander.cx.parse_sess.span_diagnostic.err_count(); let mut ret = expander.fold_crate(c); - ret.exported_macros = expander.cx.exported_macros.clone(); - if expander.cx.parse_sess.span_diagnostic.err_count() > err_count { expander.cx.parse_sess.span_diagnostic.abort_if_errors(); } + ret.exported_macros = expander.cx.exported_macros.clone(); ret } diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 97cb09991ec..105b2261117 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -74,11 +74,9 @@ 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::check_zero_tts(cx, sp, tts, "module_path!"); - let string = cx.mod_path() - .iter() - .map(|x| x.to_string()) - .collect::>() - .join("::"); + let paths = cx.syntax_env.paths(); + let string = paths.mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + base::MacEager::expr(cx.expr_str( sp, token::intern_and_get_ident(&string[..]))) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index a4be84dfbf0..ed80ec9cbc4 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -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.directory.clone(); - p.restrictions = match cx.in_block { + p.directory = cx.syntax_env.paths().directory.clone(); + p.restrictions = match cx.syntax_env.in_block() { true => Restrictions::NO_NONINLINE_MOD, false => Restrictions::empty(), }; From 7a3ae576fa729e0465719f97aa7bb80c9721b446 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 1 Sep 2016 07:01:45 +0000 Subject: [PATCH 12/19] Refactor `expand_invoc(.., fld)` -> `self.expand_invoc(..)`. --- src/libsyntax/ext/expand.rs | 419 ++++++++++++++++++------------------ 1 file changed, 212 insertions(+), 207 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index a713196032c..17b2b2d25b9 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -143,199 +143,6 @@ enum InvocationKind { }, } -fn expand_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { - match invoc.kind { - InvocationKind::Bang { .. } => expand_bang_invoc(invoc, fld), - InvocationKind::Attr { .. } => expand_attr_invoc(invoc, fld), - } -} - -fn expand_attr_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { - let Invocation { expansion_kind: kind, .. } = invoc; - let (attr, item) = match invoc.kind { - InvocationKind::Attr { attr, item } => (attr, item), - _ => unreachable!(), - }; - - let extension = match fld.cx.syntax_env.find(intern(&attr.name())) { - Some(extension) => extension, - None => unreachable!(), - }; - - attr::mark_used(&attr); - fld.cx.bt_push(ExpnInfo { - call_site: attr.span, - callee: NameAndSpan { - format: MacroAttribute(intern(&attr.name())), - span: Some(attr.span), - allow_internal_unstable: false, - } - }); - - let modified = match *extension { - MultiModifier(ref mac) => { - kind.expect_from_annotatables(mac.expand(fld.cx, attr.span, &attr.node.value, item)) - } - MultiDecorator(ref mac) => { - let mut items = Vec::new(); - mac.expand(fld.cx, attr.span, &attr.node.value, &item, &mut |item| items.push(item)); - items.push(item); - kind.expect_from_annotatables(items) - } - _ => unreachable!(), - }; - - fld.cx.bt_pop(); - - let configured = modified.fold_with(&mut fld.strip_unconfigured()); - configured.fold_with(fld) -} - -/// Expand a macro invocation. Returns the result of expansion. -fn expand_bang_invoc(invoc: Invocation, fld: &mut MacroExpander) -> Expansion { - let Invocation { mark, expansion_kind: kind, .. } = invoc; - let (attrs, mac, ident, span) = match invoc.kind { - InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), - _ => unreachable!(), - }; - let Mac_ { path, tts, .. } = mac.node; - - // Detect use of feature-gated or invalid attributes on macro invoations - // since they will not be detected after macro expansion. - for attr in attrs.iter() { - feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic, - &fld.cx.parse_sess.codemap(), - &fld.cx.ecfg.features.unwrap()); - } - - if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() { - fld.cx.span_err(path.span, "expected macro name without module separators"); - return kind.dummy(span); - } - - let extname = path.segments[0].identifier.name; - let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) { - extension - } else { - let mut err = - fld.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname)); - fld.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 { - NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { - if ident.name != keywords::Invalid.name() { - let msg = - format!("macro {}! expects no ident argument, given '{}'", extname, ident); - fld.cx.span_err(path.span, &msg); - return kind.dummy(span); - } - - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: exp_span, - allow_internal_unstable: allow_internal_unstable, - }, - }); - - kind.make_from(expandfun.expand(fld.cx, span, &marked_tts)) - } - - IdentTT(ref expander, tt_span, allow_internal_unstable) => { - if ident.name == keywords::Invalid.name() { - fld.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", extname)); - return kind.dummy(span); - }; - - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: tt_span, - allow_internal_unstable: allow_internal_unstable, - } - }); - - kind.make_from(expander.expand(fld.cx, span, ident, marked_tts)) - } - - MacroRulesTT => { - if ident.name == keywords::Invalid.name() { - fld.cx.span_err(path.span, - &format!("macro {}! expects an ident argument", extname)); - return kind.dummy(span); - }; - - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: None, - // `macro_rules!` doesn't directly allow unstable - // (this is orthogonal to whether the macro it creates allows it) - allow_internal_unstable: false, - } - }); - - let def = ast::MacroDef { - ident: ident, - id: ast::DUMMY_NODE_ID, - span: span, - imported_from: None, - use_locally: true, - body: marked_tts, - export: attr::contains_name(&attrs, "macro_export"), - allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), - attrs: attrs, - }; - - fld.cx.insert_macro(def.clone()); - - // If keep_macs is true, expands to a MacEager::items instead. - if fld.keep_macs { - Some(reconstruct_macro_rules(&def, &path)) - } else { - Some(macro_scope_placeholder()) - } - } - - MultiDecorator(..) | MultiModifier(..) => { - fld.cx.span_err(path.span, - &format!("`{}` can only be used in attributes", extname)); - return kind.dummy(span); - } - }; - - let expanded = if let Some(expanded) = opt_expanded { - expanded - } else { - let msg = format!("non-{kind} macro in {kind} position: {name}", - name = path.segments[0].identifier.name, kind = kind.name()); - fld.cx.span_err(path.span, &msg); - return kind.dummy(span); - }; - - let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) }); - let configured = marked.fold_with(&mut fld.strip_unconfigured()); - fld.load_macros(&configured); - - let fully_expanded = if fld.single_step { - configured - } else { - configured.fold_with(fld) - }; - - fld.cx.bt_pop(); - fully_expanded -} - /// A tree-folder that performs macro expansion pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, @@ -467,6 +274,204 @@ fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { } false } + + fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { + match invoc.kind { + InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), + InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc), + } + } + + fn expand_attr_invoc(&mut self, invoc: Invocation) -> 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, + callee: NameAndSpan { + format: MacroAttribute(intern(&attr.name())), + span: Some(attr.span), + allow_internal_unstable: false, + } + }); + + let modified = match *extension { + MultiModifier(ref mac) => { + let item = mac.expand(self.cx, attr.span, &attr.node.value, item); + kind.expect_from_annotatables(item) + } + MultiDecorator(ref mac) => { + let mut items = Vec::new(); + mac.expand(self.cx, attr.span, &attr.node.value, &item, + &mut |item| items.push(item)); + items.push(item); + kind.expect_from_annotatables(items) + } + _ => unreachable!(), + }; + + self.cx.bt_pop(); + + let configured = modified.fold_with(&mut self.strip_unconfigured()); + configured.fold_with(self) + } + + /// 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; + let (attrs, mac, ident, span) = match invoc.kind { + InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), + _ => unreachable!(), + }; + let Mac_ { path, tts, .. } = mac.node; + + // Detect use of feature-gated or invalid attributes on macro invoations + // since they will not be detected after macro expansion. + for attr in attrs.iter() { + feature_gate::check_attribute(&attr, &self.cx.parse_sess.span_diagnostic, + &self.cx.parse_sess.codemap(), + &self.cx.ecfg.features.unwrap()); + } + + if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() { + self.cx.span_err(path.span, "expected macro name without module separators"); + return kind.dummy(span); + } + + 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 { + NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { + if ident.name != keywords::Invalid.name() { + let msg = + format!("macro {}! expects no ident argument, given '{}'", extname, ident); + self.cx.span_err(path.span, &msg); + return kind.dummy(span); + } + + self.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: exp_span, + allow_internal_unstable: allow_internal_unstable, + }, + }); + + kind.make_from(expandfun.expand(self.cx, span, &marked_tts)) + } + + IdentTT(ref expander, tt_span, allow_internal_unstable) => { + if ident.name == keywords::Invalid.name() { + self.cx.span_err(path.span, + &format!("macro {}! expects an ident argument", extname)); + return kind.dummy(span); + }; + + self.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: tt_span, + allow_internal_unstable: allow_internal_unstable, + } + }); + + kind.make_from(expander.expand(self.cx, span, ident, marked_tts)) + } + + MacroRulesTT => { + if ident.name == keywords::Invalid.name() { + self.cx.span_err(path.span, + &format!("macro {}! expects an ident argument", extname)); + return kind.dummy(span); + }; + + self.cx.bt_push(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(extname), + span: None, + // `macro_rules!` doesn't directly allow unstable + // (this is orthogonal to whether the macro it creates allows it) + allow_internal_unstable: false, + } + }); + + let def = ast::MacroDef { + ident: ident, + id: ast::DUMMY_NODE_ID, + span: span, + imported_from: None, + use_locally: true, + body: marked_tts, + export: attr::contains_name(&attrs, "macro_export"), + allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), + attrs: attrs, + }; + + self.cx.insert_macro(def.clone()); + + // If keep_macs is true, expands to a MacEager::items instead. + if self.keep_macs { + Some(reconstruct_macro_rules(&def, &path)) + } else { + Some(macro_scope_placeholder()) + } + } + + MultiDecorator(..) | MultiModifier(..) => { + self.cx.span_err(path.span, + &format!("`{}` can only be used in attributes", extname)); + return kind.dummy(span); + } + }; + + let expanded = if let Some(expanded) = opt_expanded { + expanded + } else { + let msg = format!("non-{kind} macro in {kind} position: {name}", + name = path.segments[0].identifier.name, kind = kind.name()); + self.cx.span_err(path.span, &msg); + return kind.dummy(span); + }; + + let marked = expanded.fold_with(&mut Marker { + mark: mark, + expn_id: Some(self.cx.backtrace()) + }); + let configured = marked.fold_with(&mut self.strip_unconfigured()); + self.load_macros(&configured); + + let fully_expanded = if self.single_step { + configured + } else { + configured.fold_with(self) + }; + + self.cx.bt_pop(); + fully_expanded + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -474,7 +479,7 @@ fn fold_expr(&mut self, expr: P) -> P { let expr = expr.unwrap(); if let ast::ExprKind::Mac(mac) = expr.node { let invoc = self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); - expand_invoc(invoc, self).make_expr() + self.expand_invoc(invoc).make_expr() } else { P(noop_fold_expr(expr, self)) } @@ -485,7 +490,7 @@ fn fold_opt_expr(&mut self, expr: P) -> Option> { if let ast::ExprKind::Mac(mac) = expr.node { let invoc = self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); - expand_invoc(invoc, self).make_opt_expr() + self.expand_invoc(invoc).make_opt_expr() } else { Some(P(noop_fold_expr(expr, self))) } @@ -494,13 +499,13 @@ fn fold_opt_expr(&mut self, expr: P) -> Option> { fn fold_pat(&mut self, pat: P) -> P { match pat.node { PatKind::Mac(_) => {} - _ => return noop_fold_pat(pat, self) + _ => return noop_fold_pat(pat, self), } pat.and_then(|pat| match pat.node { PatKind::Mac(mac) => { let invoc = self.new_bang_invoc(mac, Vec::new(), pat.span, ExpansionKind::Pat); - expand_invoc(invoc, self).make_pat() + self.expand_invoc(invoc).make_pat() } _ => unreachable!(), }) @@ -509,11 +514,11 @@ fn fold_pat(&mut self, pat: P) -> P { fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { let (mac, style, attrs) = match stmt.node { StmtKind::Mac(mac) => mac.unwrap(), - _ => return noop_fold_stmt(stmt, self) + _ => return noop_fold_stmt(stmt, self), }; let invoc = self.new_bang_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); - let mut fully_expanded = expand_invoc(invoc, self).make_stmts(); + let mut fully_expanded = self.expand_invoc(invoc).make_stmts(); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -540,7 +545,7 @@ fn fold_item(&mut self, item: P) -> SmallVector> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { let invoc = self.new_attr_invoc(attr, Annotatable::Item(item), ExpansionKind::Items); - return expand_invoc(invoc, self).make_items(); + return self.expand_invoc(invoc).make_items(); } match item.node { @@ -560,7 +565,7 @@ fn fold_item(&mut self, item: P) -> SmallVector> { ident: Some(item.ident), span: item.span, }); - expand_invoc(invoc, self).make_items() + self.expand_invoc(invoc).make_items() } _ => unreachable!(), }) @@ -598,14 +603,14 @@ fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { let ast::TraitItem { attrs, span, .. } = item; let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::TraitItems); - expand_invoc(invoc, self).make_trait_items() + self.expand_invoc(invoc).make_trait_items() } _ => fold::noop_fold_trait_item(item, self), } @@ -616,16 +621,16 @@ fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector if let Some(attr) = attr { let item = Annotatable::ImplItem(P(item)); let invoc = self.new_attr_invoc(attr, item, ExpansionKind::ImplItems); - return expand_invoc(invoc, self).make_impl_items(); + return self.expand_invoc(invoc).make_impl_items(); } match item.node { ast::ImplItemKind::Macro(mac) => { let ast::ImplItem { attrs, span, .. } = item; let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::ImplItems); - expand_invoc(invoc, self).make_impl_items() + self.expand_invoc(invoc).make_impl_items() } - _ => fold::noop_fold_impl_item(item, self) + _ => fold::noop_fold_impl_item(item, self), } } @@ -638,7 +643,7 @@ fn fold_ty(&mut self, ty: P) -> P { match ty.node { ast::TyKind::Mac(mac) => { let invoc = self.new_bang_invoc(mac, Vec::new(), ty.span, ExpansionKind::Ty); - expand_invoc(invoc, self).make_ty() + self.expand_invoc(invoc).make_ty() } _ => unreachable!(), } From c07ff8d26a6d5c8728419ae4e870b3a65940efbc Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 29 Aug 2016 05:32:41 +0000 Subject: [PATCH 13/19] Add module `ext::placeholders` with `placeholder()` and `PlaceholderExpander`. --- src/libsyntax/ext/expand.rs | 48 ++------- src/libsyntax/ext/placeholders.rs | 172 ++++++++++++++++++++++++++++++ src/libsyntax/lib.rs | 1 + 3 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 src/libsyntax/ext/placeholders.rs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 17b2b2d25b9..c5fc149bb7e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -12,8 +12,9 @@ use ast::{MacStmtStyle, StmtKind, ItemKind}; use ast; use ext::hygiene::Mark; +use ext::placeholders; use attr::{self, HasAttrs}; -use codemap::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; +use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use syntax_pos::{self, Span, ExpnId}; use config::StripUnconfigured; use ext::base::*; @@ -35,8 +36,8 @@ macro_rules! expansions { $(.$fold:ident)* $(lift .$fold_elt:ident)*, $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone)] - enum ExpansionKind { OptExpr, $( $kind, )* } - enum Expansion { OptExpr(Option>), $( $kind($ty), )* } + pub enum ExpansionKind { OptExpr, $( $kind, )* } + pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } impl ExpansionKind { fn name(self) -> &'static str { @@ -55,20 +56,20 @@ fn make_from<'a>(self, result: Box) -> Option { } impl Expansion { - fn make_opt_expr(self) -> Option> { + pub fn make_opt_expr(self) -> Option> { match self { Expansion::OptExpr(expr) => expr, _ => panic!("Expansion::make_* called on the wrong kind of expansion"), } } - $( fn $make(self) -> $ty { + $( pub fn $make(self) -> $ty { match self { Expansion::$kind(ast) => ast, _ => panic!("Expansion::make_* called on the wrong kind of expansion"), } } )* - fn fold_with(self, folder: &mut F) -> Self { + pub fn fold_with(self, folder: &mut F) -> Self { use self::Expansion::*; match self { OptExpr(expr) => OptExpr(expr.and_then(|expr| folder.fold_opt_expr(expr))), @@ -434,9 +435,9 @@ fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { // If keep_macs is true, expands to a MacEager::items instead. if self.keep_macs { - Some(reconstruct_macro_rules(&def, &path)) + Some(placeholders::reconstructed_macro_rules(&def, &path)) } else { - Some(macro_scope_placeholder()) + Some(placeholders::macro_scope_placeholder()) } } @@ -650,37 +651,6 @@ fn fold_ty(&mut self, ty: P) -> P { } } -fn macro_scope_placeholder() -> Expansion { - Expansion::Items(SmallVector::one(P(ast::Item { - ident: keywords::Invalid.ident(), - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mac(dummy_spanned(ast::Mac_ { - path: ast::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() }, - tts: Vec::new(), - })), - vis: ast::Visibility::Inherited, - span: syntax_pos::DUMMY_SP, - }))) -} - -fn reconstruct_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion { - Expansion::Items(SmallVector::one(P(ast::Item { - ident: def.ident, - attrs: def.attrs.clone(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mac(ast::Mac { - span: def.span, - node: ast::Mac_ { - path: path.clone(), - tts: def.body.clone(), - } - }), - vis: ast::Visibility::Inherited, - span: def.span, - }))) -} - pub struct ExpansionConfig<'feat> { pub crate_name: String, pub features: Option<&'feat Features>, diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs new file mode 100644 index 00000000000..f1665bdde75 --- /dev/null +++ b/src/libsyntax/ext/placeholders.rs @@ -0,0 +1,172 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast; +use codemap::{DUMMY_SP, dummy_spanned}; +use ext::expand::{Expansion, ExpansionKind}; +use fold::*; +use parse::token::keywords; +use ptr::P; +use util::small_vector::SmallVector; + +use std::collections::HashMap; + +pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { + fn mac_placeholder() -> ast::Mac { + dummy_spanned(ast::Mac_ { + path: ast::Path { span: DUMMY_SP, global: false, segments: Vec::new() }, + tts: Vec::new(), + }) + } + + let ident = keywords::Invalid.ident(); + let attrs = Vec::new(); + let vis = ast::Visibility::Inherited; + let span = DUMMY_SP; + let expr_placeholder = || P(ast::Expr { + id: id, span: span, + attrs: ast::ThinVec::new(), + node: ast::ExprKind::Mac(mac_placeholder()), + }); + + match kind { + ExpansionKind::Expr => Expansion::Expr(expr_placeholder()), + ExpansionKind::OptExpr => Expansion::OptExpr(Some(expr_placeholder())), + ExpansionKind::Items => Expansion::Items(SmallVector::one(P(ast::Item { + id: id, span: span, ident: ident, vis: vis, attrs: attrs, + node: ast::ItemKind::Mac(mac_placeholder()), + }))), + ExpansionKind::TraitItems => Expansion::TraitItems(SmallVector::one(ast::TraitItem { + id: id, span: span, ident: ident, attrs: attrs, + node: ast::TraitItemKind::Macro(mac_placeholder()), + })), + ExpansionKind::ImplItems => Expansion::ImplItems(SmallVector::one(ast::ImplItem { + id: id, span: span, ident: ident, vis: vis, attrs: attrs, + node: ast::ImplItemKind::Macro(mac_placeholder()), + defaultness: ast::Defaultness::Final, + })), + ExpansionKind::Pat => Expansion::Pat(P(ast::Pat { + id: id, span: span, node: ast::PatKind::Mac(mac_placeholder()), + })), + ExpansionKind::Ty => Expansion::Ty(P(ast::Ty { + id: id, span: span, node: ast::TyKind::Mac(mac_placeholder()), + })), + ExpansionKind::Stmts => Expansion::Stmts(SmallVector::one({ + let mac = P((mac_placeholder(), ast::MacStmtStyle::Braces, ast::ThinVec::new())); + ast::Stmt { id: id, span: span, node: ast::StmtKind::Mac(mac) } + })), + } +} + +pub fn macro_scope_placeholder() -> Expansion { + placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) +} + +pub struct PlaceholderExpander { + expansions: HashMap, +} + +impl PlaceholderExpander { + pub fn new(expansions: HashMap) -> Self { + PlaceholderExpander { + expansions: expansions, + } + } + + pub fn remove(&mut self, id: ast::NodeId) -> Expansion { + let expansion = self.expansions.remove(&id).unwrap(); + expansion.fold_with(self) + } +} + +impl Folder for PlaceholderExpander { + fn fold_item(&mut self, item: P) -> SmallVector> { + match item.node { + // Scope placeholder + ast::ItemKind::Mac(_) if item.id == ast::DUMMY_NODE_ID => SmallVector::one(item), + ast::ItemKind::Mac(_) => self.remove(item.id).make_items(), + _ => noop_fold_item(item, self), + } + } + + fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { + match item.node { + ast::TraitItemKind::Macro(_) => self.remove(item.id).make_trait_items(), + _ => noop_fold_trait_item(item, self), + } + } + + fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector { + match item.node { + ast::ImplItemKind::Macro(_) => self.remove(item.id).make_impl_items(), + _ => noop_fold_impl_item(item, self), + } + } + + fn fold_expr(&mut self, expr: P) -> P { + match expr.node { + ast::ExprKind::Mac(_) => self.remove(expr.id).make_expr(), + _ => expr.map(|expr| noop_fold_expr(expr, self)), + } + } + + fn fold_opt_expr(&mut self, expr: P) -> Option> { + match expr.node { + ast::ExprKind::Mac(_) => self.remove(expr.id).make_opt_expr(), + _ => noop_fold_opt_expr(expr, self), + } + } + + fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { + let (style, mut expansion) = match stmt.node { + ast::StmtKind::Mac(mac) => (mac.1, self.remove(stmt.id).make_stmts()), + _ => return noop_fold_stmt(stmt, self), + }; + + if style == ast::MacStmtStyle::Semicolon { + if let Some(stmt) = expansion.pop() { + expansion.push(stmt.add_trailing_semicolon()); + } + } + + expansion + } + + fn fold_pat(&mut self, pat: P) -> P { + match pat.node { + ast::PatKind::Mac(_) => self.remove(pat.id).make_pat(), + _ => noop_fold_pat(pat, self), + } + } + + fn fold_ty(&mut self, ty: P) -> P { + match ty.node { + ast::TyKind::Mac(_) => self.remove(ty.id).make_ty(), + _ => noop_fold_ty(ty, self), + } + } +} + +pub fn reconstructed_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion { + Expansion::Items(SmallVector::one(P(ast::Item { + ident: def.ident, + attrs: def.attrs.clone(), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Mac(ast::Mac { + span: def.span, + node: ast::Mac_ { + path: path.clone(), + tts: def.body.clone(), + } + }), + vis: ast::Visibility::Inherited, + span: def.span, + }))) +} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 65bc9f34c90..42201231247 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -126,6 +126,7 @@ pub mod ext { pub mod base; pub mod build; pub mod expand; + pub mod placeholders; pub mod hygiene; pub mod proc_macro_shim; pub mod quote; From d986bbe674d4fd554342771e7c031b3d22f9a800 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 09:12:47 +0000 Subject: [PATCH 14/19] Implement stackless expansion. --- src/libsyntax/ext/base.rs | 25 +-- src/libsyntax/ext/expand.rs | 339 +++++++++++++++++++---------------- src/libsyntax/ext/hygiene.rs | 4 + src/libsyntax/test.rs | 7 +- 4 files changed, 194 insertions(+), 181 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index fe2806891b8..edd38ea23e2 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -646,7 +646,6 @@ pub fn expansion_cause(&self) -> Span { } pub fn bt_push(&mut self, ei: ExpnInfo) { - self.recursion_count += 1; if self.recursion_count > self.ecfg.recursion_limit { self.span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", @@ -660,17 +659,7 @@ pub fn bt_push(&mut self, ei: ExpnInfo) { callee: ei.callee }); } - pub fn bt_pop(&mut self) { - match self.backtrace { - NO_EXPANSION => self.bug("tried to pop without a push"), - expn_id => { - self.recursion_count -= 1; - self.backtrace = self.codemap().with_expn_info(expn_id, |expn_info| { - expn_info.map_or(NO_EXPANSION, |ei| ei.call_site.expn_id) - }); - } - } - } + pub fn bt_pop(&mut self) {} pub fn insert_macro(&mut self, def: ast::MacroDef) { if def.export { @@ -799,8 +788,6 @@ pub fn initialize(&mut self, user_exts: Vec, krate: &ast:: self.crate_root = Some("std"); } - // User extensions must be added before expander.load_macros is called, - // so that macros from external crates shadow user defined extensions. for (name, extension) in user_exts { self.syntax_env.insert(name, extension); } @@ -900,7 +887,7 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, /// This environment maps Names to SyntaxExtensions. pub struct SyntaxEnv { module_data: Vec, - current_module: Module, + pub current_module: Module, /// All bang-style macro/extension names /// encountered so far; to be used for diagnostics in resolve @@ -940,10 +927,6 @@ fn data(&self, module: Module) -> &ModuleData { &self.module_data[module.0 as usize] } - pub fn set_current_module(&mut self, module: Module) -> Module { - ::std::mem::replace(&mut self.current_module, module) - } - pub fn paths(&self) -> Rc { self.data(self.current_module).paths.clone() } @@ -994,8 +977,6 @@ pub fn insert(&mut self, name: Name, ext: SyntaxExtension) { } pub fn is_crate_root(&mut self) -> bool { - // The first frame is pushed in `SyntaxEnv::new()` and the second frame is - // pushed when folding the crate root pseudo-module (c.f. noop_fold_crate). - self.current_module.0 <= 1 + self.current_module == Module(0) } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c5fc149bb7e..5624c263466 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -12,7 +12,7 @@ use ast::{MacStmtStyle, StmtKind, ItemKind}; use ast; use ext::hygiene::Mark; -use ext::placeholders; +use ext::placeholders::{self, placeholder, PlaceholderExpander}; use attr::{self, HasAttrs}; use codemap::{ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use syntax_pos::{self, Span, ExpnId}; @@ -28,11 +28,13 @@ use visit; use visit::Visitor; +use std::collections::HashMap; +use std::mem; use std::path::PathBuf; use std::rc::Rc; macro_rules! expansions { - ($($kind:ident: $ty:ty, $kind_name:expr, .$make:ident, + ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident, $(.$fold:ident)* $(lift .$fold_elt:ident)*, $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone)] @@ -91,18 +93,32 @@ fn visit_with(&self, visitor: &mut V) { } } } + + impl<'a, 'b> Folder for MacroExpander<'a, 'b> { + fn fold_opt_expr(&mut self, expr: P) -> Option> { + self.expand(Expansion::OptExpr(Some(expr))).make_opt_expr() + } + $($(fn $fold(&mut self, node: $ty) -> $ty { + self.expand(Expansion::$kind(node)).$make() + })*)* + $($(fn $fold_elt(&mut self, node: $ty_elt) -> $ty { + self.expand(Expansion::$kind(SmallVector::one(node))).$make() + })*)* + } } } expansions! { - Expr: P, "expression", .make_expr, .fold_expr, .visit_expr; - Pat: P, "pattern", .make_pat, .fold_pat, .visit_pat; - Ty: P, "type", .make_ty, .fold_ty, .visit_ty; - Stmts: SmallVector, "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; - Items: SmallVector>, "item", .make_items, lift .fold_item, lift .visit_item; - TraitItems: SmallVector, + Expr: P [], "expression", .make_expr, .fold_expr, .visit_expr; + Pat: P [], "pattern", .make_pat, .fold_pat, .visit_pat; + Ty: P [], "type", .make_ty, .fold_ty, .visit_ty; + Stmts: SmallVector [SmallVector, ast::Stmt], + "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; + Items: SmallVector> [SmallVector, P], + "item", .make_items, lift .fold_item, lift .visit_item; + TraitItems: SmallVector [SmallVector, ast::TraitItem], "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; - ImplItems: SmallVector, + ImplItems: SmallVector [SmallVector, ast::ImplItem], "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; } @@ -129,6 +145,9 @@ pub struct Invocation { kind: InvocationKind, expansion_kind: ExpansionKind, mark: Mark, + module: Module, + backtrace: ExpnId, + depth: usize, } enum InvocationKind { @@ -144,7 +163,6 @@ enum InvocationKind { }, } -/// A tree-folder that performs macro expansion pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, @@ -162,13 +180,57 @@ pub fn new(cx: &'a mut ExtCtxt<'b>, } } - fn strip_unconfigured(&mut self) -> StripUnconfigured { - StripUnconfigured { + 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(); + + if self.cx.parse_sess.span_diagnostic.err_count() > err_count { + self.cx.parse_sess.span_diagnostic.abort_if_errors(); + } + + krate + } + + // Fully expand all the invocations in `expansion`. + fn expand(&mut self, expansion: Expansion) -> Expansion { + let (expansion, mut invocations) = self.collect_invocations(expansion); + invocations.reverse(); + + let mut expansions = HashMap::new(); + 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 expansion = self.expand_invoc(invoc); + + self.cx.syntax_env.current_module = module; + self.cx.recursion_count = depth + 1; + let (expansion, new_invocations) = self.collect_invocations(expansion); + + expansions.insert(mark.as_u32(), expansion); + if !self.single_step { + invocations.extend(new_invocations.into_iter().rev()); + } + } + + expansion.fold_with(&mut PlaceholderExpander::new(expansions)) + } + + fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec) { + let expansion = expansion.fold_with(&mut StripUnconfigured { config: &self.cx.cfg, should_test: self.cx.ecfg.should_test, sess: self.cx.parse_sess, features: self.cx.ecfg.features, - } + }); + self.load_macros(&expansion); + let mut collector = InvocationCollector { cx: self.cx, invocations: Vec::new() }; + (expansion.fold_with(&mut collector), collector.invocations) } fn load_macros(&mut self, node: &Expansion) { @@ -210,72 +272,6 @@ fn visit_block(&mut self, block: &ast::Block) { }); } - fn new_invoc(&self, expansion_kind: ExpansionKind, kind: InvocationKind) - -> Invocation { - Invocation { mark: Mark::fresh(), kind: kind, expansion_kind: expansion_kind } - } - - fn new_bang_invoc( - &self, mac: ast::Mac, attrs: Vec, span: Span, kind: ExpansionKind, - ) -> Invocation { - self.new_invoc(kind, InvocationKind::Bang { - attrs: attrs, - mac: mac, - ident: None, - span: span, - }) - } - - fn new_attr_invoc(&self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) - -> Invocation { - self.new_invoc(kind, InvocationKind::Attr { attr: attr, item: item }) - } - - // If `item` is an attr invocation, remove and return the macro attribute. - fn classify_item(&self, mut item: T) -> (T, Option) { - 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; - } - _ => {} - } - } - } - 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 expand_invoc(&mut self, invoc: Invocation) -> Expansion { match invoc.kind { InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), @@ -305,7 +301,7 @@ fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion { } }); - let modified = match *extension { + match *extension { MultiModifier(ref mac) => { let item = mac.expand(self.cx, attr.span, &attr.node.value, item); kind.expect_from_annotatables(item) @@ -318,12 +314,7 @@ fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion { kind.expect_from_annotatables(items) } _ => unreachable!(), - }; - - self.cx.bt_pop(); - - let configured = modified.fold_with(&mut self.strip_unconfigured()); - configured.fold_with(self) + } } /// Expand a macro invocation. Returns the result of expansion. @@ -457,30 +448,94 @@ fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { return kind.dummy(span); }; - let marked = expanded.fold_with(&mut Marker { + expanded.fold_with(&mut Marker { mark: mark, - expn_id: Some(self.cx.backtrace()) - }); - let configured = marked.fold_with(&mut self.strip_unconfigured()); - self.load_macros(&configured); - - let fully_expanded = if self.single_step { - configured - } else { - configured.fold_with(self) - }; - - self.cx.bt_pop(); - fully_expanded + expn_id: Some(self.cx.backtrace()), + }) } } -impl<'a, 'b> Folder for MacroExpander<'a, 'b> { +struct InvocationCollector<'a, 'b: 'a> { + cx: &'a mut ExtCtxt<'b>, + invocations: Vec, +} + +impl<'a, 'b> InvocationCollector<'a, 'b> { + fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion { + let mark = Mark::fresh(); + 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, + }); + placeholder(expansion_kind, mark.as_u32()) + } + + fn collect_bang( + &mut self, mac: ast::Mac, attrs: Vec, span: Span, kind: ExpansionKind, + ) -> Expansion { + self.collect(kind, InvocationKind::Bang { attrs: attrs, mac: mac, ident: None, span: span }) + } + + fn collect_attr(&mut self, attr: ast::Attribute, item: Annotatable, kind: ExpansionKind) + -> Expansion { + self.collect(kind, InvocationKind::Attr { attr: attr, item: item }) + } + + // If `item` is an attr invocation, remove and return the macro attribute. + fn classify_item(&self, mut item: T) -> (T, Option) { + 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; + } + _ => {} + } + } + } + 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 + } +} + +impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_expr(&mut self, expr: P) -> P { let expr = expr.unwrap(); if let ast::ExprKind::Mac(mac) = expr.node { - let invoc = self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr); - self.expand_invoc(invoc).make_expr() + self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr).make_expr() } else { P(noop_fold_expr(expr, self)) } @@ -489,9 +544,8 @@ fn fold_expr(&mut self, expr: P) -> P { fn fold_opt_expr(&mut self, expr: P) -> Option> { let expr = expr.unwrap(); if let ast::ExprKind::Mac(mac) = expr.node { - let invoc = - self.new_bang_invoc(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr); - self.expand_invoc(invoc).make_opt_expr() + self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr) + .make_opt_expr() } else { Some(P(noop_fold_expr(expr, self))) } @@ -504,10 +558,8 @@ fn fold_pat(&mut self, pat: P) -> P { } pat.and_then(|pat| match pat.node { - PatKind::Mac(mac) => { - let invoc = self.new_bang_invoc(mac, Vec::new(), pat.span, ExpansionKind::Pat); - self.expand_invoc(invoc).make_pat() - } + PatKind::Mac(mac) => + self.collect_bang(mac, Vec::new(), pat.span, ExpansionKind::Pat).make_pat(), _ => unreachable!(), }) } @@ -518,35 +570,34 @@ fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { _ => return noop_fold_stmt(stmt, self), }; - let invoc = self.new_bang_invoc(mac, attrs.into(), stmt.span, ExpansionKind::Stmts); - let mut fully_expanded = self.expand_invoc(invoc).make_stmts(); + let mut placeholder = + self.collect_bang(mac, attrs.into(), stmt.span, ExpansionKind::Stmts).make_stmts(); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. if style == MacStmtStyle::Semicolon { - if let Some(stmt) = fully_expanded.pop() { - fully_expanded.push(stmt.add_trailing_semicolon()); + if let Some(stmt) = placeholder.pop() { + placeholder.push(stmt.add_trailing_semicolon()); } } - fully_expanded + placeholder } fn fold_block(&mut self, block: P) -> P { let paths = self.cx.syntax_env.paths(); let module = self.cx.syntax_env.add_module(false, true, paths); - let orig_module = self.cx.syntax_env.set_current_module(module); - + let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module); let result = noop_fold_block(block, self); - self.cx.syntax_env.set_current_module(orig_module); + self.cx.syntax_env.current_module = orig_module; result } fn fold_item(&mut self, item: P) -> SmallVector> { let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let invoc = self.new_attr_invoc(attr, Annotatable::Item(item), ExpansionKind::Items); - return self.expand_invoc(invoc).make_items(); + let item = Annotatable::Item(item); + return self.collect_attr(attr, item, ExpansionKind::Items).make_items(); } match item.node { @@ -560,13 +611,12 @@ fn fold_item(&mut self, item: P) -> SmallVector> { item.and_then(|item| match item.node { ItemKind::Mac(mac) => { - let invoc = self.new_invoc(ExpansionKind::Items, InvocationKind::Bang { + self.collect(ExpansionKind::Items, InvocationKind::Bang { mac: mac, attrs: item.attrs, ident: Some(item.ident), span: item.span, - }); - self.expand_invoc(invoc).make_items() + }).make_items() } _ => unreachable!(), }) @@ -590,9 +640,9 @@ fn fold_item(&mut self, item: P) -> SmallVector> { 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 = self.cx.syntax_env.set_current_module(module); + let module = mem::replace(&mut self.cx.syntax_env.current_module, module); let result = noop_fold_item(item, self); - self.cx.syntax_env.set_current_module(module); + self.cx.syntax_env.current_module = module; result }, _ => noop_fold_item(item, self), @@ -603,15 +653,13 @@ fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { let ast::TraitItem { attrs, span, .. } = item; - let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::TraitItems); - self.expand_invoc(invoc).make_trait_items() + self.collect_bang(mac, attrs, span, ExpansionKind::TraitItems).make_trait_items() } _ => fold::noop_fold_trait_item(item, self), } @@ -621,15 +669,13 @@ fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector let (item, attr) = self.classify_item(item); if let Some(attr) = attr { let item = Annotatable::ImplItem(P(item)); - let invoc = self.new_attr_invoc(attr, item, ExpansionKind::ImplItems); - return self.expand_invoc(invoc).make_impl_items(); + return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); } match item.node { ast::ImplItemKind::Macro(mac) => { let ast::ImplItem { attrs, span, .. } = item; - let invoc = self.new_bang_invoc(mac, attrs, span, ExpansionKind::ImplItems); - self.expand_invoc(invoc).make_impl_items() + self.collect_bang(mac, attrs, span, ExpansionKind::ImplItems).make_impl_items() } _ => fold::noop_fold_impl_item(item, self), } @@ -642,10 +688,8 @@ fn fold_ty(&mut self, ty: P) -> P { }; match ty.node { - ast::TyKind::Mac(mac) => { - let invoc = self.new_bang_invoc(mac, Vec::new(), ty.span, ExpansionKind::Ty); - self.expand_invoc(invoc).make_ty() - } + ast::TyKind::Mac(mac) => + self.collect_bang(mac, Vec::new(), ty.span, ExpansionKind::Ty).make_ty(), _ => unreachable!(), } } @@ -699,30 +743,17 @@ fn enable_rustc_macro = rustc_macro, pub fn expand_crate(cx: &mut ExtCtxt, user_exts: Vec, c: Crate) -> Crate { - let mut expander = MacroExpander::new(cx, false, false); - expand_crate_with_expander(&mut expander, user_exts, c) + cx.initialize(user_exts, &c); + cx.expander().expand_crate(c) } // Expands crate using supplied MacroExpander - allows for // non-standard expansion behaviour (e.g. step-wise). pub fn expand_crate_with_expander(expander: &mut MacroExpander, user_exts: Vec, - mut c: Crate) -> Crate { + c: Crate) -> Crate { expander.cx.initialize(user_exts, &c); - - let items = Expansion::Items(SmallVector::many(c.module.items)); - let configured = items.fold_with(&mut expander.strip_unconfigured()); - expander.load_macros(&configured); - c.module.items = configured.make_items().into(); - - let err_count = expander.cx.parse_sess.span_diagnostic.err_count(); - let mut ret = expander.fold_crate(c); - if expander.cx.parse_sess.span_diagnostic.err_count() > err_count { - expander.cx.parse_sess.span_diagnostic.abort_if_errors(); - } - - ret.exported_macros = expander.cx.exported_macros.clone(); - ret + expander.expand_crate(c) } // A Marker adds the given mark to the syntax context and diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index ade165e0ef9..27e8eab62e1 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -40,6 +40,10 @@ pub fn fresh() -> Self { ::std::mem::replace(&mut data.next_mark, next_mark) }) } + + pub fn as_u32(&self) -> u32 { + self.0 + } } struct HygieneData { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 6155ad729a2..3108296e778 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -300,14 +300,11 @@ fn generate_test_harness(sess: &ParseSess, } }); - let mut fold = TestHarnessGenerator { + TestHarnessGenerator { cx: cx, tests: Vec::new(), tested_submods: Vec::new(), - }; - let res = fold.fold_crate(krate); - fold.cx.ext_cx.bt_pop(); - return res; + }.fold_crate(krate) } /// Craft a span that will be ignored by the stability lint's From 2c88b4b7908b8451e9c3a3c8d9c8ea43fa78c52a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 02:20:03 +0000 Subject: [PATCH 15/19] Load macros from `extern crate`s in the `InvocationCollector` fold. --- src/libsyntax/ext/expand.rs | 84 ++++++++++--------------------------- 1 file changed, 22 insertions(+), 62 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 5624c263466..0986d32ff56 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -25,8 +25,6 @@ use ptr::P; use tokenstream::TokenTree; use util::small_vector::SmallVector; -use visit; -use visit::Visitor; use std::collections::HashMap; use std::mem; @@ -35,8 +33,7 @@ macro_rules! expansions { ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident, - $(.$fold:ident)* $(lift .$fold_elt:ident)*, - $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { + $(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => { #[derive(Copy, Clone)] pub enum ExpansionKind { OptExpr, $( $kind, )* } pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } @@ -81,17 +78,6 @@ pub fn fold_with(self, folder: &mut F) -> Self { }, )*)* } } - - fn visit_with(&self, visitor: &mut V) { - match *self { - Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), - $($( 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> { @@ -109,17 +95,17 @@ fn fold_opt_expr(&mut self, expr: P) -> Option> { } expansions! { - Expr: P [], "expression", .make_expr, .fold_expr, .visit_expr; - Pat: P [], "pattern", .make_pat, .fold_pat, .visit_pat; - Ty: P [], "type", .make_ty, .fold_ty, .visit_ty; + Expr: P [], "expression", .make_expr, .fold_expr; + Pat: P [], "pattern", .make_pat, .fold_pat; + Ty: P [], "type", .make_ty, .fold_ty; Stmts: SmallVector [SmallVector, ast::Stmt], - "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; + "statement", .make_stmts, lift .fold_stmt; Items: SmallVector> [SmallVector, P], - "item", .make_items, lift .fold_item, lift .visit_item; + "item", .make_items, lift .fold_item; TraitItems: SmallVector [SmallVector, ast::TraitItem], - "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; + "trait item", .make_trait_items, lift .fold_trait_item; ImplItems: SmallVector [SmallVector, ast::ImplItem], - "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; + "impl item", .make_impl_items, lift .fold_impl_item; } impl ExpansionKind { @@ -228,50 +214,10 @@ fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec{ - cx: &'a mut ExtCtxt<'b>, - at_crate_root: bool, - } - - impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> { - fn visit_mac(&mut self, _: &ast::Mac) {} - fn visit_item(&mut self, item: &ast::Item) { - if let ast::ItemKind::ExternCrate(..) = item.node { - // We need to error on `#[macro_use] extern crate` when it isn't at the - // crate root, because `$crate` won't work properly. - for def in self.cx.loader.load_crate(item, self.at_crate_root) { - match def { - LoadedMacro::Def(def) => self.cx.insert_macro(def), - LoadedMacro::CustomDerive(name, ext) => { - self.cx.insert_custom_derive(&name, ext, item.span); - } - } - } - } else { - let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false); - visit::walk_item(self, item); - self.at_crate_root = at_crate_root; - } - } - fn visit_block(&mut self, block: &ast::Block) { - let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false); - visit::walk_block(self, block); - self.at_crate_root = at_crate_root; - } - } - - node.visit_with(&mut MacroLoadingVisitor { - at_crate_root: self.cx.syntax_env.is_crate_root(), - cx: self.cx, - }); - } - fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { match invoc.kind { InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), @@ -645,6 +591,20 @@ fn fold_item(&mut self, item: P) -> SmallVector> { self.cx.syntax_env.current_module = module; 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) { + match def { + LoadedMacro::Def(def) => self.cx.insert_macro(def), + LoadedMacro::CustomDerive(name, ext) => { + self.cx.insert_custom_derive(&name, ext, item.span); + } + } + } + SmallVector::one(item) + }, _ => noop_fold_item(item, self), } } From 3af0c6572eebd2c7fad72e8ba37eac71b8abc8bc Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 01:44:23 +0000 Subject: [PATCH 16/19] Refactor code out of the folder implementation for `StripUnconfigured`. --- src/libsyntax/config.rs | 154 ++++++++++++++++------------- src/libsyntax/util/small_vector.rs | 6 ++ 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 7f6395997ab..07604bffa1c 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -13,7 +13,7 @@ use {fold, attr}; use ast; use codemap::{Spanned, respan}; -use parse::{ParseSess, token}; +use parse::ParseSess; use ptr::P; use util::small_vector::SmallVector; @@ -60,6 +60,15 @@ pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool) (krate, features) } +macro_rules! configure { + ($this:ident, $node:ident) => { + match $this.configure($node) { + Some(node) => node, + None => return Default::default(), + } + } +} + impl<'a> StripUnconfigured<'a> { fn configure(&mut self, node: T) -> Option { let node = self.process_cfg_attrs(node); @@ -156,37 +165,35 @@ fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) { } } } -} -impl<'a> fold::Folder for StripUnconfigured<'a> { - fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { + fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { ast::ForeignMod { abi: foreign_mod.abi, - items: foreign_mod.items.into_iter().filter_map(|item| { - self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self)) - }).collect(), + items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(), } } - fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { - let fold_struct = |this: &mut Self, vdata| match vdata { + fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantData { + match vdata { ast::VariantData::Struct(fields, id) => { - let fields = fields.into_iter().filter_map(|field| this.configure(field)); + let fields = fields.into_iter().filter_map(|field| self.configure(field)); ast::VariantData::Struct(fields.collect(), id) } ast::VariantData::Tuple(fields, id) => { - let fields = fields.into_iter().filter_map(|field| this.configure(field)); + let fields = fields.into_iter().filter_map(|field| self.configure(field)); ast::VariantData::Tuple(fields.collect(), id) } ast::VariantData::Unit(id) => ast::VariantData::Unit(id) - }; + } + } - let item = match item { + fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { + match item { ast::ItemKind::Struct(def, generics) => { - ast::ItemKind::Struct(fold_struct(self, def), generics) + ast::ItemKind::Struct(self.configure_variant_data(def), generics) } ast::ItemKind::Union(def, generics) => { - ast::ItemKind::Union(fold_struct(self, def), generics) + ast::ItemKind::Union(self.configure_variant_data(def), generics) } ast::ItemKind::Enum(def, generics) => { let variants = def.variants.into_iter().filter_map(|v| { @@ -195,7 +202,7 @@ fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { node: ast::Variant_ { name: v.node.name, attrs: v.node.attrs, - data: fold_struct(self, v.node.data), + data: self.configure_variant_data(v.node.data), disr_expr: v.node.disr_expr, }, span: v.span @@ -207,12 +214,19 @@ fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { }, generics) } item => item, - }; - - fold::noop_fold_item_kind(item, self) + } } - fn fold_expr(&mut self, expr: P) -> P { + fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { + if let ast::ExprKind::Match(m, arms) = expr_kind { + let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect(); + ast::ExprKind::Match(m, arms) + } else { + expr_kind + } + } + + fn configure_expr(&mut self, expr: P) -> P { self.visit_stmt_or_expr_attrs(expr.attrs()); // If an expr is valid to cfg away it will have been removed by the @@ -227,62 +241,62 @@ fn fold_expr(&mut self, expr: P) -> P { self.sess.span_diagnostic.span_err(attr.span, msg); } - let expr = self.process_cfg_attrs(expr); - fold_expr(self, expr) + self.process_cfg_attrs(expr) } - fn fold_opt_expr(&mut self, expr: P) -> Option> { - self.configure(expr).map(|expr| fold_expr(self, expr)) - } - - fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { + fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option { self.visit_stmt_or_expr_attrs(stmt.attrs()); - self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self)) - .unwrap_or(SmallVector::zero()) - } - - fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { - fold::noop_fold_mac(mac, self) - } - - fn fold_item(&mut self, item: P) -> SmallVector> { - self.configure(item).map(|item| fold::noop_fold_item(item, self)) - .unwrap_or(SmallVector::zero()) - } - - fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector { - self.configure(item).map(|item| fold::noop_fold_impl_item(item, self)) - .unwrap_or(SmallVector::zero()) - } - - fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { - self.configure(item).map(|item| fold::noop_fold_trait_item(item, self)) - .unwrap_or(SmallVector::zero()) - } - - fn fold_interpolated(&mut self, nt: token::Nonterminal) -> token::Nonterminal { - // Don't configure interpolated AST (c.f. #34171). - // Interpolated AST will get configured once the surrounding tokens are parsed. - nt + self.configure(stmt) } } -fn fold_expr(folder: &mut StripUnconfigured, expr: P) -> P { - expr.map(|ast::Expr {id, span, node, attrs}| { - fold::noop_fold_expr(ast::Expr { - id: id, - node: match node { - ast::ExprKind::Match(m, arms) => { - ast::ExprKind::Match(m, arms.into_iter() - .filter_map(|a| folder.configure(a)) - .collect()) - } - _ => node - }, - span: span, - attrs: attrs, - }, folder) - }) +impl<'a> fold::Folder for StripUnconfigured<'a> { + fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { + let foreign_mod = self.configure_foreign_mod(foreign_mod); + fold::noop_fold_foreign_mod(foreign_mod, self) + } + + fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { + let item = self.configure_item_kind(item); + fold::noop_fold_item_kind(item, self) + } + + fn fold_expr(&mut self, expr: P) -> P { + let mut expr = self.configure_expr(expr).unwrap(); + expr.node = self.configure_expr_kind(expr.node); + P(fold::noop_fold_expr(expr, self)) + } + + fn fold_opt_expr(&mut self, expr: P) -> Option> { + let mut expr = configure!(self, expr).unwrap(); + expr.node = self.configure_expr_kind(expr.node); + Some(P(fold::noop_fold_expr(expr, self))) + } + + fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { + match self.configure_stmt(stmt) { + Some(stmt) => fold::noop_fold_stmt(stmt, self), + None => return SmallVector::zero(), + } + } + + fn fold_item(&mut self, item: P) -> SmallVector> { + fold::noop_fold_item(configure!(self, item), self) + } + + fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector { + fold::noop_fold_impl_item(configure!(self, item), self) + } + + fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { + fold::noop_fold_trait_item(configure!(self, item), self) + } + + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + // Don't configure interpolated AST (c.f. #34171). + // Interpolated AST will get configured once the surrounding tokens are parsed. + mac + } } fn is_cfg(attr: &ast::Attribute) -> bool { diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index 893646f121f..373dfc4ddfa 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -29,6 +29,12 @@ enum SmallVectorRepr { Many(Vec), } +impl Default for SmallVector { + fn default() -> Self { + SmallVector { repr: Zero } + } +} + impl Into> for SmallVector { fn into(self) -> Vec { match self.repr { From d76bf3ed80b0f6d6fb44704f98df1067353de300 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 7 Sep 2016 22:24:01 +0000 Subject: [PATCH 17/19] Strip unconfigured nodes in the `InvocationCollector` fold. --- src/libsyntax/config.rs | 12 +++--- src/libsyntax/ext/expand.rs | 73 ++++++++++++++++++++++++++++++------- src/libsyntax/lib.rs | 1 + 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 07604bffa1c..3f5b294cc04 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -70,7 +70,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - fn configure(&mut self, node: T) -> Option { + pub fn configure(&mut self, node: T) -> Option { let node = self.process_cfg_attrs(node); if self.in_cfg(node.attrs()) { Some(node) } else { None } } @@ -166,7 +166,7 @@ fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) { } } - fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { + pub fn configure_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { ast::ForeignMod { abi: foreign_mod.abi, items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(), @@ -187,7 +187,7 @@ fn configure_variant_data(&mut self, vdata: ast::VariantData) -> ast::VariantDat } } - fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { + pub fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { match item { ast::ItemKind::Struct(def, generics) => { ast::ItemKind::Struct(self.configure_variant_data(def), generics) @@ -217,7 +217,7 @@ fn configure_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { } } - fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { + pub fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { if let ast::ExprKind::Match(m, arms) = expr_kind { let arms = arms.into_iter().filter_map(|a| self.configure(a)).collect(); ast::ExprKind::Match(m, arms) @@ -226,7 +226,7 @@ fn configure_expr_kind(&mut self, expr_kind: ast::ExprKind) -> ast::ExprKind { } } - fn configure_expr(&mut self, expr: P) -> P { + pub fn configure_expr(&mut self, expr: P) -> P { self.visit_stmt_or_expr_attrs(expr.attrs()); // If an expr is valid to cfg away it will have been removed by the @@ -244,7 +244,7 @@ fn configure_expr(&mut self, expr: P) -> P { self.process_cfg_attrs(expr) } - fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option { + pub fn configure_stmt(&mut self, stmt: ast::Stmt) -> Option { self.visit_stmt_or_expr_attrs(stmt.attrs()); self.configure(stmt) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0986d32ff56..68aca3ee32f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -208,14 +208,23 @@ fn expand(&mut self, expansion: Expansion) -> Expansion { } fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec) { - let expansion = expansion.fold_with(&mut StripUnconfigured { - config: &self.cx.cfg, - should_test: self.cx.ecfg.should_test, - sess: self.cx.parse_sess, - features: self.cx.ecfg.features, - }); - let mut collector = InvocationCollector { cx: self.cx, invocations: Vec::new() }; - (expansion.fold_with(&mut collector), collector.invocations) + let crate_config = mem::replace(&mut self.cx.cfg, Vec::new()); + let result = { + let mut collector = InvocationCollector { + cfg: StripUnconfigured { + config: &crate_config, + should_test: self.cx.ecfg.should_test, + sess: self.cx.parse_sess, + features: self.cx.ecfg.features, + }, + cx: self.cx, + invocations: Vec::new(), + }; + (expansion.fold_with(&mut collector), collector.invocations) + }; + + self.cx.cfg = crate_config; + result } fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { @@ -403,9 +412,19 @@ fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { struct InvocationCollector<'a, 'b: 'a> { cx: &'a mut ExtCtxt<'b>, + cfg: StripUnconfigured<'a>, invocations: Vec, } +macro_rules! fully_configure { + ($this:ident, $node:ident, $noop_fold:ident) => { + match $noop_fold($node, &mut $this.cfg).pop() { + Some(node) => node, + None => return SmallVector::zero(), + } + } +} + impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion { let mark = Mark::fresh(); @@ -475,11 +494,17 @@ fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { } false } + + fn configure(&mut self, node: T) -> Option { + self.cfg.configure(node) + } } impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_expr(&mut self, expr: P) -> P { - let expr = expr.unwrap(); + let mut expr = self.cfg.configure_expr(expr).unwrap(); + expr.node = self.cfg.configure_expr_kind(expr.node); + if let ast::ExprKind::Mac(mac) = expr.node { self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::Expr).make_expr() } else { @@ -488,7 +513,9 @@ fn fold_expr(&mut self, expr: P) -> P { } fn fold_opt_expr(&mut self, expr: P) -> Option> { - let expr = expr.unwrap(); + let mut expr = configure!(self, expr).unwrap(); + expr.node = self.cfg.configure_expr_kind(expr.node); + if let ast::ExprKind::Mac(mac) = expr.node { self.collect_bang(mac, expr.attrs.into(), expr.span, ExpansionKind::OptExpr) .make_opt_expr() @@ -511,6 +538,11 @@ fn fold_pat(&mut self, pat: P) -> P { } fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { + let stmt = match self.cfg.configure_stmt(stmt) { + Some(stmt) => stmt, + None => return SmallVector::zero(), + }; + let (mac, style, attrs) = match stmt.node { StmtKind::Mac(mac) => mac.unwrap(), _ => return noop_fold_stmt(stmt, self), @@ -540,9 +572,11 @@ fn fold_block(&mut self, block: P) -> P { } fn fold_item(&mut self, item: P) -> SmallVector> { + let item = configure!(self, item); + let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = Annotatable::Item(item); + let item = Annotatable::Item(fully_configure!(self, item, noop_fold_item)); return self.collect_attr(attr, item, ExpansionKind::Items).make_items(); } @@ -610,9 +644,12 @@ fn fold_item(&mut self, item: P) -> SmallVector> { } fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector { + let item = configure!(self, item); + let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = Annotatable::TraitItem(P(item)); + let item = + Annotatable::TraitItem(P(fully_configure!(self, item, noop_fold_trait_item))); return self.collect_attr(attr, item, ExpansionKind::TraitItems).make_trait_items() } @@ -626,9 +663,11 @@ fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector SmallVector { + let item = configure!(self, item); + let (item, attr) = self.classify_item(item); if let Some(attr) = attr { - let item = Annotatable::ImplItem(P(item)); + let item = Annotatable::ImplItem(P(fully_configure!(self, item, noop_fold_impl_item))); return self.collect_attr(attr, item, ExpansionKind::ImplItems).make_impl_items(); } @@ -653,6 +692,14 @@ fn fold_ty(&mut self, ty: P) -> P { _ => unreachable!(), } } + + fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { + noop_fold_foreign_mod(self.cfg.configure_foreign_mod(foreign_mod), self) + } + + fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { + noop_fold_item_kind(self.cfg.configure_item_kind(item), self) + } } pub struct ExpansionConfig<'feat> { diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 42201231247..4a2c9aff2d2 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -104,6 +104,7 @@ pub mod syntax { pub mod ast; pub mod attr; pub mod codemap; +#[macro_use] pub mod config; pub mod entry; pub mod feature_gate; From 2d759046ba685f419b3de79367e5b973c1b84105 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 03:35:59 +0000 Subject: [PATCH 18/19] Implement stackless placeholder expansion. --- src/libsyntax/ext/expand.rs | 19 +++++++++++++++---- src/libsyntax/ext/placeholders.rs | 11 +++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 68aca3ee32f..c5b637a9800 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -26,7 +26,6 @@ use tokenstream::TokenTree; use util::small_vector::SmallVector; -use std::collections::HashMap; use std::mem; use std::path::PathBuf; use std::rc::Rc; @@ -182,10 +181,11 @@ fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { // Fully expand all the invocations in `expansion`. fn expand(&mut self, expansion: Expansion) -> Expansion { + self.cx.recursion_count = 0; let (expansion, mut invocations) = self.collect_invocations(expansion); invocations.reverse(); - let mut expansions = HashMap::new(); + 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; @@ -198,13 +198,24 @@ fn expand(&mut self, expansion: Expansion) -> Expansion { self.cx.recursion_count = depth + 1; let (expansion, new_invocations) = self.collect_invocations(expansion); - expansions.insert(mark.as_u32(), expansion); + if expansions.len() == depth { + expansions.push(Vec::new()); + } + expansions[depth].push((mark.as_u32(), expansion)); if !self.single_step { invocations.extend(new_invocations.into_iter().rev()); } } - expansion.fold_with(&mut PlaceholderExpander::new(expansions)) + let mut placeholder_expander = PlaceholderExpander::new(); + while let Some(expansions) = expansions.pop() { + for (mark, expansion) in expansions.into_iter().rev() { + let expansion = expansion.fold_with(&mut placeholder_expander); + placeholder_expander.add(mark, expansion); + } + } + + placeholder_expander.remove(0) } fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec) { diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index f1665bdde75..abadcf867b1 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -74,15 +74,18 @@ pub struct PlaceholderExpander { } impl PlaceholderExpander { - pub fn new(expansions: HashMap) -> Self { + pub fn new() -> Self { PlaceholderExpander { - expansions: expansions, + expansions: HashMap::new(), } } + pub fn add(&mut self, id: ast::NodeId, expansion: Expansion) { + self.expansions.insert(id, expansion); + } + pub fn remove(&mut self, id: ast::NodeId) -> Expansion { - let expansion = self.expansions.remove(&id).unwrap(); - expansion.fold_with(self) + self.expansions.remove(&id).unwrap() } } From 9ac91fa48b3eb479cccb5695395faed8f59ece8e Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 5 Sep 2016 04:08:38 +0000 Subject: [PATCH 19/19] Improve `directory` computation during invocation collection. --- src/libsyntax/ext/expand.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c5b637a9800..4715eda8374 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -615,16 +615,20 @@ fn fold_item(&mut self, item: P) -> SmallVector> { ast::ItemKind::Mod(ast::Mod { inner, .. }) => { let mut paths = (*self.cx.syntax_env.paths()).clone(); paths.mod_path.push(item.ident); - if item.span.contains(inner) { + + // 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`). + // Thus, if `inner` is the dummy span, we know the module is inline. + let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP; + + if inline_module { paths.directory.push(&*{ ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); } else { - paths.directory = match inner { - syntax_pos::DUMMY_SP => PathBuf::new(), - _ => PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)), - }; + paths.directory = + PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); paths.directory.pop(); }