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]
This commit is contained in:
bors 2014-07-13 19:16:28 +00:00
commit 7a6208f2cc
28 changed files with 561 additions and 359 deletions

View file

@ -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));
}
}

View file

@ -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))
}

View file

@ -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(_) => ()
}
}

View file

@ -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),
};

View file

@ -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, ());

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -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)
}

View file

@ -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)

View file

@ -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
}

View file

@ -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
}
_ => {

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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,
}),
}

View file

@ -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)]

View file

@ -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={})",

View file

@ -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()));
}
}

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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);
}
};
// 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<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);
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<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());

View file

@ -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);

View file

@ -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> {

View file

@ -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_,
}
}

View file

@ -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,

View file

@ -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(..) => {}
}

View file

@ -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);
}