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(&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, ¶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 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, ¶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 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 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; /// 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 } @@ -957,19 +959,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 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(&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) -> 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 { @@ -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, 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/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 { 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/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, 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); - } - }; - - // 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(marked_after).node.clone(); - fld.cx.bt_pop(); - - box(GC) ast::Expr { - id: ast::DUMMY_NODE_ID, - node: fully_expanded, - span: 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); } + }; + + // 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(); + + 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 @@ -526,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() } @@ -543,79 +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 we're +// 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); - 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> = 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 { +fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> SmallVector> { 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); + 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); - // 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 + 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) -> Gc { + fn fold_method(&mut self, method: Gc) -> SmallVector> { expand_method(method, self) } @@ -1128,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 { @@ -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>> { + 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); 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) -> 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,20 +808,27 @@ 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 { - 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(p: Gc, folder: &mut T) -> Gc { 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>) -> Gc { 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>(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(..) => {} } 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 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); +} + + +