From 7f30eef2eee5d57bc9759766ec7f62d931f8993b Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 19 May 2016 09:45:37 +0000 Subject: [PATCH 01/11] 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); From 2e7afd73676f95ad3b944788c4798ad538a5edd2 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 19 May 2016 10:30:34 +0000 Subject: [PATCH 02/11] Improve diagnostics --- src/libsyntax/ext/expand.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b2ebe0e0145..6c22fe4c8bc 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -272,11 +272,9 @@ fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut Macr let parsed = match opt_parsed { Some(e) => e, None => { - fld.cx.span_err( - pth.span, - &format!("non-expression macro in expression position: {}", - extname - )); + let msg = format!("non-{kind} macro in {kind} position: {name}", + name = extname, kind = T::kind_name()); + fld.cx.span_err(pth.span, &msg); return None; } }; From 8ee93b73d16ba7909a33f5ae4f729af3ba1a3de8 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 20 May 2016 23:35:56 +0000 Subject: [PATCH 03/11] Re-fold expanded items in `expand_mac_invoc` --- src/libsyntax/ext/expand.rs | 76 +++++++------------------------------ 1 file changed, 13 insertions(+), 63 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6c22fe4c8bc..8833b901ec7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -93,18 +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, fld) { - Some(expr) => expr, - None => { - return DummyResult::raw_expr(span); - } - }; - - // Keep going, outside-in. - let fully_expanded = fld.fold_expr(expanded_expr); - fld.cx.bt_pop(); - - fully_expanded + expand_mac_invoc(mac, span, fld) } ast::ExprKind::InPlace(placer, value_expr) => { @@ -215,8 +204,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { } /// 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 { +fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut MacroExpander) -> T { // 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 @@ -229,7 +217,7 @@ fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut Macr "expected macro name without module \ separators"); // let compilation continue - return None; + return T::dummy(span); } let extname = pth.segments[0].identifier.name; match fld.cx.syntax_env.find(extname) { @@ -242,7 +230,7 @@ fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut Macr err.emit(); // let compilation continue - None + T::dummy(span) } Some(rc) => match *rc { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { @@ -275,17 +263,20 @@ fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut Macr let msg = format!("non-{kind} macro in {kind} position: {name}", name = extname, kind = T::kind_name()); fld.cx.span_err(pth.span, &msg); - return None; + return T::dummy(span); } }; - Some(parsed.fold_with(&mut Marker { mark: fm })) + let marked = parsed.fold_with(&mut Marker { mark: fm }); + let fully_expanded = marked.fold_with(fld); + fld.cx.bt_pop(); + fully_expanded } _ => { fld.cx.span_err( pth.span, &format!("'{}' is not a tt-style macro", extname)); - None + T::dummy(span) } } } @@ -543,21 +534,9 @@ 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: Option> = + let mut fully_expanded: SmallVector = expand_mac_invoc(mac.unwrap(), stmt.span, fld); - let mut fully_expanded = match maybe_new_items { - Some(stmts) => { - // Keep going, outside-in. - let new_items = stmts.into_iter().flat_map(|s| { - fld.fold_stmt(s).into_iter() - }).collect(); - fld.cx.bt_pop(); - new_items - } - None => SmallVector::zero() - }; - // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. if style == MacStmtStyle::Semicolon { @@ -1096,21 +1075,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) }), ast::ImplItemKind::Macro(mac) => { check_attributes(&ii.attrs, fld); - - let maybe_new_items: Option> = - expand_mac_invoc(mac, ii.span, fld); - - match maybe_new_items { - Some(impl_items) => { - // expand again if necessary - let new_items = impl_items.into_iter().flat_map(|ii| { - expand_impl_item(ii, fld).into_iter() - }).collect(); - fld.cx.bt_pop(); - new_items - } - None => SmallVector::zero() - } + expand_mac_invoc(mac, ii.span, fld) } _ => fold::noop_fold_impl_item(ii, fld) } @@ -1154,22 +1119,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, fld) { - Some(ty) => ty, - None => { - return DummyResult::raw_ty(t.span); - } - }; - - // Keep going, outside-in. - let fully_expanded = fld.fold_ty(expanded_ty); - fld.cx.bt_pop(); - - fully_expanded.map(|t| ast::Ty { - id: ast::DUMMY_NODE_ID, - node: t.node, - span: t.span, - }) + expand_mac_invoc(mac, t.span, fld) } else { feature_gate::emit_feature_err( &fld.cx.parse_sess.span_diagnostic, From f4075e9467ef704d7f8cd120669faec455eb84f9 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 19 May 2016 10:18:54 +0000 Subject: [PATCH 04/11] Use `expand_mac_invoc` in `expand_pat` --- src/libsyntax/ext/expand.rs | 78 ++----------------------------------- 1 file changed, 3 insertions(+), 75 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8833b901ec7..8a5689daca5 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -750,77 +750,10 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { PatKind::Mac(_) => {} _ => return noop_fold_pat(p, fld) } - p.map(|ast::Pat {node, span, ..}| { - let (pth, tts) = match node { - PatKind::Mac(mac) => (mac.node.path, mac.node.tts), + p.and_then(|ast::Pat {node, span, ..}| { + match node { + PatKind::Mac(mac) => expand_mac_invoc(mac, span, fld), _ => unreachable!() - }; - - if pth.segments.len() > 1 { - fld.cx.span_err(pth.span, "expected macro name without module separators"); - return DummyResult::raw_pat(span); - } - let extname = pth.segments[0].identifier.name; - let marked_after = match fld.cx.syntax_env.find(extname) { - None => { - fld.cx.span_err(pth.span, - &format!("macro undefined: '{}!'", - extname)); - // let compilation continue - return DummyResult::raw_pat(span); - } - - Some(rc) => match *rc { - NormalTT(ref expander, tt_span, allow_internal_unstable) => { - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: tt_span, - allow_internal_unstable: allow_internal_unstable, - } - }); - - let fm = fresh_mark(); - let marked_before = mark_tts(&tts[..], fm); - let mac_span = fld.cx.original_span(); - let pat = expander.expand(fld.cx, - mac_span, - &marked_before[..]).make_pat(); - let expanded = match pat { - Some(e) => e, - None => { - fld.cx.span_err( - pth.span, - &format!( - "non-pattern macro in pattern position: {}", - extname - ) - ); - return DummyResult::raw_pat(span); - } - }; - - // mark after: - mark_pat(expanded,fm) - } - _ => { - fld.cx.span_err(span, - &format!("{}! is not legal in pattern position", - extname)); - return DummyResult::raw_pat(span); - } - } - }; - - let fully_expanded = - fld.fold_pat(marked_after).node.clone(); - fld.cx.bt_pop(); - - ast::Pat { - id: ast::DUMMY_NODE_ID, - node: fully_expanded, - span: span } }) } @@ -1388,11 +1321,6 @@ fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m}) } -// 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 item. Used following the expansion of a macro. fn mark_item(expr: P, m: Mrk) -> P { Marker{mark:m}.fold_item(expr) From 215c1460f68a5c47f40ea173484cd791166ebc94 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 20 May 2016 23:55:39 +0000 Subject: [PATCH 05/11] Check attributes in `expand_mac_invoc` --- src/libsyntax/ext/expand.rs | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8a5689daca5..aa5d93d8269 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -16,7 +16,7 @@ use ast; use ext::mtwt; use ext::build::AstBuilder; use attr; -use attr::{AttrMetaMethods, WithAttrs}; +use attr::{AttrMetaMethods, WithAttrs, ThinAttributesExt}; use codemap; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use ext::base::*; @@ -86,14 +86,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. ast::ExprKind::Mac(mac) => { - if let Some(ref attrs) = attrs { - check_attributes(attrs, fld); - } - - // Assert that we drop any macro attributes on the floor here - drop(attrs); - - expand_mac_invoc(mac, span, fld) + expand_mac_invoc(mac, attrs.into_attr_vec(), span, fld) } ast::ExprKind::InPlace(placer, value_expr) => { @@ -204,7 +197,12 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { } /// Expand a (not-ident-style) macro invocation. Returns the result of expansion. -fn expand_mac_invoc(mac: ast::Mac, span: Span, fld: &mut MacroExpander) -> T { +fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, + fld: &mut MacroExpander) -> T + where T: MacroGenerable, +{ + check_attributes(&attrs, fld); + // 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 @@ -527,15 +525,8 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { _ => return expand_non_macro_stmt(stmt, fld) }; - if let Some(ref attrs) = attrs { - check_attributes(attrs, fld); - } - - // Assert that we drop any macro attributes on the floor here - drop(attrs); - let mut fully_expanded: SmallVector = - expand_mac_invoc(mac.unwrap(), stmt.span, fld); + expand_mac_invoc(mac.unwrap(), attrs.into_attr_vec(), stmt.span, fld); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -752,7 +743,7 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { } p.and_then(|ast::Pat {node, span, ..}| { match node { - PatKind::Mac(mac) => expand_mac_invoc(mac, span, fld), + PatKind::Mac(mac) => expand_mac_invoc(mac, Vec::new(), span, fld), _ => unreachable!() } }) @@ -1007,8 +998,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) span: fld.new_span(ii.span) }), ast::ImplItemKind::Macro(mac) => { - check_attributes(&ii.attrs, fld); - expand_mac_invoc(mac, ii.span, fld) + expand_mac_invoc(mac, ii.attrs, ii.span, fld) } _ => fold::noop_fold_impl_item(ii, fld) } @@ -1052,7 +1042,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 { - expand_mac_invoc(mac, t.span, fld) + expand_mac_invoc(mac, Vec::new(), t.span, fld) } else { feature_gate::emit_feature_err( &fld.cx.parse_sess.span_diagnostic, From 3192adbf87138b2a7276719f32764db79cb2a883 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 21 May 2016 00:15:00 +0000 Subject: [PATCH 06/11] Refactor out `mac_result` in `expand_mac_invoc` --- src/libsyntax/ext/expand.rs | 107 ++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index aa5d93d8269..952d398e5bd 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -201,47 +201,46 @@ fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, fld: &mut MacroExpander) -> T where T: MacroGenerable, { - check_attributes(&attrs, fld); - // 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 // for the other three macro invocation chunks of code // in this file. - let Mac_ { path: pth, tts, .. } = mac.node; - if pth.segments.len() > 1 { - fld.cx.span_err(pth.span, - "expected macro name without module \ - separators"); - // let compilation continue - return T::dummy(span); - } - let extname = pth.segments[0].identifier.name; - match fld.cx.syntax_env.find(extname) { - None => { - let mut err = fld.cx.struct_span_err( - pth.span, - &format!("macro undefined: '{}!'", - &extname)); + let Mac_ { path, tts, .. } = mac.node; + let mark = fresh_mark(); + + fn mac_result<'a>(path: &ast::Path, tts: Vec, mark: Mrk, + attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) + -> Option> { + check_attributes(&attrs, fld); + + if path.segments.len() > 1 { + 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 compilation continue - T::dummy(span) - } - Some(rc) => match *rc { + match *extension { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: exp_span, - allow_internal_unstable: allow_internal_unstable, - }, - }); - let fm = fresh_mark(); - let marked_before = mark_tts(&tts[..], fm); + call_site: call_site, + callee: NameAndSpan { + format: MacroBang(extname), + span: exp_span, + allow_internal_unstable: allow_internal_unstable, + }, + }); // The span that we pass to the expanders we want to // be the root of the call stack. That's the most @@ -249,35 +248,35 @@ fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, // the macro. let mac_span = fld.cx.original_span(); - let opt_parsed = { - let expanded = expandfun.expand(fld.cx, - mac_span, - &marked_before[..]); - T::make_with(expanded) - }; - let parsed = match opt_parsed { - Some(e) => e, - None => { - let msg = format!("non-{kind} macro in {kind} position: {name}", - name = extname, kind = T::kind_name()); - fld.cx.span_err(pth.span, &msg); - return T::dummy(span); - } - }; - let marked = parsed.fold_with(&mut Marker { mark: fm }); - let fully_expanded = marked.fold_with(fld); - fld.cx.bt_pop(); - fully_expanded + let marked_tts = mark_tts(&tts[..], mark); + Some(expandfun.expand(fld.cx, mac_span, &marked_tts)) } _ => { - fld.cx.span_err( - pth.span, - &format!("'{}' is not a tt-style macro", - extname)); - T::dummy(span) + fld.cx.span_err(path.span, + &format!("'{}' is not a tt-style macro", extname)); + None } } } + + let opt_expanded = T::make_with(match mac_result(&path, tts, mark, attrs, span, fld) { + Some(result) => result, + None => return T::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()); + fld.cx.span_err(path.span, &msg); + return T::dummy(span); + }; + + let marked = expanded.fold_with(&mut Marker { mark: mark }); + let fully_expanded = marked.fold_with(fld); + fld.cx.bt_pop(); + fully_expanded } /// Rename loop label and expand its loop body From a3d705ef3094968c457cf0408cc6bbe241e627b5 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 21 May 2016 00:19:00 +0000 Subject: [PATCH 07/11] Refactor away `expand_item_mac` --- src/libsyntax/ext/expand.rs | 244 +++++++++++++----------------------- 1 file changed, 85 insertions(+), 159 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 952d398e5bd..c8272316c3c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -86,7 +86,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. ast::ExprKind::Mac(mac) => { - expand_mac_invoc(mac, attrs.into_attr_vec(), span, fld) + expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, fld) } ast::ExprKind::InPlace(placer, value_expr) => { @@ -196,21 +196,17 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }); } -/// Expand a (not-ident-style) macro invocation. Returns the result of expansion. -fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, +/// 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. This also goes - // for the other three macro invocation chunks of code - // in this file. - + // 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. let Mac_ { path, tts, .. } = mac.node; let mark = fresh_mark(); - fn mac_result<'a>(path: &ast::Path, tts: Vec, mark: Mrk, + fn mac_result<'a>(path: &ast::Path, ident: Option, tts: Vec, mark: Mrk, attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) -> Option> { check_attributes(&attrs, fld); @@ -231,8 +227,16 @@ fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, return None; }; + let ident = ident.unwrap_or(Ident::with_empty_ctxt(keywords::Invalid.name())); 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 { @@ -251,15 +255,72 @@ fn expand_mac_invoc(mac: ast::Mac, attrs: Vec, span: Span, let marked_tts = mark_tts(&tts[..], mark); Some(expandfun.expand(fld.cx, mac_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 None; + }; + + fld.cx.bt_push(ExpnInfo { + call_site: call_site, + callee: NameAndSpan { + format: MacroBang(extname), + span: tt_span, + allow_internal_unstable: allow_internal_unstable, + } + }); + + let marked_tts = mark_tts(&tts, mark); + 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, + } + }); + + // DON'T mark before expansion. + fld.cx.insert_macro(ast::MacroDef { + ident: ident, + id: ast::DUMMY_NODE_ID, + span: call_site, + imported_from: None, + use_locally: true, + body: tts, + export: attr::contains_name(&attrs, "macro_export"), + allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"), + attrs: attrs, + }); + + // macro_rules! has a side effect but expands to nothing. + fld.cx.bt_pop(); + None + } + + MultiDecorator(..) | MultiModifier(..) => { fld.cx.span_err(path.span, - &format!("'{}' is not a tt-style macro", extname)); + &format!("`{}` can only be used in attributes", extname)); None } } } - let opt_expanded = T::make_with(match mac_result(&path, tts, mark, attrs, span, fld) { + let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) { Some(result) => result, None => return T::dummy(span), }); @@ -375,141 +436,6 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool false } -// Support for item-position macro invocations, exactly the same -// logic as for expression-position macro invocations. -pub fn expand_item_mac(it: P, - fld: &mut MacroExpander) -> SmallVector> { - let (extname, path_span, tts, span, attrs, ident) = it.and_then(|it| match it.node { - ItemKind::Mac(codemap::Spanned { node: Mac_ { path, tts, .. }, .. }) => - (path.segments[0].identifier.name, path.span, tts, it.span, it.attrs, it.ident), - _ => fld.cx.span_bug(it.span, "invalid item macro invocation") - }); - - check_attributes(&attrs, fld); - - let fm = fresh_mark(); - let items = { - let expanded = match fld.cx.syntax_env.find(extname) { - None => { - fld.cx.span_err(path_span, - &format!("macro undefined: '{}!'", - extname)); - // let compilation continue - return SmallVector::zero(); - } - - Some(rc) => match *rc { - NormalTT(ref expander, tt_span, allow_internal_unstable) => { - if ident.name != keywords::Invalid.name() { - fld.cx - .span_err(path_span, - &format!("macro {}! expects no ident argument, given '{}'", - extname, - ident)); - return SmallVector::zero(); - } - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: tt_span, - allow_internal_unstable: allow_internal_unstable, - } - }); - // mark before expansion: - let marked_before = mark_tts(&tts[..], fm); - expander.expand(fld.cx, span, &marked_before[..]) - } - 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 SmallVector::zero(); - } - fld.cx.bt_push(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(extname), - span: tt_span, - allow_internal_unstable: allow_internal_unstable, - } - }); - // mark before expansion: - let marked_tts = mark_tts(&tts[..], fm); - expander.expand(fld.cx, span, ident, marked_tts) - } - MacroRulesTT => { - if ident.name == keywords::Invalid.name() { - fld.cx.span_err(path_span, "macro_rules! expects an ident argument"); - return SmallVector::zero(); - } - - 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, - } - }); - // DON'T mark before expansion. - - let allow_internal_unstable = attr::contains_name(&attrs, - "allow_internal_unstable"); - - let export = attr::contains_name(&attrs, "macro_export"); - let def = ast::MacroDef { - ident: ident, - attrs: attrs, - id: ast::DUMMY_NODE_ID, - span: span, - imported_from: None, - export: export, - use_locally: true, - allow_internal_unstable: allow_internal_unstable, - body: tts, - }; - fld.cx.insert_macro(def); - - // macro_rules! has a side effect but expands to nothing. - fld.cx.bt_pop(); - return SmallVector::zero(); - } - _ => { - fld.cx.span_err(span, - &format!("{}! is not legal in item position", - extname)); - return SmallVector::zero(); - } - } - }; - - expanded.make_items() - }; - - let items = match items { - Some(items) => { - items.into_iter() - .map(|i| mark_item(i, fm)) - .flat_map(|i| fld.fold_item(i).into_iter()) - .collect() - } - None => { - fld.cx.span_err(path_span, - &format!("non-item macro in item position: {}", - extname)); - return SmallVector::zero(); - } - }; - - fld.cx.bt_pop(); - items -} - /// Expand a stmt fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { // perform all pending renames @@ -525,7 +451,7 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { }; let mut fully_expanded: SmallVector = - expand_mac_invoc(mac.unwrap(), attrs.into_attr_vec(), stmt.span, fld); + expand_mac_invoc(mac.unwrap(), None, attrs.into_attr_vec(), stmt.span, fld); // If this is a macro invocation with a semicolon, then apply that // semicolon to the final statement produced by expansion. @@ -742,7 +668,7 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { } p.and_then(|ast::Pat {node, span, ..}| { match node { - PatKind::Mac(mac) => expand_mac_invoc(mac, Vec::new(), span, fld), + PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld), _ => unreachable!() } }) @@ -813,7 +739,13 @@ fn expand_annotatable(a: Annotatable, let mut new_items: SmallVector = match a { Annotatable::Item(it) => match it.node { ast::ItemKind::Mac(..) => { - expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect() + let new_items: SmallVector> = it.and_then(|it| match it.node { + ItemKind::Mac(mac) => + expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld), + _ => unreachable!(), + }); + + new_items.into_iter().map(|i| Annotatable::Item(i)).collect() } ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => { let valid_ident = @@ -997,7 +929,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) span: fld.new_span(ii.span) }), ast::ImplItemKind::Macro(mac) => { - expand_mac_invoc(mac, ii.attrs, ii.span, fld) + expand_mac_invoc(mac, None, ii.attrs, ii.span, fld) } _ => fold::noop_fold_impl_item(ii, fld) } @@ -1041,7 +973,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 { - expand_mac_invoc(mac, Vec::new(), t.span, fld) + expand_mac_invoc(mac, None, Vec::new(), t.span, fld) } else { feature_gate::emit_feature_err( &fld.cx.parse_sess.span_diagnostic, @@ -1310,12 +1242,6 @@ fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m}) } -// 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") -} - /// 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); From 82b49cd2000859c8208cf15a78441a72a3edc499 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 21 May 2016 01:26:30 +0000 Subject: [PATCH 08/11] Refactor away `check_attributes` --- src/libsyntax/ext/expand.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c8272316c3c..9bfeb81b74a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -69,16 +69,6 @@ impl_macro_generable! { "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) { - 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()); - } -} - pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let expr_span = e.span; return e.and_then(|ast::Expr {id, node, span, attrs}| match node { @@ -209,7 +199,13 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec(path: &ast::Path, ident: Option, tts: Vec, mark: Mrk, attrs: Vec, call_site: Span, fld: &'a mut MacroExpander) -> Option> { - check_attributes(&attrs, fld); + // 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 { fld.cx.span_err(path.span, "expected macro name without module separators"); From ba8b9324d6c10ce151de50057472f5c258476655 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 21 May 2016 08:39:33 +0000 Subject: [PATCH 09/11] Move `placement_in_syntax` gated feature checking from expansion to the post-expansion visitor --- src/libsyntax/ext/expand.rs | 16 ---------------- src/libsyntax/feature_gate.rs | 3 +++ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9bfeb81b74a..df1bbf5f26e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -70,7 +70,6 @@ impl_macro_generable! { } pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { - let expr_span = e.span; return e.and_then(|ast::Expr {id, node, span, attrs}| match node { // expr_mac should really be expr_ext or something; it's the @@ -79,21 +78,6 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, fld) } - ast::ExprKind::InPlace(placer, value_expr) => { - // Ensure feature-gate is enabled - if !fld.cx.ecfg.features.unwrap().placement_in_syntax { - feature_gate::emit_feature_err( - &fld.cx.parse_sess.span_diagnostic, "placement_in_syntax", expr_span, - feature_gate::GateIssue::Language, feature_gate::EXPLAIN_PLACEMENT_IN - ); - } - - let placer = fld.fold_expr(placer); - let value_expr = fld.fold_expr(value_expr); - fld.cx.expr(span, ast::ExprKind::InPlace(placer, value_expr)) - .with_attrs(fold_thin_attrs(attrs, fld)) - } - ast::ExprKind::While(cond, body, opt_ident) => { let cond = fld.fold_expr(cond); let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index acef98f2afc..dbef06f7aa4 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -997,6 +997,9 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { ast::ExprKind::Try(..) => { gate_feature_post!(&self, question_mark, e.span, "the `?` operator is not stable"); } + ast::ExprKind::InPlace(..) => { + gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN); + } _ => {} } visit::walk_expr(self, e); From 3c421ac67ef55541de73689f2f6bbabd7ddb0a9a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 21 May 2016 01:58:42 +0000 Subject: [PATCH 10/11] Test diagnostics --- src/test/compile-fail/macro-error.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/macro-error.rs b/src/test/compile-fail/macro-error.rs index 817b675aedf..a69188da58d 100644 --- a/src/test/compile-fail/macro-error.rs +++ b/src/test/compile-fail/macro-error.rs @@ -8,12 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that we report errors at macro definition, not expansion. +#![feature(type_macros)] macro_rules! foo { ($a:expr) => $a; //~ ERROR macro rhs must be delimited } fn main() { - foo!(0); + foo!(0); // Check that we report errors at macro definition, not expansion. + + let _: cfg!(foo) = (); //~ ERROR non-type macro in type position + derive!(); //~ ERROR `derive` can only be used in attributes } From e9c0283369bbb9879e0be2440d77f23d806b5537 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 24 May 2016 06:12:54 +0000 Subject: [PATCH 11/11] Add comments and fix a nit --- src/libsyntax/ext/expand.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index df1bbf5f26e..596faac3588 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -35,10 +35,18 @@ use std_inject; use std::collections::HashSet; use std::env; +// 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; + + // Fold this node or list of nodes using the given folder. fn fold_with(self, folder: &mut F) -> Self; + + // Return a placeholder expansion to allow compilation to continue after an erroring expansion. fn dummy(span: Span) -> Self; + + // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics. fn kind_name() -> &'static str; } @@ -207,7 +215,7 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec { if ident.name != keywords::Invalid.name() {