From b0b4b3122a4af7bf9b361c8f646da4a120e7ba38 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 11 Jul 2014 21:22:11 -0700 Subject: [PATCH 1/9] refactor Method definition to make space for macros This change propagates to many locations, but because of the Macro Exterminator (or, more properly, the invariant that it protects), macro invocations can't occur downstream of expansion. This means that in librustc and librustdoc, extracting the desired field can simply assume that it can't be a macro invocation. Functions in ast_util abstract over this check. --- src/librustc/metadata/encoder.rs | 4 +- src/librustc/middle/astencode.rs | 2 +- src/librustc/middle/dead.rs | 6 +- src/librustc/middle/effect.rs | 3 +- src/librustc/middle/privacy.rs | 19 ++--- src/librustc/middle/reachable.rs | 8 +- src/librustc/middle/resolve.rs | 17 +++-- src/librustc/middle/save/mod.rs | 13 ++-- src/librustc/middle/trans/debuginfo.rs | 16 ++-- src/librustc/middle/trans/inline.rs | 5 +- src/librustc/middle/trans/meth.rs | 9 ++- src/librustc/middle/trans/monomorphize.rs | 7 +- src/librustc/middle/typeck/check/mod.rs | 10 ++- src/librustc/middle/typeck/collect.rs | 29 ++++--- .../middle/typeck/infer/error_reporting.rs | 13 ++-- src/librustdoc/clean/mod.rs | 21 +++--- src/libsyntax/ast.rs | 19 +++-- src/libsyntax/ast_map.rs | 32 +++++--- src/libsyntax/ast_util.rs | 75 ++++++++++++++----- src/libsyntax/ext/deriving/generic/mod.rs | 14 ++-- src/libsyntax/ext/expand.rs | 28 ++++--- src/libsyntax/fold.rs | 21 ++++-- src/libsyntax/parse/parser.rs | 16 +--- src/libsyntax/print/pprust.rs | 31 ++++++-- src/libsyntax/visit.rs | 32 +++++--- 25 files changed, 277 insertions(+), 173 deletions(-) diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 493fd575a8408..6cb0ab51ca10d 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -799,7 +799,7 @@ fn encode_info_for_method(ecx: &EncodeContext, } else { encode_symbol(ecx, ebml_w, m.def_id.node); } - encode_method_argument_names(ebml_w, &*ast_method.decl); + encode_method_argument_names(ebml_w, method_fn_decl(&*ast_method)); } ebml_w.end_tag(); @@ -1241,7 +1241,7 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_method_sort(ebml_w, 'p'); encode_inlined_item(ecx, ebml_w, IIMethodRef(def_id, true, &*m)); - encode_method_argument_names(ebml_w, &*m.decl); + encode_method_argument_names(ebml_w, method_fn_decl(m)); } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index fb2b4951ea3d6..8fa7d7c2aaa77 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -136,7 +136,7 @@ pub fn decode_inlined_item(cdata: &cstore::crate_metadata, let ident = match ii { ast::IIItem(i) => i.ident, ast::IIForeign(i) => i.ident, - ast::IIMethod(_, _, m) => m.ident, + ast::IIMethod(_, _, m) => ast_util::method_ident(&*m), }; debug!("Fn named: {}", token::get_ident(ident)); debug!("< Decoded inlined fn: {}::{}", diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 9ec167ee8263e..d84c62f744edc 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -22,6 +22,7 @@ use util::nodemap::NodeSet; use std::collections::HashSet; use syntax::ast; use syntax::ast_map; +use syntax::ast_util; use syntax::ast_util::{local_def, is_local}; use syntax::attr::AttrMetaMethods; use syntax::attr; @@ -212,7 +213,7 @@ impl<'a> MarkSymbolVisitor<'a> { visit::walk_trait_method(self, &*trait_method, ctxt); } ast_map::NodeMethod(method) => { - visit::walk_block(self, &*method.body, ctxt); + visit::walk_block(self, ast_util::method_body(&*method), ctxt); } ast_map::NodeForeignItem(foreign_item) => { visit::walk_foreign_item(self, &*foreign_item, ctxt); @@ -520,7 +521,8 @@ impl<'a> Visitor<()> for DeadVisitor<'a> { // Overwrite so that we don't warn the trait method itself. fn visit_trait_method(&mut self, trait_method: &ast::TraitMethod, _: ()) { match *trait_method { - ast::Provided(ref method) => visit::walk_block(self, &*method.body, ()), + ast::Provided(ref method) => visit::walk_block(self, + ast_util::method_body(&**method), ()), ast::Required(_) => () } } diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index 782a380e23a0e..415135a2d048f 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -17,6 +17,7 @@ use middle::typeck::MethodCall; use util::ppaux; use syntax::ast; +use syntax::ast_util; use syntax::codemap::Span; use syntax::visit; use syntax::visit::Visitor; @@ -94,7 +95,7 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { visit::FkItemFn(_, _, fn_style, _) => (true, fn_style == ast::UnsafeFn), visit::FkMethod(_, _, method) => - (true, method.fn_style == ast::UnsafeFn), + (true, ast_util::method_fn_style(method) == ast::UnsafeFn), _ => (false, false), }; diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 7630321bd559b..580e7b2db57c2 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -26,6 +26,7 @@ use util::nodemap::{NodeMap, NodeSet}; use syntax::ast; use syntax::ast_map; +use syntax::ast_util; use syntax::ast_util::{is_local, local_def}; use syntax::attr; use syntax::codemap::Span; @@ -263,10 +264,10 @@ impl<'a> Visitor<()> for EmbargoVisitor<'a> { if public_ty || public_trait { for method in methods.iter() { - let meth_public = match method.explicit_self.node { + let meth_public = match ast_util::method_explicit_self(&**method).node { ast::SelfStatic => public_ty, _ => true, - } && method.vis == ast::Public; + } && ast_util::method_vis(&**method) == ast::Public; if meth_public || tr.is_some() { self.exported_items.insert(method.id); } @@ -456,8 +457,8 @@ impl<'a> PrivacyVisitor<'a> { let imp = self.tcx.map.get_parent_did(closest_private_id); match ty::impl_trait_ref(self.tcx, imp) { Some(..) => return Allowable, - _ if m.vis == ast::Public => return Allowable, - _ => m.vis + _ if ast_util::method_vis(&**m) == ast::Public => return Allowable, + _ => ast_util::method_vis(&**m) } } Some(ast_map::NodeTraitMethod(_)) => { @@ -1078,7 +1079,7 @@ impl<'a> SanePrivacyVisitor<'a> { "visibility qualifiers have no effect on trait \ impls"); for m in methods.iter() { - check_inherited(m.span, m.vis, ""); + check_inherited(m.span, ast_util::method_vis(&**m), ""); } } @@ -1110,7 +1111,7 @@ impl<'a> SanePrivacyVisitor<'a> { for m in methods.iter() { match *m { ast::Provided(ref m) => { - check_inherited(m.span, m.vis, + check_inherited(m.span, ast_util::method_vis(&**m), "unnecessary visibility"); } ast::Required(ref m) => { @@ -1148,7 +1149,7 @@ impl<'a> SanePrivacyVisitor<'a> { match item.node { ast::ItemImpl(_, _, _, ref methods) => { for m in methods.iter() { - check_inherited(tcx, m.span, m.vis); + check_inherited(tcx, m.span, ast_util::method_vis(&**m)); } } ast::ItemForeignMod(ref fm) => { @@ -1174,7 +1175,7 @@ impl<'a> SanePrivacyVisitor<'a> { match *m { ast::Required(..) => {} ast::Provided(ref m) => check_inherited(tcx, m.span, - m.vis), + ast_util::method_vis(&**m)), } } } @@ -1344,7 +1345,7 @@ impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> { // methods will be visible as `Public::foo`. let mut found_pub_static = false; for method in methods.iter() { - if method.explicit_self.node == ast::SelfStatic && + if ast_util::method_explicit_self(&**method).node == ast::SelfStatic && self.exported_items.contains(&method.id) { found_pub_static = true; visit::walk_method_helper(self, &**method, ()); diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 26bb0b62cb05f..d9324574da73f 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -68,7 +68,7 @@ fn item_might_be_inlined(item: &ast::Item) -> bool { fn method_might_be_inlined(tcx: &ty::ctxt, method: &ast::Method, impl_src: ast::DefId) -> bool { if attributes_specify_inlining(method.attrs.as_slice()) || - generics_require_inlining(&method.generics) { + generics_require_inlining(ast_util::method_generics(&*method)) { return true } if is_local(impl_src) { @@ -200,7 +200,7 @@ impl<'a> ReachableContext<'a> { } } Some(ast_map::NodeMethod(method)) => { - if generics_require_inlining(&method.generics) || + if generics_require_inlining(ast_util::method_generics(&*method)) || attributes_specify_inlining(method.attrs.as_slice()) { true } else { @@ -316,14 +316,14 @@ impl<'a> ReachableContext<'a> { // Keep going, nothing to get exported } ast::Provided(ref method) => { - visit::walk_block(self, &*method.body, ()) + visit::walk_block(self, ast_util::method_body(&**method), ()) } } } ast_map::NodeMethod(method) => { let did = self.tcx.map.get_parent_did(search_item); if method_might_be_inlined(self.tcx, &*method, did) { - visit::walk_block(self, &*method.body, ()) + visit::walk_block(self, ast_util::method_body(&*method), ()) } } // Nothing to recurse on for these diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 4655507f5d1a7..6c6ac81b98530 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -22,6 +22,7 @@ use util::nodemap::{NodeMap, DefIdSet, FnvHashMap}; use syntax::ast::*; use syntax::ast; +use syntax::ast_util; use syntax::ast_util::{local_def}; use syntax::ast_util::{walk_pat, trait_method_to_ty_method}; use syntax::ext::mtwt; @@ -1298,20 +1299,20 @@ impl<'a> Resolver<'a> { // For each method... for method in methods.iter() { // Add the method to the module. - let ident = method.ident; + let ident = ast_util::method_ident(&**method); let method_name_bindings = self.add_child(ident, new_parent.clone(), ForbidDuplicateValues, method.span); - let def = match method.explicit_self.node { + let def = match ast_util::method_explicit_self(&**method).node { SelfStatic => { // Static methods become // `def_static_method`s. DefStaticMethod(local_def(method.id), FromImpl(local_def( item.id)), - method.fn_style) + ast_util::method_fn_style(&**method)) } _ => { // Non-static methods become @@ -1320,7 +1321,7 @@ impl<'a> Resolver<'a> { } }; - let is_public = method.vis == ast::Public; + let is_public = ast_util::method_vis(&**method) == ast::Public; method_name_bindings.define_value(def, method.span, is_public); @@ -4003,13 +4004,15 @@ impl<'a> Resolver<'a> { fn resolve_method(&mut self, rib_kind: RibKind, method: &Method) { - let method_generics = &method.generics; + let method_generics = ast_util::method_generics(method); let type_parameters = HasTypeParameters(method_generics, FnSpace, method.id, rib_kind); - self.resolve_function(rib_kind, Some(method.decl), type_parameters, method.body); + self.resolve_function(rib_kind, Some(ast_util::method_fn_decl(method)), + type_parameters, + ast_util::method_body(method)); } fn with_current_self_type(&mut self, self_type: &Ty, f: |&mut Resolver| -> T) -> T { @@ -4080,7 +4083,7 @@ impl<'a> Resolver<'a> { fn check_trait_method(&self, method: &Method) { // If there is a TraitRef in scope for an impl, then the method must be in the trait. for &(did, ref trait_ref) in self.current_trait_ref.iter() { - let method_name = method.ident.name; + let method_name = ast_util::method_ident(method).name; if self.method_map.borrow().find(&(method_name, did)).is_none() { let path_str = self.path_idents_to_string(&trait_ref.path); diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs index d16e2bbf66b93..a492b4ab9525e 100644 --- a/src/librustc/middle/save/mod.rs +++ b/src/librustc/middle/save/mod.rs @@ -333,7 +333,7 @@ impl <'l> DxrVisitor<'l> { }, }; - qualname.push_str(get_ident(method.ident).get()); + qualname.push_str(get_ident(ast_util::method_ident(&*method)).get()); let qualname = qualname.as_slice(); // record the decl for this def (if it has one) @@ -349,17 +349,18 @@ impl <'l> DxrVisitor<'l> { decl_id, scope_id); - self.process_formals(&method.decl.inputs, qualname, e); + let m_decl = ast_util::method_fn_decl(&*method); + self.process_formals(&m_decl.inputs, qualname, e); // walk arg and return types - for arg in method.decl.inputs.iter() { + for arg in m_decl.inputs.iter() { self.visit_ty(&*arg.ty, e); } - self.visit_ty(&*method.decl.output, e); + self.visit_ty(m_decl.output, e); // walk the fn body - self.visit_block(&*method.body, DxrVisitorEnv::new_nested(method.id)); + self.visit_block(ast_util::method_body(&*method), DxrVisitorEnv::new_nested(method.id)); - self.process_generic_params(&method.generics, + self.process_generic_params(ast_util::method_generics(&*method), method.span, qualname, method.id, diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 9acd04871999a..e3af8f93eadb3 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -1138,10 +1138,10 @@ pub fn create_function_debug_context(cx: &CrateContext, } } ast_map::NodeMethod(ref method) => { - (method.ident, - method.decl, - &method.generics, - method.body, + (ast_util::method_ident(&**method), + ast_util::method_fn_decl(&**method), + ast_util::method_generics(&**method), + ast_util::method_body(&**method), method.span, true) } @@ -1167,10 +1167,10 @@ pub fn create_function_debug_context(cx: &CrateContext, ast_map::NodeTraitMethod(ref trait_method) => { match **trait_method { ast::Provided(ref method) => { - (method.ident, - method.decl, - &method.generics, - method.body, + (ast_util::method_ident(&**method), + ast_util::method_fn_decl(&**method), + ast_util::method_generics(&**method), + ast_util::method_body(&**method), method.span, true) } diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index 2ebbc2f5340b7..f11577482b6dc 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -128,11 +128,12 @@ pub fn maybe_instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId) let impl_tpt = ty::lookup_item_type(ccx.tcx(), impl_did); let unparameterized = impl_tpt.generics.types.is_empty() && - mth.generics.ty_params.is_empty(); + ast_util::method_generics(&*mth).ty_params.is_empty(); if unparameterized { let llfn = get_item_val(ccx, mth.id); - trans_fn(ccx, &*mth.decl, &*mth.body, llfn, + trans_fn(ccx, ast_util::method_fn_decl(&*mth), + ast_util::method_body(&*mth), llfn, ¶m_substs::empty(), mth.id, []); } local_def(mth.id) diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index c79e435707aee..6b54556bbe0ac 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -38,7 +38,7 @@ use std::c_str::ToCStr; use std::gc::Gc; use syntax::abi::Rust; use syntax::parse::token; -use syntax::{ast, ast_map, visit}; +use syntax::{ast, ast_map, visit, ast_util}; /** The main "translation" pass for methods. Generates code @@ -66,9 +66,10 @@ pub fn trans_impl(ccx: &CrateContext, return; } for method in methods.iter() { - if method.generics.ty_params.len() == 0u { + if ast_util::method_generics(&**method).ty_params.len() == 0u { let llfn = get_item_val(ccx, method.id); - trans_fn(ccx, &*method.decl, &*method.body, + trans_fn(ccx, ast_util::method_fn_decl(&**method), + ast_util::method_body(&**method), llfn, ¶m_substs::empty(), method.id, []); } else { let mut v = TransItemVisitor{ ccx: ccx }; @@ -160,7 +161,7 @@ pub fn trans_static_method_callee(bcx: &Block, ast_map::NodeTraitMethod(method) => { let ident = match *method { ast::Required(ref m) => m.ident, - ast::Provided(ref m) => m.ident + ast::Provided(ref m) => ast_util::method_ident(&**m) }; ident.name } diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs index 82dfd29320589..01544214ccf7f 100644 --- a/src/librustc/middle/trans/monomorphize.rs +++ b/src/librustc/middle/trans/monomorphize.rs @@ -25,6 +25,7 @@ use util::ppaux::Repr; use syntax::abi; use syntax::ast; use syntax::ast_map; +use syntax::ast_util; use syntax::ast_util::local_def; use std::hash::{sip, Hash}; @@ -181,7 +182,8 @@ pub fn monomorphic_fn(ccx: &CrateContext, ast_map::NodeMethod(mth) => { let d = mk_lldecl(); set_llvm_fn_attrs(mth.attrs.as_slice(), d); - trans_fn(ccx, &*mth.decl, &*mth.body, d, &psubsts, mth.id, []); + trans_fn(ccx, ast_util::method_fn_decl(&*mth), + ast_util::method_body(&*mth), d, &psubsts, mth.id, []); d } ast_map::NodeTraitMethod(method) => { @@ -189,7 +191,8 @@ pub fn monomorphic_fn(ccx: &CrateContext, ast::Provided(mth) => { let d = mk_lldecl(); set_llvm_fn_attrs(mth.attrs.as_slice(), d); - trans_fn(ccx, &*mth.decl, &*mth.body, d, &psubsts, mth.id, []); + trans_fn(ccx, ast_util::method_fn_decl(&*mth), + ast_util::method_body(&*mth), d, &psubsts, mth.id, []); d } _ => { diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index cdf434f4099dc..fb29baeea73f2 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -757,14 +757,16 @@ fn check_method_body(ccx: &CrateCtxt, let method_def_id = local_def(method.id); let method_ty = ty::method(ccx.tcx, method_def_id); let method_generics = &method_ty.generics; + let m_body = ast_util::method_body(&*method); let param_env = ty::construct_parameter_environment(ccx.tcx, method_generics, - method.body.id); + m_body.id); let fty = ty::node_id_to_type(ccx.tcx, method.id); - check_bare_fn(ccx, &*method.decl, &*method.body, method.id, fty, param_env); + check_bare_fn(ccx, ast_util::method_fn_decl(&*method), + m_body, method.id, fty, param_env); } fn check_impl_methods_against_trait(ccx: &CrateCtxt, @@ -792,7 +794,7 @@ fn check_impl_methods_against_trait(ccx: &CrateCtxt, compare_impl_method(ccx.tcx, &*impl_method_ty, impl_method.span, - impl_method.body.id, + ast_util::method_body(&**impl_method).id, &**trait_method_ty, &impl_trait_ref.substs); } @@ -815,7 +817,7 @@ fn check_impl_methods_against_trait(ccx: &CrateCtxt, for trait_method in trait_methods.iter() { let is_implemented = impl_methods.iter().any( - |m| m.ident.name == trait_method.ident.name); + |m| ast_util::method_ident(&**m).name == trait_method.ident.name); let is_provided = provided_methods.iter().any( |m| m.ident.name == trait_method.ident.name); diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index b6cdeb92aa3a3..9f08dc2c92483 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -57,7 +57,8 @@ use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound}; use syntax::ast::{TraitTyParamBound, UnboxedFnTyParamBound}; use syntax::ast; use syntax::ast_map; -use syntax::ast_util::{local_def, split_trait_methods}; +use syntax::ast_util; +use syntax::ast_util::{local_def, method_ident, split_trait_methods}; use syntax::codemap::Span; use syntax::codemap; use syntax::owned_slice::OwnedSlice; @@ -213,8 +214,11 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, &ast::Provided(ref m) => { ty_method_of_trait_method( ccx, trait_id, &trait_def.generics, - &m.id, &m.ident, &m.explicit_self, - &m.generics, &m.fn_style, &*m.decl) + &m.id, &ast_util::method_ident(&**m), + ast_util::method_explicit_self(&**m), + ast_util::method_generics(&**m), + &ast_util::method_fn_style(&**m), + ast_util::method_fn_decl(&**m)) } }); @@ -330,7 +334,7 @@ fn convert_methods(ccx: &CrateCtxt, let tcx = ccx.tcx; let mut seen_methods = HashSet::new(); for m in ms.iter() { - if !seen_methods.insert(m.ident.repr(ccx.tcx)) { + if !seen_methods.insert(ast_util::method_ident(&**m).repr(tcx)) { tcx.sess.span_err(m.span, "duplicate method in trait impl"); } @@ -342,9 +346,9 @@ fn convert_methods(ccx: &CrateCtxt, rcvr_visibility)); let fty = ty::mk_bare_fn(tcx, mty.fty.clone()); debug!("method {} (id {}) has type {}", - m.ident.repr(ccx.tcx), + method_ident(&**m).repr(tcx), m.id, - fty.repr(ccx.tcx)); + fty.repr(tcx)); tcx.tcache.borrow_mut().insert( local_def(m.id), Polytype { @@ -365,23 +369,24 @@ fn convert_methods(ccx: &CrateCtxt, rcvr_visibility: ast::Visibility) -> ty::Method { - let fty = astconv::ty_of_method(ccx, m.id, m.fn_style, + let fty = astconv::ty_of_method(ccx, m.id, ast_util::method_fn_style(&*m), untransformed_rcvr_ty, - m.explicit_self, &*m.decl); + *ast_util::method_explicit_self(&*m), + ast_util::method_fn_decl(&*m)); // if the method specifies a visibility, use that, otherwise // inherit the visibility from the impl (so `foo` in `pub impl // { fn foo(); }` is public, but private in `priv impl { fn // foo(); }`). - let method_vis = m.vis.inherit_from(rcvr_visibility); + let method_vis = ast_util::method_vis(&*m).inherit_from(rcvr_visibility); let m_ty_generics = - ty_generics_for_fn_or_method(ccx, &m.generics, + ty_generics_for_fn_or_method(ccx, ast_util::method_generics(&*m), (*rcvr_ty_generics).clone()); - ty::Method::new(m.ident, + ty::Method::new(ast_util::method_ident(&*m), m_ty_generics, fty, - m.explicit_self.node, + ast_util::method_explicit_self(&*m).node, method_vis, local_def(m.id), container, diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index b0c9900be909c..bdd6d96f394a2 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -693,15 +693,18 @@ impl<'a> ErrorReporting for InferCtxt<'a> { Some(ref node) => match *node { ast_map::NodeItem(ref item) => { match item.node { - ast::ItemFn(ref fn_decl, ref pur, _, ref gen, _) => { + ast::ItemFn(fn_decl, ref pur, _, ref gen, _) => { Some((fn_decl, gen, *pur, item.ident, None, item.span)) }, _ => None } } ast_map::NodeMethod(ref m) => { - Some((&m.decl, &m.generics, m.fn_style, - m.ident, Some(m.explicit_self.node), m.span)) + Some((ast_util::method_fn_decl(&**m), + ast_util::method_generics(&**m), + ast_util::method_fn_style(&**m), + ast_util::method_ident(&**m), + Some(ast_util::method_explicit_self(&**m).node), m.span)) }, _ => None }, @@ -711,7 +714,7 @@ impl<'a> ErrorReporting for InferCtxt<'a> { = node_inner.expect("expect item fn"); let taken = lifetimes_in_scope(self.tcx, scope_id); let life_giver = LifeGiver::with_taken(taken.as_slice()); - let rebuilder = Rebuilder::new(self.tcx, *fn_decl, expl_self, + let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self, generics, same_regions, &life_giver); let (fn_decl, expl_self, generics) = rebuilder.rebuild(); self.give_expl_lifetime_param(&fn_decl, fn_style, ident, @@ -1452,7 +1455,7 @@ fn lifetimes_in_scope(tcx: &ty::ctxt, _ => None }, ast_map::NodeMethod(m) => { - taken.push_all(m.generics.lifetimes.as_slice()); + taken.push_all(ast_util::method_generics(&*m).lifetimes.as_slice()); Some(m.id) }, _ => None diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a5c6725bd819c..2e3bb2eef7d9d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -695,29 +695,30 @@ pub struct Method { impl Clean for ast::Method { fn clean(&self) -> Item { - let inputs = match self.explicit_self.node { - ast::SelfStatic => self.decl.inputs.as_slice(), - _ => self.decl.inputs.slice_from(1) + let fn_decl = ast_util::method_fn_decl(self); + let inputs = match ast_util::method_explicit_self(self).node { + ast::SelfStatic => fn_decl.inputs.as_slice(), + _ => fn_decl.inputs.slice_from(1) }; let decl = FnDecl { inputs: Arguments { values: inputs.iter().map(|x| x.clean()).collect(), }, - output: (self.decl.output.clean()), - cf: self.decl.cf.clean(), + output: (fn_decl.output.clean()), + cf: fn_decl.cf.clean(), attrs: Vec::new() }; Item { - name: Some(self.ident.clean()), + name: Some(ast_util::method_ident(self).clean()), attrs: self.attrs.clean().move_iter().collect(), source: self.span.clean(), def_id: ast_util::local_def(self.id), - visibility: self.vis.clean(), + visibility: ast_util::method_vis(self).clean(), stability: get_stability(ast_util::local_def(self.id)), inner: MethodItem(Method { - generics: self.generics.clean(), - self_: self.explicit_self.node.clean(), - fn_style: self.fn_style.clone(), + generics: ast_util::method_generics(self).clean(), + self_: ast_util::method_explicit_self(self).node.clean(), + fn_style: ast_util::method_fn_style(self).clone(), decl: decl, }), } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index dd8304014433a..98318312d58da 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -633,6 +633,8 @@ pub type Mac = Spanned; /// There's only one flavor, now, so this could presumably be simplified. #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub enum Mac_ { + // NB: the additional ident for a macro_rules-style macro is actually + // stored in the enclosing item. Oog. MacInvocTT(Path, Vec , SyntaxContext), // new macro-invocation } @@ -950,19 +952,20 @@ pub enum ExplicitSelf_ { pub type ExplicitSelf = Spanned; -// Represents a method declaration #[deriving(PartialEq, Eq, Encodable, Decodable, Hash)] pub struct Method { - pub ident: Ident, pub attrs: Vec, - pub generics: Generics, - pub explicit_self: ExplicitSelf, - pub fn_style: FnStyle, - pub decl: P, - pub body: P, pub id: NodeId, pub span: Span, - pub vis: Visibility, + pub node: Method_ +} + +#[deriving(PartialEq, Eq, Encodable, Decodable, Hash)] +pub enum Method_ { + /// Represents a method declaration + MethDecl(Ident, Generics, ExplicitSelf, FnStyle, P, P, Visibility), + /// Represents a macro in method position + MethMac(Mac), } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index de2ecd9a26404..d0a0c4fe380f0 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -304,8 +304,10 @@ impl Map { } } + /// returns the name associated with the given NodeId's AST pub fn get_path_elem(&self, id: NodeId) -> PathElem { - match self.get(id) { + let node = self.get(id); + match node { NodeItem(item) => { match item.node { ItemMod(_) | ItemForeignMod(_) => { @@ -315,13 +317,19 @@ impl Map { } } NodeForeignItem(i) => PathName(i.ident.name), - NodeMethod(m) => PathName(m.ident.name), + NodeMethod(m) => match m.node { + MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name), + MethMac(_) => fail!("no path elem for {:?}", node) + }, NodeTraitMethod(tm) => match *tm { Required(ref m) => PathName(m.ident.name), - Provided(ref m) => PathName(m.ident.name) + Provided(m) => match m.node { + MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name), + MethMac(_) => fail!("no path elem for {:?}", node), + } }, NodeVariant(v) => PathName(v.node.name.name), - node => fail!("no path elem for {:?}", node) + _ => fail!("no path elem for {:?}", node) } } @@ -369,6 +377,8 @@ impl Map { } } + /// Given a node ID and a closure, apply the closure to the array + /// of attributes associated with the AST corresponding to the Node ID pub fn with_attrs(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T { let node = self.get(id); let attrs = match node { @@ -695,11 +705,15 @@ fn node_id_to_string(map: &Map, id: NodeId) -> String { let path_str = map.path_to_str_with_ident(id, item.ident); format!("foreign item {} (id={})", path_str, id) } - Some(NodeMethod(m)) => { - format!("method {} in {} (id={})", - token::get_ident(m.ident), - map.path_to_string(id), id) - } + Some(NodeMethod(m)) => match m.node { + MethDecl(ident, _, _, _, _, _, _) => + format!("method {} in {} (id={})", + token::get_ident(ident), + map.path_to_string(id), id), + MethMac(ref mac) => + format!("method macro {} (id={})", + pprust::mac_to_string(mac), id) + }, Some(NodeTraitMethod(ref tm)) => { let m = ast_util::trait_method_to_ty_method(&**tm); format!("method {} in {} (id={})", diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index cd38c9b3e98c1..a18d8a81ef47f 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -240,32 +240,31 @@ pub fn impl_pretty_name(trait_ref: &Option, ty: &Ty) -> Ident { token::gensym_ident(pretty.as_slice()) } -pub fn public_methods(ms: Vec> ) -> Vec> { - ms.move_iter().filter(|m| { - match m.vis { - Public => true, - _ => false - } - }).collect() -} - /// extract a TypeMethod from a TraitMethod. if the TraitMethod is /// a default, pull out the useful fields to make a TypeMethod +// +// NB: to be used only after expansion is complete, and macros are gone. pub fn trait_method_to_ty_method(method: &TraitMethod) -> TypeMethod { match *method { Required(ref m) => (*m).clone(), - Provided(ref m) => { - TypeMethod { - ident: m.ident, - attrs: m.attrs.clone(), - fn_style: m.fn_style, - decl: m.decl, - generics: m.generics.clone(), - explicit_self: m.explicit_self, - id: m.id, - span: m.span, - vis: m.vis, + Provided(m) => { + match m.node { + MethDecl(ident, ref generics, explicit_self, fn_style, decl, _, vis) => { + TypeMethod { + ident: ident, + attrs: m.attrs.clone(), + fn_style: fn_style, + decl: decl, + generics: generics.clone(), + explicit_self: explicit_self, + id: m.id, + span: m.span, + vis: vis, + } + }, + MethMac(_) => fail!("expected non-macro method declaration") } + } } } @@ -346,6 +345,9 @@ pub trait IdVisitingOperation { fn visit_id(&self, node_id: NodeId); } +/// A visitor that applies its operation to all of the node IDs +/// in a visitable thing. + pub struct IdVisitor<'a, O> { pub operation: &'a O, pub pass_through_items: bool, @@ -740,6 +742,38 @@ pub fn static_has_significant_address(mutbl: ast::Mutability, inline == InlineNever || inline == InlineNone } + +/// Macro invocations are guaranteed not to occur after expansion is complete. +/// extracting fields of a method requires a dynamic check to make sure that it's +/// not a macro invocation, though this check is guaranteed to succeed, assuming +/// that the invocations are indeed gone. +macro_rules! method_field_extractor { + ($fn_name:ident, $field_ty:ty, $field_pat:pat, $result:ident) => { + /// Returns the ident of a Method. To be used after expansion is complete + pub fn $fn_name<'a>(method: &'a ast::Method) -> $field_ty { + match method.node { + $field_pat => $result, + MethMac(_) => { + fail!("expected an AST without macro invocations"); + } + } + } + } +} + +// Note: this is unhygienic in the lifetime 'a. In order to fix this, we'd have to +// add :lifetime as a macro argument type, so that the 'a could be supplied by the macro +// invocation. +pub method_field_extractor!(method_ident,ast::Ident,MethDecl(ident,_,_,_,_,_,_),ident) +pub method_field_extractor!(method_generics,&'a ast::Generics, + MethDecl(_,ref generics,_,_,_,_,_),generics) +pub method_field_extractor!(method_explicit_self,&'a ast::ExplicitSelf, + MethDecl(_,_,ref explicit_self,_,_,_,_),explicit_self) +pub method_field_extractor!(method_fn_style,ast::FnStyle,MethDecl(_,_,_,fn_style,_,_,_),fn_style) +pub method_field_extractor!(method_fn_decl,P,MethDecl(_,_,_,_,decl,_,_),decl) +pub method_field_extractor!(method_body,P,MethDecl(_,_,_,_,_,body,_),body) +pub method_field_extractor!(method_vis,ast::Visibility,MethDecl(_,_,_,_,_,_,vis),vis) + #[cfg(test)] mod test { use ast::*; @@ -765,3 +799,4 @@ mod test { .iter().map(ident_to_segment).collect::>().as_slice())); } } + diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 7d454016d602d..46efdccadec80 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -648,16 +648,16 @@ impl<'a> MethodDef<'a> { // Create the method. box(GC) ast::Method { - ident: method_ident, attrs: self.attributes.clone(), - generics: fn_generics, - explicit_self: explicit_self, - fn_style: ast::NormalFn, - decl: fn_decl, - body: body_block, id: ast::DUMMY_NODE_ID, span: trait_.span, - vis: ast::Inherited, + node: ast::MethDecl(method_ident, + fn_generics, + explicit_self, + ast::NormalFn, + fn_decl, + body_block, + ast::Inherited) } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6e44bfa6747eb..81309181bc01f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -941,21 +941,25 @@ impl<'a> Folder for PatIdentRenamer<'a> { // expand a method fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> Gc { let id = fld.new_id(m.id); - let (rewritten_fn_decl, rewritten_body) - = expand_and_rename_fn_decl_and_block(m.decl,m.body,fld); - - // all of the other standard stuff: box(GC) ast::Method { - id: id, - ident: fld.fold_ident(m.ident), attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(), - generics: fold_generics(&m.generics, fld), - explicit_self: fld.fold_explicit_self(&m.explicit_self), - fn_style: m.fn_style, - decl: rewritten_fn_decl, - body: rewritten_body, + id: id, span: fld.new_span(m.span), - vis: m.vis + node: match m.node { + ast::MethDecl(ident, ref generics, ref explicit_self, fn_style, decl, body, vis) => { + let (rewritten_fn_decl, rewritten_body) + = expand_and_rename_fn_decl_and_block(decl,body,fld); + + ast::MethDecl(fld.fold_ident(ident), + fold_generics(generics, fld), + fld.fold_explicit_self(explicit_self), + fn_style, + rewritten_fn_decl, + rewritten_body, + vis) + }, + ast::MethMac(ref _mac) => fail!("expansion in method position not implemented yet!") + } } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 3e3b57be6e406..88c8318e1b78b 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -794,16 +794,21 @@ pub fn noop_fold_foreign_item(ni: &ForeignItem, pub fn noop_fold_method(m: &Method, folder: &mut T) -> Gc { let id = folder.new_id(m.id); // Needs to be first, for ast_map. box(GC) Method { - id: id, - ident: folder.fold_ident(m.ident), attrs: m.attrs.iter().map(|a| folder.fold_attribute(*a)).collect(), - generics: fold_generics(&m.generics, folder), - explicit_self: folder.fold_explicit_self(&m.explicit_self), - fn_style: m.fn_style, - decl: folder.fold_fn_decl(&*m.decl), - body: folder.fold_block(m.body), + id: id, span: folder.new_span(m.span), - vis: m.vis + node: match m.node { + MethDecl(ident, ref generics, ref explicit_self, fn_style, decl, body, vis) => { + MethDecl(folder.fold_ident(ident), + fold_generics(generics, folder), + folder.fold_explicit_self(explicit_self), + fn_style, + folder.fold_fn_decl(&*decl), + folder.fold_block(body), + vis) + }, + MethMac(ref mac) => MethMac(folder.fold_mac(mac)), + } } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 84db2bc5a2217..cc3a7dc095bd2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1249,16 +1249,10 @@ impl<'a> Parser<'a> { p.parse_inner_attrs_and_block(); let attrs = attrs.append(inner_attrs.as_slice()); Provided(box(GC) ast::Method { - ident: ident, attrs: attrs, - generics: generics, - explicit_self: explicit_self, - fn_style: style, - decl: d, - body: body, id: ast::DUMMY_NODE_ID, span: mk_sp(lo, hi), - vis: vis, + node: ast::MethDecl(ident, generics, explicit_self, style, d, body, vis) }) } @@ -4049,16 +4043,10 @@ impl<'a> Parser<'a> { let hi = body.span.hi; let attrs = attrs.append(inner_attrs.as_slice()); box(GC) ast::Method { - ident: ident, attrs: attrs, - generics: generics, - explicit_self: explicit_self, - fn_style: fn_style, - decl: decl, - body: body, id: ast::DUMMY_NODE_ID, span: mk_sp(lo, hi), - vis: visa, + node: ast::MethDecl(ident, generics, explicit_self, fn_style, decl, body, visa), } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9589a92348585..615a4489a73de 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -245,6 +245,10 @@ pub fn arg_to_string(arg: &ast::Arg) -> String { to_string(|s| s.print_arg(arg)) } +pub fn mac_to_string(arg: &ast::Mac) -> String { + to_string(|s| s.print_mac(arg)) +} + pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> String { match vis { ast::Public => format!("pub {}", s), @@ -342,6 +346,7 @@ impl<'a> State<'a> { match self.s.last_token() { pp::End => true, _ => false } } + // is this the beginning of a line? pub fn is_bol(&mut self) -> bool { self.s.last_token().is_eof() || self.s.last_token().is_hardbreak_tok() } @@ -627,6 +632,7 @@ impl<'a> State<'a> { } } + /// Pretty-print an item pub fn print_item(&mut self, item: &ast::Item) -> IoResult<()> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(item.span.lo)); @@ -998,11 +1004,26 @@ impl<'a> State<'a> { try!(self.hardbreak_if_not_bol()); try!(self.maybe_print_comment(meth.span.lo)); try!(self.print_outer_attributes(meth.attrs.as_slice())); - try!(self.print_fn(&*meth.decl, Some(meth.fn_style), abi::Rust, - meth.ident, &meth.generics, Some(meth.explicit_self.node), - meth.vis)); - try!(word(&mut self.s, " ")); - self.print_block_with_attrs(&*meth.body, meth.attrs.as_slice()) + match meth.node { + ast::MethDecl(ident, ref generics, ref explicit_self, fn_style, decl, body, vis) => { + try!(self.print_fn(&*decl, Some(fn_style), abi::Rust, + ident, generics, Some(explicit_self.node), + vis)); + try!(word(&mut self.s, " ")); + self.print_block_with_attrs(&*body, meth.attrs.as_slice()) + }, + ast::MethMac(codemap::Spanned { node: ast::MacInvocTT(ref pth, ref tts, _), + ..}) => { + // code copied from ItemMac: + try!(self.print_path(pth, false)); + try!(word(&mut self.s, "! ")); + try!(self.cbox(indent_unit)); + try!(self.popen()); + try!(self.print_tts(tts.as_slice())); + try!(self.pclose()); + self.end() + } + } } pub fn print_outer_attributes(&mut self, diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 7caaf2f6cc1fa..795f19d0cfb06 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -560,15 +560,21 @@ pub fn walk_fn_decl>(visitor: &mut V, pub fn walk_method_helper>(visitor: &mut V, method: &Method, env: E) { - visitor.visit_ident(method.span, method.ident, env.clone()); - visitor.visit_fn(&FkMethod(method.ident, &method.generics, method), - &*method.decl, - &*method.body, - method.span, - method.id, - env.clone()); - for attr in method.attrs.iter() { - visitor.visit_attribute(attr, env.clone()); + match method.node { + MethDecl(ident, ref generics, _, _, decl, body, _) => { + visitor.visit_ident(method.span, ident, env.clone()); + visitor.visit_fn(&FkMethod(ident, generics, method), + decl, + body, + method.span, + method.id, + env.clone()); + for attr in method.attrs.iter() { + visitor.visit_attribute(attr, env.clone()); + } + + }, + MethMac(ref mac) => visitor.visit_mac(mac, env.clone()) } } @@ -586,8 +592,12 @@ pub fn walk_fn>(visitor: &mut V, } FkMethod(_, generics, method) => { visitor.visit_generics(generics, env.clone()); - - visitor.visit_explicit_self(&method.explicit_self, env.clone()); + match method.node { + MethDecl(_, _, ref explicit_self, _, _, _, _) => + visitor.visit_explicit_self(explicit_self, env.clone()), + MethMac(ref mac) => + visitor.visit_mac(mac, env.clone()) + } } FkFnBlock(..) => {} } From c654fd1f497c0e09b0dd59a334fb426fa4b17756 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 11 Jul 2014 20:56:46 -0700 Subject: [PATCH 2/9] test case for method macros --- src/test/run-pass/macro-method-issue-4621.rs | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/run-pass/macro-method-issue-4621.rs diff --git a/src/test/run-pass/macro-method-issue-4621.rs b/src/test/run-pass/macro-method-issue-4621.rs new file mode 100644 index 0000000000000..99d47e4bfc097 --- /dev/null +++ b/src/test/run-pass/macro-method-issue-4621.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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. + +#![feature(macro_rules)] + +struct A; + +macro_rules! make_thirteen_method {() => (pub fn thirteen(&self)->int {13})} +impl A { make_thirteen_method!() } + +fn main() { + assert_eq!(A.thirteen(),13); +} + + + From b293a6604bdbca136e46a68e358be5dde1faa7a9 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 11 Jul 2014 21:17:17 -0700 Subject: [PATCH 3/9] macro method unit test case fix --- src/libsyntax/ext/expand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 81309181bc01f..f091217cf5a57 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1531,9 +1531,9 @@ mod test { } // macro_rules in method position. Sadly, unimplemented. - #[ignore] #[test] fn macro_in_method_posn(){ + #[test] fn macro_in_method_posn(){ expand_crate_str( - "macro_rules! my_method (() => fn thirteen(&self) -> int {13}) + "macro_rules! my_method (() => (fn thirteen(&self) -> int {13})) struct A; impl A{ my_method!()} fn f(){A.thirteen;}".to_string()); From c4cc3ba13019daf0cc947727b276941dcde9b623 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sat, 12 Jul 2014 22:33:30 -0700 Subject: [PATCH 4/9] update fold_method to return a smallvector This is nice for macros, to allow them to expand into multiple methods --- src/librustc/middle/astencode.rs | 7 ++++-- src/libsyntax/ast_map.rs | 7 +++--- src/libsyntax/fold.rs | 39 ++++++++++++++++++++++++-------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 8fa7d7c2aaa77..d58023a48756f 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -345,7 +345,9 @@ fn simplify_ast(ii: e::InlinedItemRef) -> ast::InlinedItem { // HACK we're not dropping items. e::IIItemRef(i) => ast::IIItem(fold::noop_fold_item(i, &mut fld) .expect_one("expected one item")), - e::IIMethodRef(d, p, m) => ast::IIMethod(d, p, fold::noop_fold_method(m, &mut fld)), + e::IIMethodRef(d, p, m) => ast::IIMethod(d, p, fold::noop_fold_method(m, &mut fld) + .expect_one( + "noop_fold_method must produce exactly one method")), e::IIForeignRef(i) => ast::IIForeign(fold::noop_fold_foreign_item(i, &mut fld)) } } @@ -387,7 +389,8 @@ fn renumber_and_map_ast(xcx: &ExtendedDecodeContext, ast::IIItem(fld.fold_item(i).expect_one("expected one item")) } ast::IIMethod(d, is_provided, m) => { - ast::IIMethod(xcx.tr_def_id(d), is_provided, fld.fold_method(m)) + ast::IIMethod(xcx.tr_def_id(d), is_provided, fld.fold_method(m) + .expect_one("expected one method")) } ast::IIForeign(i) => ast::IIForeign(fld.fold_foreign_item(i)) } diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index d0a0c4fe380f0..b8a0a31f9c3b6 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -571,13 +571,14 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> { m } - fn fold_method(&mut self, m: Gc) -> Gc { + fn fold_method(&mut self, m: Gc) -> SmallVector> { let parent = self.parent; self.parent = DUMMY_NODE_ID; - let m = fold::noop_fold_method(&*m, self); + let m = fold::noop_fold_method(&*m, self).expect_one( + "noop_fold_method must produce exactly one method"); assert_eq!(self.parent, m.id); self.parent = parent; - m + SmallVector::one(m) } fn fold_fn_decl(&mut self, decl: &FnDecl) -> P { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 88c8318e1b78b..fd786192cb48c 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -114,7 +114,7 @@ pub trait Folder { noop_fold_type_method(m, self) } - fn fold_method(&mut self, m: Gc) -> Gc { + fn fold_method(&mut self, m: Gc) -> SmallVector> { noop_fold_method(&*m, self) } @@ -465,10 +465,16 @@ fn fold_interpolated(nt : &token::Nonterminal, fld: &mut T) -> token: match *nt { token::NtItem(item) => token::NtItem(fld.fold_item(item) + // this is probably okay, because the only folds likely + // to peek inside interpolated nodes will be renamings/markings, + // which map single items to single items .expect_one("expected fold to produce exactly one item")), token::NtBlock(block) => token::NtBlock(fld.fold_block(block)), token::NtStmt(stmt) => token::NtStmt(fld.fold_stmt(stmt) + // this is probably okay, because the only folds likely + // to peek inside interpolated nodes will be renamings/markings, + // which map single items to single items .expect_one("expected fold to produce exactly one statement")), token::NtPat(pat) => token::NtPat(fld.fold_pat(pat)), token::NtExpr(expr) => token::NtExpr(fld.fold_expr(expr)), @@ -683,15 +689,26 @@ pub fn noop_fold_item_underscore(i: &Item_, folder: &mut T) -> Item_ ItemImpl(fold_generics(generics, folder), ifce.as_ref().map(|p| fold_trait_ref(p, folder)), folder.fold_ty(ty), - methods.iter().map(|x| folder.fold_method(*x)).collect() + methods.iter().flat_map(|x| folder.fold_method(*x).move_iter()).collect() ) } ItemTrait(ref generics, ref unbound, ref traits, ref methods) => { - let methods = methods.iter().map(|method| { - match *method { - Required(ref m) => Required(folder.fold_type_method(m)), - Provided(method) => Provided(folder.fold_method(method)) - } + let methods = methods.iter().flat_map(|method| { + let r = match *method { + Required(ref m) => + SmallVector::one(Required(folder.fold_type_method(m))).move_iter(), + Provided(method) => { + // the awkward collect/iter idiom here is because + // even though an iter and a map satisfy the same trait bound, + // they're not actually the same type, so the method arms + // don't unify. + let methods : SmallVector = + folder.fold_method(method).move_iter() + .map(|m| Provided(m)).collect(); + methods.move_iter() + } + }; + r }).collect(); ItemTrait(fold_generics(generics, folder), unbound.clone(), @@ -791,9 +808,11 @@ pub fn noop_fold_foreign_item(ni: &ForeignItem, } } -pub fn noop_fold_method(m: &Method, folder: &mut T) -> Gc { +// Default fold over a method. +// Invariant: produces exactly one method. +pub fn noop_fold_method(m: &Method, folder: &mut T) -> SmallVector> { let id = folder.new_id(m.id); // Needs to be first, for ast_map. - box(GC) Method { + SmallVector::one(box(GC) Method { attrs: m.attrs.iter().map(|a| folder.fold_attribute(*a)).collect(), id: id, span: folder.new_span(m.span), @@ -809,7 +828,7 @@ pub fn noop_fold_method(m: &Method, folder: &mut T) -> Gc { }, MethMac(ref mac) => MethMac(folder.fold_mac(mac)), } - } + }) } pub fn noop_fold_pat(p: Gc, folder: &mut T) -> Gc { From 6ee2155fe02cf5c7d8717358e2e10aff9a659b5f Mon Sep 17 00:00:00 2001 From: John Clements Date: Sat, 12 Jul 2014 18:25:39 -0700 Subject: [PATCH 5/9] remove no-stmt check nothing wrong with a statement expanding into 0 stmts, that I can see. --- src/libsyntax/ext/expand.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f091217cf5a57..6bfe69f3a0679 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -612,10 +612,6 @@ fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector> { // Keep going, outside-in. let fully_expanded = fld.fold_stmt(&*marked_after); - if fully_expanded.is_empty() { - fld.cx.span_err(pth.span, "macro didn't expand to a statement"); - return SmallVector::zero(); - } fld.cx.bt_pop(); let fully_expanded: SmallVector> = fully_expanded.move_iter() .map(|s| box(GC) Spanned { span: s.span, node: s.node.clone() }) From 6c8bb5a68a82afc0a94769b115988f4012e5991b Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 15:15:31 -0700 Subject: [PATCH 6/9] macro in method position parsing --- src/libsyntax/parse/parser.rs | 55 ++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index cc3a7dc095bd2..e0c94dffb5cae 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3246,6 +3246,7 @@ impl<'a> Parser<'a> { } else if is_ident(&self.token) && !token::is_any_keyword(&self.token) && self.look_ahead(1, |t| *t == token::NOT) { + // it's a macro invocation: check_expected_item(self, !item_attrs.is_empty()); @@ -4021,7 +4022,7 @@ impl<'a> Parser<'a> { } /// Parse a method in a trait impl, starting with `attrs` attributes. - fn parse_method(&mut self, + pub fn parse_method(&mut self, already_parsed_attrs: Option>) -> Gc { let next_attrs = self.parse_outer_attributes(); let attrs = match already_parsed_attrs { @@ -4031,22 +4032,50 @@ impl<'a> Parser<'a> { let lo = self.span.lo; - let visa = self.parse_visibility(); - let fn_style = self.parse_fn_style(); - let ident = self.parse_ident(); - let generics = self.parse_generics(); - let (explicit_self, decl) = self.parse_fn_decl_with_self(|p| { - p.parse_arg() - }); + // code copied from parse_macro_use_or_failure... abstraction! + let (method_, hi, new_attrs) = { + if !token::is_any_keyword(&self.token) + && self.look_ahead(1, |t| *t == token::NOT) + && (self.look_ahead(2, |t| *t == token::LPAREN) + || self.look_ahead(2, |t| *t == token::LBRACE)) { + // method macro. + let pth = self.parse_path(NoTypesAllowed).path; + self.expect(&token::NOT); - let (inner_attrs, body) = self.parse_inner_attrs_and_block(); - let hi = body.span.hi; - let attrs = attrs.append(inner_attrs.as_slice()); + // eat a matched-delimiter token tree: + let tts = match token::close_delimiter_for(&self.token) { + Some(ket) => { + self.bump(); + self.parse_seq_to_end(&ket, + seq_sep_none(), + |p| p.parse_token_tree()) + } + None => self.fatal("expected open delimiter") + }; + let m_ = ast::MacInvocTT(pth, tts, EMPTY_CTXT); + let m: ast::Mac = codemap::Spanned { node: m_, + span: mk_sp(self.span.lo, + self.span.hi) }; + (ast::MethMac(m), self.span.hi, attrs) + } else { + let visa = self.parse_visibility(); + let fn_style = self.parse_fn_style(); + let ident = self.parse_ident(); + let generics = self.parse_generics(); + let (explicit_self, decl) = self.parse_fn_decl_with_self(|p| { + p.parse_arg() + }); + let (inner_attrs, body) = self.parse_inner_attrs_and_block(); + let new_attrs = attrs.append(inner_attrs.as_slice()); + (ast::MethDecl(ident, generics, explicit_self, fn_style, decl, body, visa), + body.span.hi, new_attrs) + } + }; box(GC) ast::Method { - attrs: attrs, + attrs: new_attrs, id: ast::DUMMY_NODE_ID, span: mk_sp(lo, hi), - node: ast::MethDecl(ident, generics, explicit_self, fn_style, decl, body, visa), + node: method_, } } From 2c4b6d6f7d6001bf9241f90f1465f4a98490a0d9 Mon Sep 17 00:00:00 2001 From: John Clements Date: Thu, 10 Jul 2014 17:46:09 -0700 Subject: [PATCH 7/9] add make_method method to MacResult trait this allows macro results to be parsed as methods --- src/libsyntax/ext/base.rs | 18 ++++++++++++++++++ src/libsyntax/ext/tt/macro_rules.rs | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index dcb69ae8f7e64..56484c4ba59df 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -104,6 +104,9 @@ pub type IdentMacroExpanderFn = /// just into the compiler's internal macro table, for `make_def`). pub trait MacResult { /// Define a new macro. + // this should go away; the idea that a macro might expand into + // either a macro definition or an expression, depending on what + // the context wants, is kind of silly. fn make_def(&self) -> Option { None } @@ -115,6 +118,12 @@ pub trait MacResult { fn make_items(&self) -> Option>> { None } + + /// Create zero or more methods. + fn make_methods(&self) -> Option>> { + None + } + /// Create a pattern. fn make_pat(&self) -> Option> { None @@ -222,6 +231,7 @@ impl DummyResult { span: sp, } } + } impl MacResult for DummyResult { @@ -232,6 +242,14 @@ impl MacResult for DummyResult { Some(DummyResult::raw_pat(self.span)) } fn make_items(&self) -> Option>> { + // this code needs a comment... why not always just return the Some() ? + if self.expr_only { + None + } else { + Some(SmallVector::zero()) + } + } + fn make_methods(&self) -> Option>> { if self.expr_only { None } else { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 923b3e78731a0..1eb37abb781a3 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -38,7 +38,7 @@ struct ParserAnyMacro<'a> { impl<'a> ParserAnyMacro<'a> { /// Make sure we don't have any tokens left to parse, so we don't /// silently drop anything. `allow_semi` is so that "optional" - /// semilons at the end of normal expressions aren't complained + /// semicolons at the end of normal expressions aren't complained /// about e.g. the semicolon in `macro_rules! kapow( () => { /// fail!(); } )` doesn't get picked up by .parse_expr(), but it's /// allowed to be there. @@ -73,6 +73,9 @@ impl<'a> MacResult for ParserAnyMacro<'a> { let mut ret = SmallVector::zero(); loop { let mut parser = self.parser.borrow_mut(); + // so... do outer attributes attached to the macro invocation + // just disappear? This question applies to make_methods, as + // well. match parser.parse_item_with_outer_attributes() { Some(item) => ret.push(item), None => break @@ -81,6 +84,20 @@ impl<'a> MacResult for ParserAnyMacro<'a> { self.ensure_complete_parse(false); Some(ret) } + + fn make_methods(&self) -> Option>> { + let mut ret = SmallVector::zero(); + loop { + let mut parser = self.parser.borrow_mut(); + match parser.token { + EOF => break, + _ => ret.push(parser.parse_method(None)) + } + } + self.ensure_complete_parse(false); + Some(ret) + } + fn make_stmt(&self) -> Option> { let attrs = self.parser.borrow_mut().parse_outer_attributes(); let ret = self.parser.borrow_mut().parse_stmt(attrs); From bb333ca3926df37a6529e4546147642bbce89c41 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sat, 12 Jul 2014 15:00:23 -0700 Subject: [PATCH 8/9] expansion abstraction --- src/libsyntax/ext/expand.rs | 258 ++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 144 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6bfe69f3a0679..36ec299b705ba 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -37,92 +37,28 @@ pub fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. ExprMac(ref mac) => { - match (*mac).node { - // 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. - // Token-tree macros: - MacInvocTT(ref pth, ref tts, _) => { - if pth.segments.len() > 1u { - fld.cx.span_err(pth.span, - "expected macro name without module \ - separators"); - // let compilation continue - return DummyResult::raw_expr(e.span); - } - let extname = pth.segments.get(0).identifier; - let extnamestr = token::get_ident(extname); - let marked_after = match fld.extsbox.find(&extname.name) { - None => { - fld.cx.span_err( - pth.span, - format!("macro undefined: '{}!'", - extnamestr.get()).as_slice()); - - // let compilation continue - return DummyResult::raw_expr(e.span); - } - Some(&NormalTT(ref expandfun, exp_span)) => { - fld.cx.bt_push(ExpnInfo { - call_site: e.span, - callee: NameAndSpan { - name: extnamestr.get().to_string(), - format: MacroBang, - span: exp_span, - }, - }); - let fm = fresh_mark(); - // mark before: - let marked_before = mark_tts(tts.as_slice(), fm); - - // The span that we pass to the expanders we want to - // be the root of the call stack. That's the most - // relevant span and it's the actual invocation of - // the macro. - let mac_span = original_span(fld.cx); - - let expanded = match expandfun.expand(fld.cx, - mac_span.call_site, - marked_before.as_slice()).make_expr() { - Some(e) => e, - None => { - fld.cx.span_err( - pth.span, - format!("non-expression macro in expression position: {}", - extnamestr.get().as_slice() - ).as_slice()); - return DummyResult::raw_expr(e.span); - } - }; + let expanded_expr = match expand_mac_invoc(mac,&e.span, + |r|{r.make_expr()}, + |expr,fm|{mark_expr(expr,fm)}, + fld) { + Some(expr) => expr, + None => { + return DummyResult::raw_expr(e.span); + } + }; - // mark after: - mark_expr(expanded,fm) - } - _ => { - fld.cx.span_err( - pth.span, - format!("'{}' is not a tt-style macro", - extnamestr.get()).as_slice()); - return DummyResult::raw_expr(e.span); - } - }; + // Keep going, outside-in. + // + // FIXME(pcwalton): Is it necessary to clone the + // node here? + let fully_expanded = + fld.fold_expr(expanded_expr).node.clone(); + fld.cx.bt_pop(); - // Keep going, outside-in. - // - // FIXME(pcwalton): Is it necessary to clone the - // node here? - let fully_expanded = - fld.fold_expr(marked_after).node.clone(); - fld.cx.bt_pop(); - - box(GC) ast::Expr { - id: ast::DUMMY_NODE_ID, - node: fully_expanded, - span: e.span, - } - } + box(GC) ast::Expr { + id: ast::DUMMY_NODE_ID, + node: fully_expanded, + span: e.span, } } @@ -246,6 +182,88 @@ pub fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { } } +/// 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: |Box|->Option, + mark_thunk: |T,Mrk|->T, + fld: &mut MacroExpander) + -> Option { + match (*mac).node { + // 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. + // Token-tree macros: + MacInvocTT(ref pth, ref tts, _) => { + if pth.segments.len() > 1u { + fld.cx.span_err(pth.span, + "expected macro name without module \ + separators"); + // let compilation continue + return None; + } + let extname = pth.segments.get(0).identifier; + let extnamestr = token::get_ident(extname); + match fld.extsbox.find(&extname.name) { + None => { + fld.cx.span_err( + pth.span, + format!("macro undefined: '{}!'", + extnamestr.get()).as_slice()); + + // let compilation continue + None + } + Some(&NormalTT(ref expandfun, exp_span)) => { + fld.cx.bt_push(ExpnInfo { + call_site: *span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: exp_span, + }, + }); + let fm = fresh_mark(); + let marked_before = mark_tts(tts.as_slice(), fm); + + // The span that we pass to the expanders we want to + // be the root of the call stack. That's the most + // relevant span and it's the actual invocation of + // the macro. + let mac_span = original_span(fld.cx); + + let expanded = expandfun.expand(fld.cx, + mac_span.call_site, + marked_before.as_slice()); + let parsed = match parse_thunk(expanded) { + Some(e) => e, + None => { + fld.cx.span_err( + pth.span, + format!("non-expression macro in expression position: {}", + extnamestr.get().as_slice() + ).as_slice()); + return None; + } + }; + Some(mark_thunk(parsed,fm)) + } + _ => { + fld.cx.span_err( + pth.span, + format!("'{}' is not a tt-style macro", + extnamestr.get()).as_slice()); + None + } + } + } + } +} + /// Rename loop label and expand its loop body /// /// The renaming procedure for loop is different in the sense that the loop @@ -543,75 +561,27 @@ fn expand_item_mac(it: Gc, fld: &mut MacroExpander) return items; } -// expand a stmt +/// Expand a stmt +// +// I don't understand why this returns a vector... it looks like someone got +// half done adding machinery to allow macros to expand into multiple statements. fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector> { - // why the copying here and not in expand_expr? - // looks like classic changed-in-only-one-place - let (pth, tts, semi) = match s.node { - StmtMac(ref mac, semi) => { - match mac.node { - MacInvocTT(ref pth, ref tts, _) => { - (pth, (*tts).clone(), semi) - } - } - } + let (mac, semi) = match s.node { + StmtMac(ref mac, semi) => (mac, semi), _ => return expand_non_macro_stmt(s, fld) }; - if pth.segments.len() > 1u { - fld.cx.span_err(pth.span, "expected macro name without module separators"); - return SmallVector::zero(); - } - let extname = pth.segments.get(0).identifier; - let extnamestr = token::get_ident(extname); - let marked_after = match fld.extsbox.find(&extname.name) { + let expanded_stmt = match expand_mac_invoc(mac,s.span, + |r|{r.make_stmt()}, + |sts,mrk|{mark_stmt(sts,mrk)}, + fld) { + Some(stmt) => stmt, None => { - fld.cx.span_err(pth.span, - format!("macro undefined: '{}!'", - extnamestr).as_slice()); - return SmallVector::zero(); - } - - Some(&NormalTT(ref expandfun, exp_span)) => { - fld.cx.bt_push(ExpnInfo { - call_site: s.span, - callee: NameAndSpan { - name: extnamestr.get().to_string(), - format: MacroBang, - span: exp_span, - } - }); - let fm = fresh_mark(); - // mark before expansion: - let marked_tts = mark_tts(tts.as_slice(), fm); - - // See the comment in expand_expr for why we want the original span, - // not the current mac.span. - let mac_span = original_span(fld.cx); - - let expanded = match expandfun.expand(fld.cx, - mac_span.call_site, - marked_tts.as_slice()).make_stmt() { - Some(stmt) => stmt, - None => { - fld.cx.span_err(pth.span, - format!("non-statement macro in statement position: {}", - extnamestr).as_slice()); - return SmallVector::zero(); - } - }; - - mark_stmt(&*expanded,fm) - } - - _ => { - fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro", - extnamestr).as_slice()); return SmallVector::zero(); } }; // Keep going, outside-in. - let fully_expanded = fld.fold_stmt(&*marked_after); + let fully_expanded = fld.fold_stmt(&*expanded_stmt); fld.cx.bt_pop(); let fully_expanded: SmallVector> = fully_expanded.move_iter() .map(|s| box(GC) Spanned { span: s.span, node: s.node.clone() }) From aee5917556856428072cc090fb892176eaa075b3 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 13 Jul 2014 09:35:48 -0700 Subject: [PATCH 9/9] macro expansion for methods Closes #4621 --- src/libsyntax/ext/expand.rs | 72 ++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 36ec299b705ba..58689389769c9 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -544,7 +544,7 @@ fn expand_item_mac(it: Gc, fld: &mut MacroExpander) match expanded.make_items() { Some(items) => { items.move_iter() - .flat_map(|i| mark_item(i, fm).move_iter()) + .map(|i| mark_item(i, fm)) .flat_map(|i| fld.fold_item(i).move_iter()) .collect() } @@ -563,14 +563,14 @@ fn expand_item_mac(it: Gc, fld: &mut MacroExpander) /// Expand a stmt // -// I don't understand why this returns a vector... it looks like someone got +// I don't understand why this returns a vector... it looks like we're // half done adding machinery to allow macros to expand into multiple statements. fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector> { let (mac, semi) = match s.node { StmtMac(ref mac, semi) => (mac, semi), _ => return expand_non_macro_stmt(s, fld) }; - let expanded_stmt = match expand_mac_invoc(mac,s.span, + let expanded_stmt = match expand_mac_invoc(mac,&s.span, |r|{r.make_stmt()}, |sts,mrk|{mark_stmt(sts,mrk)}, fld) { @@ -905,26 +905,41 @@ impl<'a> Folder for PatIdentRenamer<'a> { } // expand a method -fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> Gc { +fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> SmallVector> { let id = fld.new_id(m.id); - box(GC) ast::Method { - attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(), - id: id, - span: fld.new_span(m.span), - node: match m.node { - ast::MethDecl(ident, ref generics, ref explicit_self, fn_style, decl, body, vis) => { - let (rewritten_fn_decl, rewritten_body) - = expand_and_rename_fn_decl_and_block(decl,body,fld); - - ast::MethDecl(fld.fold_ident(ident), - fold_generics(generics, fld), - fld.fold_explicit_self(explicit_self), - fn_style, - rewritten_fn_decl, - rewritten_body, - vis) - }, - ast::MethMac(ref _mac) => fail!("expansion in method position not implemented yet!") + match m.node { + ast::MethDecl(ident, ref generics, ref explicit_self, fn_style, decl, body, vis) => { + let (rewritten_fn_decl, rewritten_body) + = expand_and_rename_fn_decl_and_block(decl,body,fld); + SmallVector::one(box(GC) ast::Method { + attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(), + id: id, + span: fld.new_span(m.span), + node: ast::MethDecl(fld.fold_ident(ident), + fold_generics(generics, fld), + fld.fold_explicit_self(explicit_self), + fn_style, + rewritten_fn_decl, + rewritten_body, + vis) + }) + }, + ast::MethMac(ref mac) => { + let maybe_new_methods = + expand_mac_invoc(mac, &m.span, + |r|{r.make_methods()}, + |meths,mark|{ + meths.move_iter().map(|m|{mark_method(m,mark)}) + .collect()}, + fld); + + let new_methods = match maybe_new_methods { + Some(methods) => methods, + None => SmallVector::zero() + }; + + // expand again if necessary + new_methods.move_iter().flat_map(|m| fld.fold_method(m).move_iter()).collect() } } } @@ -983,7 +998,7 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_arm(arm, self) } - fn fold_method(&mut self, method: Gc) -> Gc { + fn fold_method(&mut self, method: Gc) -> SmallVector> { expand_method(method, self) } @@ -1098,12 +1113,19 @@ fn mark_pat(pat: Gc, m: Mrk) -> Gc { // apply a given mark to the given stmt. Used following the expansion of a macro. fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> Gc { Marker{mark:m}.fold_stmt(expr) - .expect_one("marking a stmt didn't return a 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: Gc, m: Mrk) -> SmallVector> { +fn mark_item(expr: Gc, m: Mrk) -> Gc { 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_method(expr: Gc, m: Mrk) -> Gc { + Marker{mark:m}.fold_method(expr) + .expect_one("marking an item didn't return exactly one method") } fn original_span(cx: &ExtCtxt) -> Gc {