From 7f30eef2eee5d57bc9759766ec7f62d931f8993b Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 19 May 2016 09:45:37 +0000 Subject: [PATCH] Introduce `MacroGenerable` trait --- src/libsyntax/ext/expand.rs | 99 +++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f243706eecb..b2ebe0e0145 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -35,6 +35,40 @@ use std_inject; use std::collections::HashSet; use std::env; +trait MacroGenerable: Sized { + fn make_with<'a>(result: Box) -> Option; + fn fold_with(self, folder: &mut F) -> Self; + fn dummy(span: Span) -> Self; + fn kind_name() -> &'static str; +} + +macro_rules! impl_macro_generable { + ($($ty:ty: $kind_name:expr, .$make:ident, $(.$fold:ident)* $(lift .$fold_elt:ident)*, + |$span:ident| $dummy:expr;)*) => { $( + 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 dummy($span: Span) -> Self { $dummy } + } + )* } +} + +impl_macro_generable! { + P: "expression", .make_expr, .fold_expr, |span| DummyResult::raw_expr(span); + P: "pattern", .make_pat, .fold_pat, |span| P(DummyResult::raw_pat(span)); + P: "type", .make_ty, .fold_ty, |span| DummyResult::raw_ty(span); + SmallVector: + "impl item", .make_impl_items, lift .fold_impl_item, |_span| SmallVector::zero(); + SmallVector>: + "item", .make_items, lift .fold_item, |_span| SmallVector::zero(); + SmallVector: + "statement", .make_stmts, lift .fold_stmt, |_span| SmallVector::zero(); +} + // this function is called to detect use of feature-gated or invalid attributes // on macro invoations since they will not be detected after macro expansion fn check_attributes(attrs: &[ast::Attribute], fld: &MacroExpander) { @@ -59,9 +93,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // Assert that we drop any macro attributes on the floor here drop(attrs); - let expanded_expr = match expand_mac_invoc(mac, span, - |r| r.make_expr(), - mark_expr, fld) { + let expanded_expr = match expand_mac_invoc(mac, span, fld) { Some(expr) => expr, None => { return DummyResult::raw_expr(span); @@ -182,19 +214,9 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }); } -/// Expand a (not-ident-style) macro invocation. Returns the result -/// of expansion and the mark which must be applied to the result. -/// Our current interface doesn't allow us to apply the mark to the -/// result until after calling make_expr, make_items, etc. -fn expand_mac_invoc(mac: ast::Mac, - span: codemap::Span, - parse_thunk: F, - mark_thunk: G, - fld: &mut MacroExpander) - -> Option where - F: for<'a> FnOnce(Box) -> Option, - G: FnOnce(T, Mrk) -> T, -{ +/// Expand a (not-ident-style) macro invocation. Returns the result of expansion. +fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut MacroExpander) + -> Option { // 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. This also goes @@ -245,7 +267,7 @@ fn expand_mac_invoc(mac: ast::Mac, let expanded = expandfun.expand(fld.cx, mac_span, &marked_before[..]); - parse_thunk(expanded) + T::make_with(expanded) }; let parsed = match opt_parsed { Some(e) => e, @@ -258,7 +280,7 @@ fn expand_mac_invoc(mac: ast::Mac, return None; } }; - Some(mark_thunk(parsed,fm)) + Some(parsed.fold_with(&mut Marker { mark: fm })) } _ => { fld.cx.span_err( @@ -523,11 +545,8 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { // Assert that we drop any macro attributes on the floor here drop(attrs); - let maybe_new_items = - expand_mac_invoc(mac.unwrap(), stmt.span, - |r| r.make_stmts(), - |stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)), - fld); + let maybe_new_items: Option> = + expand_mac_invoc(mac.unwrap(), stmt.span, fld); let mut fully_expanded = match maybe_new_items { Some(stmts) => { @@ -759,6 +778,7 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { PatKind::Mac(mac) => (mac.node.path, mac.node.tts), _ => unreachable!() }; + if pth.segments.len() > 1 { fld.cx.span_err(pth.span, "expected macro name without module separators"); return DummyResult::raw_pat(span); @@ -1079,11 +1099,8 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) ast::ImplItemKind::Macro(mac) => { check_attributes(&ii.attrs, fld); - let maybe_new_items = - expand_mac_invoc(mac, ii.span, - |r| r.make_impl_items(), - |meths, mark| meths.move_map(|m| mark_impl_item(m, mark)), - fld); + let maybe_new_items: Option> = + expand_mac_invoc(mac, ii.span, fld); match maybe_new_items { Some(impl_items) => { @@ -1139,10 +1156,7 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { let t = match t.node.clone() { ast::TyKind::Mac(mac) => { if fld.cx.ecfg.features.unwrap().type_macros { - let expanded_ty = match expand_mac_invoc(mac, t.span, - |r| r.make_ty(), - mark_ty, - fld) { + let expanded_ty = match expand_mac_invoc(mac, t.span, fld) { Some(ty) => ty, None => { return DummyResult::raw_ty(t.span); @@ -1426,38 +1440,17 @@ fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m}) } -// apply a given mark to the given expr. Used following the expansion of a macro. -fn mark_expr(expr: P, m: Mrk) -> P { - Marker{mark:m}.fold_expr(expr) -} - // apply a given mark to the given pattern. Used following the expansion of a macro. fn mark_pat(pat: P, m: Mrk) -> P { Marker{mark:m}.fold_pat(pat) } -// apply a given mark to the given stmt. Used following the expansion of a macro. -fn mark_stmt(stmt: ast::Stmt, m: Mrk) -> ast::Stmt { - Marker{mark:m}.fold_stmt(stmt) - .expect_one("marking a stmt didn't return exactly one stmt") -} - // apply a given mark to the given item. Used following the expansion of a macro. fn mark_item(expr: P, m: Mrk) -> P { Marker{mark:m}.fold_item(expr) .expect_one("marking an item didn't return exactly one item") } -// apply a given mark to the given item. Used following the expansion of a macro. -fn mark_impl_item(ii: ast::ImplItem, m: Mrk) -> ast::ImplItem { - Marker{mark:m}.fold_impl_item(ii) - .expect_one("marking an impl item didn't return exactly one impl item") -} - -fn mark_ty(ty: P, m: Mrk) -> P { - Marker { mark: m }.fold_ty(ty) -} - /// Check that there are no macro invocations left in the AST: pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) { visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);