about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-07-13 19:16:28 +0000
committerbors <bors@rust-lang.org>2014-07-13 19:16:28 +0000
commit7a6208f2cc1bbe29dc42b21d27e98894b8bacc04 (patch)
treed4341cdd32f5d747a6289b2335012ec8f435d151
parentfbeee04f31ac16fe61f29749145a29e562d065fe (diff)
parentaee5917556856428072cc090fb892176eaa075b3 (diff)
downloadrust-7a6208f2cc1bbe29dc42b21d27e98894b8bacc04.tar.gz
rust-7a6208f2cc1bbe29dc42b21d27e98894b8bacc04.zip
auto merge of #15646 : jbclements/rust/method-macros, r=cmr
This patch adds support for macros in method position. It follows roughly the template for Item macros, where an outer `Method` wrapper contains a `Method_` enum which can either be a macro invocation or a standard macro definition. 

One note; adding support for macros that expand into multiple methods is not included here, but should be a simple parser change, since this patch updates the type of fold_macro to return a smallvector of methods.

For reviewers, please pay special attention to the parser changes; these are the ones I'm most concerned about.

Because of the small change to the interface of fold_method, this is a ...

[breaking change]
-rw-r--r--src/librustc/metadata/encoder.rs4
-rw-r--r--src/librustc/middle/astencode.rs9
-rw-r--r--src/librustc/middle/dead.rs6
-rw-r--r--src/librustc/middle/effect.rs3
-rw-r--r--src/librustc/middle/privacy.rs19
-rw-r--r--src/librustc/middle/reachable.rs8
-rw-r--r--src/librustc/middle/resolve.rs17
-rw-r--r--src/librustc/middle/save/mod.rs13
-rw-r--r--src/librustc/middle/trans/debuginfo.rs16
-rw-r--r--src/librustc/middle/trans/inline.rs5
-rw-r--r--src/librustc/middle/trans/meth.rs9
-rw-r--r--src/librustc/middle/trans/monomorphize.rs7
-rw-r--r--src/librustc/middle/typeck/check/mod.rs10
-rw-r--r--src/librustc/middle/typeck/collect.rs29
-rw-r--r--src/librustc/middle/typeck/infer/error_reporting.rs13
-rw-r--r--src/librustdoc/clean/mod.rs21
-rw-r--r--src/libsyntax/ast.rs19
-rw-r--r--src/libsyntax/ast_map.rs39
-rw-r--r--src/libsyntax/ast_util.rs75
-rw-r--r--src/libsyntax/ext/base.rs18
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs14
-rw-r--r--src/libsyntax/ext/expand.rs332
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs19
-rw-r--r--src/libsyntax/fold.rs60
-rw-r--r--src/libsyntax/parse/parser.rs69
-rw-r--r--src/libsyntax/print/pprust.rs31
-rw-r--r--src/libsyntax/visit.rs32
-rw-r--r--src/test/run-pass/macro-method-issue-4621.rs23
28 files changed, 561 insertions, 359 deletions
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 493fd575a84..6cb0ab51ca1 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 fb2b4951ea3..d58023a4875 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: {}::{}",
@@ -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/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 9ec167ee826..d84c62f744e 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 782a380e23a..415135a2d04 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 7630321bd55..580e7b2db57 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 26bb0b62cb0..d9324574da7 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 4655507f5d1..6c6ac81b985 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<T>(&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 d16e2bbf66b..a492b4ab952 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 9acd0487199..e3af8f93ead 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 2ebbc2f5340..f11577482b6 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,
                        &param_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 c79e435707a..6b54556bbe0 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, &param_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 82dfd293205..01544214ccf 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 cdf434f4099..fb29baeea73 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 b6cdeb92aa3..9f08dc2c924 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 b0c9900be90..bdd6d96f394 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 a5c6725bd81..2e3bb2eef7d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -695,29 +695,30 @@ pub struct Method {
 
 impl Clean<Item> 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 d51054542b9..c7154da494f 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -640,6 +640,8 @@ pub type Mac = Spanned<Mac_>;
 /// 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<TokenTree> , SyntaxContext),   // new macro-invocation
 }
 
@@ -957,19 +959,20 @@ pub enum ExplicitSelf_ {
 
 pub type ExplicitSelf = Spanned<ExplicitSelf_>;
 
-// Represents a method declaration
 #[deriving(PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct Method {
-    pub ident: Ident,
     pub attrs: Vec<Attribute>,
-    pub generics: Generics,
-    pub explicit_self: ExplicitSelf,
-    pub fn_style: FnStyle,
-    pub decl: P<FnDecl>,
-    pub body: P<Block>,
     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<FnDecl>, P<Block>, 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 de2ecd9a264..b8a0a31f9c3 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<T>(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T {
         let node = self.get(id);
         let attrs = match node {
@@ -561,13 +571,14 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
         m
     }
 
-    fn fold_method(&mut self, m: Gc<Method>) -> Gc<Method> {
+    fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>> {
         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<FnDecl> {
@@ -695,11 +706,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 cd38c9b3e98..a18d8a81ef4 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<TraitRef>, ty: &Ty) -> Ident {
     token::gensym_ident(pretty.as_slice())
 }
 
-pub fn public_methods(ms: Vec<Gc<Method>> ) -> Vec<Gc<Method>> {
-    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<ast::FnDecl>,MethDecl(_,_,_,_,decl,_,_),decl)
+pub method_field_extractor!(method_body,P<ast::Block>,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::<Vec<PathSegment>>().as_slice()));
     }
 }
+
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index dcb69ae8f7e..56484c4ba59 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<MacroDef> {
         None
     }
@@ -115,6 +118,12 @@ pub trait MacResult {
     fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
         None
     }
+
+    /// Create zero or more methods.
+    fn make_methods(&self) -> Option<SmallVector<Gc<ast::Method>>> {
+        None
+    }
+
     /// Create a pattern.
     fn make_pat(&self) -> Option<Gc<ast::Pat>> {
         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<SmallVector<Gc<ast::Item>>> {
+        // 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<SmallVector<Gc<ast::Method>>> {
         if self.expr_only {
             None
         } else {
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 7d454016d60..46efdccadec 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 6e44bfa6747..58689389769 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -37,92 +37,28 @@ pub fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
         // 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<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
     }
 }
 
+/// 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<T>(mac: &ast::Mac, span: &codemap::Span,
+                       parse_thunk: |Box<MacResult>|->Option<T>,
+                       mark_thunk: |T,Mrk|->T,
+                       fld: &mut MacroExpander)
+    -> Option<T> {
+    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
@@ -526,7 +544,7 @@ fn expand_item_mac(it: Gc<ast::Item>, 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()
                 }
@@ -543,79 +561,27 @@ fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander)
     return items;
 }
 
-// expand a stmt
+/// Expand a stmt
+//
+// 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<Gc<Stmt>> {
-    // 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);
-    if fully_expanded.is_empty() {
-        fld.cx.span_err(pth.span, "macro didn't expand to a statement");
-        return SmallVector::zero();
-    }
+    let fully_expanded = fld.fold_stmt(&*expanded_stmt);
     fld.cx.bt_pop();
     let fully_expanded: SmallVector<Gc<Stmt>> = fully_expanded.move_iter()
             .map(|s| box(GC) Spanned { span: s.span, node: s.node.clone() })
@@ -939,23 +905,42 @@ impl<'a> Folder for PatIdentRenamer<'a> {
 }
 
 // expand a method
-fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> Gc<ast::Method> {
+fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> SmallVector<Gc<ast::Method>> {
     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,
-        span: fld.new_span(m.span),
-        vis: m.vis
+    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()
+        }
     }
 }
 
@@ -1013,7 +998,7 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
         expand_arm(arm, self)
     }
 
-    fn fold_method(&mut self, method: Gc<ast::Method>) -> Gc<ast::Method> {
+    fn fold_method(&mut self, method: Gc<ast::Method>) -> SmallVector<Gc<ast::Method>> {
         expand_method(method, self)
     }
 
@@ -1128,12 +1113,19 @@ fn mark_pat(pat: Gc<ast::Pat>, m: Mrk) -> Gc<ast::Pat> {
 // apply a given mark to the given stmt. Used following the expansion of a macro.
 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> Gc<ast::Stmt> {
     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<ast::Item>, m: Mrk) -> SmallVector<Gc<ast::Item>> {
+fn mark_item(expr: Gc<ast::Item>, m: Mrk) -> Gc<ast::Item> {
     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<ast::Method>, m: Mrk) -> Gc<ast::Method> {
+    Marker{mark:m}.fold_method(expr)
+        .expect_one("marking an item didn't return exactly one method")
 }
 
 fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
@@ -1527,9 +1519,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());
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 923b3e78731..1eb37abb781 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<SmallVector<Gc<ast::Method>>> {
+        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<Gc<ast::Stmt>> {
         let attrs = self.parser.borrow_mut().parse_outer_attributes();
         let ret = self.parser.borrow_mut().parse_stmt(attrs);
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 3e3b57be6e4..fd786192cb4 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<Method>) -> Gc<Method>  {
+    fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>>  {
         noop_fold_method(&*m, self)
     }
 
@@ -465,10 +465,16 @@ fn fold_interpolated<T: Folder>(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<T: Folder>(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<ast::TraitMethod> =
+                                folder.fold_method(method).move_iter()
+                                .map(|m| Provided(m)).collect();
+                            methods.move_iter()
+                        }
+                };
+                r
             }).collect();
             ItemTrait(fold_generics(generics, folder),
                       unbound.clone(),
@@ -791,20 +808,27 @@ pub fn noop_fold_foreign_item<T: Folder>(ni: &ForeignItem,
     }
 }
 
-pub fn noop_fold_method<T: Folder>(m: &Method, folder: &mut T) -> Gc<Method> {
+// Default fold over a method.
+// Invariant: produces exactly one method.
+pub fn noop_fold_method<T: Folder>(m: &Method, folder: &mut T) -> SmallVector<Gc<Method>> {
     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),
+    SmallVector::one(box(GC) Method {
         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)),
+        }
+    })
 }
 
 pub fn noop_fold_pat<T: Folder>(p: Gc<Pat>, folder: &mut T) -> Gc<Pat> {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 84db2bc5a22..e0c94dffb5c 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)
                 })
               }
 
@@ -3252,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());
 
@@ -4027,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<Vec<Attribute>>) -> Gc<Method> {
         let next_attrs = self.parse_outer_attributes();
         let attrs = match already_parsed_attrs {
@@ -4037,28 +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 {
-            ident: ident,
-            attrs: attrs,
-            generics: generics,
-            explicit_self: explicit_self,
-            fn_style: fn_style,
-            decl: decl,
-            body: body,
+            attrs: new_attrs,
             id: ast::DUMMY_NODE_ID,
             span: mk_sp(lo, hi),
-            vis: visa,
+            node: method_,
         }
     }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 9589a923485..615a4489a73 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 7caaf2f6cc1..795f19d0cfb 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -560,15 +560,21 @@ pub fn walk_fn_decl<E: Clone, V: Visitor<E>>(visitor: &mut V,
 pub fn walk_method_helper<E: Clone, V: Visitor<E>>(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<E: Clone, V: Visitor<E>>(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(..) => {}
     }
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 00000000000..99d47e4bfc0
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
+}
+
+
+