Rollup merge of #34213 - josephDunne:trait_item_macros, r=jseyfried
**syntax-[breaking-change]** cc #31645 New `TraitItemKind::Macro` variant This change adds support for macro expansion inside trait items by adding the new `TraitItemKind::Macro` and associated parsing code.
This commit is contained in:
commit
f0310e061b
@ -683,6 +683,7 @@ impl<'a> LoweringContext<'a> {
|
||||
hir::TypeTraitItem(this.lower_bounds(bounds),
|
||||
default.as_ref().map(|x| this.lower_ty(x)))
|
||||
}
|
||||
TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"),
|
||||
},
|
||||
span: i.span,
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
|
||||
TraitItemKind::Method(..) | TraitItemKind::Const(..) =>
|
||||
DefPathData::ValueNs(ti.ident.name),
|
||||
TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.name),
|
||||
TraitItemKind::Macro(..) => DefPathData::MacroDef(ti.ident.name),
|
||||
};
|
||||
|
||||
let def = self.create_def(ti.id, def_data);
|
||||
|
@ -313,6 +313,7 @@ impl<'b> Resolver<'b> {
|
||||
(Def::Method(item_def_id), ValueNS)
|
||||
}
|
||||
TraitItemKind::Type(..) => (Def::AssociatedTy(def_id, item_def_id), TypeNS),
|
||||
TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"),
|
||||
};
|
||||
|
||||
self.define(module_parent, item.ident.name, ns, (def, item.span, vis));
|
||||
|
@ -1637,6 +1637,7 @@ impl<'a> Resolver<'a> {
|
||||
visit::walk_trait_item(this, trait_item)
|
||||
});
|
||||
}
|
||||
TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -1215,7 +1215,8 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
|
||||
trait_item.span);
|
||||
}
|
||||
ast::TraitItemKind::Const(_, None) |
|
||||
ast::TraitItemKind::Type(..) => {}
|
||||
ast::TraitItemKind::Type(..) |
|
||||
ast::TraitItemKind::Macro(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1430,6 +1430,7 @@ pub enum TraitItemKind {
|
||||
Const(P<Ty>, Option<P<Expr>>),
|
||||
Method(MethodSig, Option<P<Block>>),
|
||||
Type(TyParamBounds, Option<P<Ty>>),
|
||||
Macro(Mac),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
|
@ -241,6 +241,11 @@ pub trait MacResult {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create zero or more trait items.
|
||||
fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create a pattern.
|
||||
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
|
||||
None
|
||||
@ -288,6 +293,7 @@ make_MacEager! {
|
||||
pat: P<ast::Pat>,
|
||||
items: SmallVector<P<ast::Item>>,
|
||||
impl_items: SmallVector<ast::ImplItem>,
|
||||
trait_items: SmallVector<ast::TraitItem>,
|
||||
stmts: SmallVector<ast::Stmt>,
|
||||
ty: P<ast::Ty>,
|
||||
}
|
||||
@ -305,6 +311,10 @@ impl MacResult for MacEager {
|
||||
self.impl_items
|
||||
}
|
||||
|
||||
fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
|
||||
self.trait_items
|
||||
}
|
||||
|
||||
fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
|
||||
match self.stmts.as_ref().map_or(0, |s| s.len()) {
|
||||
0 => make_stmts_default!(self),
|
||||
@ -413,6 +423,14 @@ impl MacResult for DummyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVector<ast::TraitItem>> {
|
||||
if self.expr_only {
|
||||
None
|
||||
} else {
|
||||
Some(SmallVector::zero())
|
||||
}
|
||||
}
|
||||
|
||||
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<ast::Stmt>> {
|
||||
Some(SmallVector::one(
|
||||
codemap::respan(self.span,
|
||||
|
@ -81,8 +81,11 @@ impl_macro_generable! {
|
||||
"statement", .make_stmts, lift .fold_stmt, lift .visit_stmt, |_span| SmallVector::zero();
|
||||
SmallVector<P<ast::Item>>:
|
||||
"item", .make_items, lift .fold_item, lift .visit_item, |_span| SmallVector::zero();
|
||||
SmallVector<ast::TraitItem>:
|
||||
"trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item,
|
||||
|_span| SmallVector::zero();
|
||||
SmallVector<ast::ImplItem>:
|
||||
"impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item,
|
||||
"impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item,
|
||||
|_span| SmallVector::zero();
|
||||
}
|
||||
|
||||
@ -754,25 +757,10 @@ fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector
|
||||
_ => noop_fold_item(it, fld),
|
||||
}.into_iter().map(|i| Annotatable::Item(i)).collect(),
|
||||
|
||||
Annotatable::TraitItem(it) => match it.node {
|
||||
ast::TraitItemKind::Method(_, Some(_)) => {
|
||||
let ti = it.unwrap();
|
||||
SmallVector::one(ast::TraitItem {
|
||||
id: ti.id,
|
||||
ident: ti.ident,
|
||||
attrs: ti.attrs,
|
||||
node: match ti.node {
|
||||
ast::TraitItemKind::Method(sig, Some(body)) => {
|
||||
let (sig, body) = expand_and_rename_method(sig, body, fld);
|
||||
ast::TraitItemKind::Method(sig, Some(body))
|
||||
}
|
||||
_ => unreachable!()
|
||||
},
|
||||
span: ti.span,
|
||||
})
|
||||
}
|
||||
_ => fold::noop_fold_trait_item(it.unwrap(), fld)
|
||||
}.into_iter().map(|ti| Annotatable::TraitItem(P(ti))).collect(),
|
||||
Annotatable::TraitItem(it) => {
|
||||
expand_trait_item(it.unwrap(), fld).into_iter().
|
||||
map(|it| Annotatable::TraitItem(P(it))).collect()
|
||||
}
|
||||
|
||||
Annotatable::ImplItem(ii) => {
|
||||
expand_impl_item(ii.unwrap(), fld).into_iter().
|
||||
@ -900,6 +888,31 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
|
||||
-> SmallVector<ast::TraitItem> {
|
||||
match ti.node {
|
||||
ast::TraitItemKind::Method(_, Some(_)) => {
|
||||
SmallVector::one(ast::TraitItem {
|
||||
id: ti.id,
|
||||
ident: ti.ident,
|
||||
attrs: ti.attrs,
|
||||
node: match ti.node {
|
||||
ast::TraitItemKind::Method(sig, Some(body)) => {
|
||||
let (sig, body) = expand_and_rename_method(sig, body, fld);
|
||||
ast::TraitItemKind::Method(sig, Some(body))
|
||||
}
|
||||
_ => unreachable!()
|
||||
},
|
||||
span: ti.span,
|
||||
})
|
||||
}
|
||||
ast::TraitItemKind::Macro(mac) => {
|
||||
expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
|
||||
}
|
||||
_ => fold::noop_fold_trait_item(ti, fld)
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
|
||||
/// PatIdents in its arguments to perform renaming in the FnDecl and
|
||||
/// the block, returning both the new FnDecl and the new Block.
|
||||
|
@ -100,6 +100,21 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn make_trait_items(self: Box<ParserAnyMacro<'a>>)
|
||||
-> Option<SmallVector<ast::TraitItem>> {
|
||||
let mut ret = SmallVector::zero();
|
||||
loop {
|
||||
let mut parser = self.parser.borrow_mut();
|
||||
match parser.token {
|
||||
token::Eof => break,
|
||||
_ => ret.push(panictry!(parser.parse_trait_item()))
|
||||
}
|
||||
}
|
||||
self.ensure_complete_parse(false, "item");
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
|
||||
fn make_stmts(self: Box<ParserAnyMacro<'a>>)
|
||||
-> Option<SmallVector<ast::Stmt>> {
|
||||
let mut ret = SmallVector::zero();
|
||||
|
@ -945,6 +945,9 @@ pub fn noop_fold_trait_item<T: Folder>(i: TraitItem, folder: &mut T)
|
||||
TraitItemKind::Type(folder.fold_bounds(bounds),
|
||||
default.map(|x| folder.fold_ty(x)))
|
||||
}
|
||||
ast::TraitItemKind::Macro(mac) => {
|
||||
TraitItemKind::Macro(folder.fold_mac(mac))
|
||||
}
|
||||
},
|
||||
span: folder.new_span(i.span)
|
||||
})
|
||||
|
@ -1232,55 +1232,70 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
/// Parse the items in a trait declaration
|
||||
pub fn parse_trait_items(&mut self) -> PResult<'a, Vec<TraitItem>> {
|
||||
self.parse_unspanned_seq(
|
||||
&token::OpenDelim(token::Brace),
|
||||
&token::CloseDelim(token::Brace),
|
||||
SeqSep::none(),
|
||||
|p| -> PResult<'a, TraitItem> {
|
||||
maybe_whole!(no_clone_from_p p, NtTraitItem);
|
||||
let mut attrs = p.parse_outer_attributes()?;
|
||||
let lo = p.span.lo;
|
||||
pub fn parse_trait_item(&mut self) -> PResult<'a, TraitItem> {
|
||||
maybe_whole!(no_clone_from_p self, NtTraitItem);
|
||||
let mut attrs = self.parse_outer_attributes()?;
|
||||
let lo = self.span.lo;
|
||||
|
||||
let (name, node) = if p.eat_keyword(keywords::Type) {
|
||||
let TyParam {ident, bounds, default, ..} = p.parse_ty_param()?;
|
||||
p.expect(&token::Semi)?;
|
||||
(ident, TraitItemKind::Type(bounds, default))
|
||||
} else if p.is_const_item() {
|
||||
p.expect_keyword(keywords::Const)?;
|
||||
let ident = p.parse_ident()?;
|
||||
p.expect(&token::Colon)?;
|
||||
let ty = p.parse_ty_sum()?;
|
||||
let default = if p.check(&token::Eq) {
|
||||
p.bump();
|
||||
let expr = p.parse_expr()?;
|
||||
p.commit_expr_expecting(&expr, token::Semi)?;
|
||||
Some(expr)
|
||||
} else {
|
||||
p.expect(&token::Semi)?;
|
||||
None
|
||||
};
|
||||
(ident, TraitItemKind::Const(ty, default))
|
||||
let (name, node) = if self.eat_keyword(keywords::Type) {
|
||||
let TyParam {ident, bounds, default, ..} = self.parse_ty_param()?;
|
||||
self.expect(&token::Semi)?;
|
||||
(ident, TraitItemKind::Type(bounds, default))
|
||||
} else if self.is_const_item() {
|
||||
self.expect_keyword(keywords::Const)?;
|
||||
let ident = self.parse_ident()?;
|
||||
self.expect(&token::Colon)?;
|
||||
let ty = self.parse_ty_sum()?;
|
||||
let default = if self.check(&token::Eq) {
|
||||
self.bump();
|
||||
let expr = self.parse_expr()?;
|
||||
self.commit_expr_expecting(&expr, token::Semi)?;
|
||||
Some(expr)
|
||||
} else {
|
||||
let (constness, unsafety, abi) = match p.parse_fn_front_matter() {
|
||||
self.expect(&token::Semi)?;
|
||||
None
|
||||
};
|
||||
(ident, TraitItemKind::Const(ty, default))
|
||||
} else if !self.token.is_any_keyword()
|
||||
&& self.look_ahead(1, |t| *t == token::Not)
|
||||
&& (self.look_ahead(2, |t| *t == token::OpenDelim(token::Paren))
|
||||
|| self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))) {
|
||||
// trait item macro.
|
||||
// code copied from parse_macro_use_or_failure... abstraction!
|
||||
let lo = self.span.lo;
|
||||
let pth = self.parse_ident_into_path()?;
|
||||
self.expect(&token::Not)?;
|
||||
|
||||
// eat a matched-delimiter token tree:
|
||||
let delim = self.expect_open_delim()?;
|
||||
let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
|
||||
SeqSep::none(),
|
||||
|pp| pp.parse_token_tree())?;
|
||||
let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
|
||||
let m: ast::Mac = codemap::Spanned { node: m_,
|
||||
span: mk_sp(lo,
|
||||
self.last_span.hi) };
|
||||
if delim != token::Brace {
|
||||
self.expect(&token::Semi)?
|
||||
}
|
||||
(keywords::Invalid.ident(), ast::TraitItemKind::Macro(m))
|
||||
} else {
|
||||
let (constness, unsafety, abi) = match self.parse_fn_front_matter() {
|
||||
Ok(cua) => cua,
|
||||
Err(e) => {
|
||||
loop {
|
||||
match p.token {
|
||||
match self.token {
|
||||
token::Eof => break,
|
||||
|
||||
token::CloseDelim(token::Brace) |
|
||||
token::Semi => {
|
||||
p.bump();
|
||||
self.bump();
|
||||
break;
|
||||
}
|
||||
|
||||
token::OpenDelim(token::Brace) => {
|
||||
p.parse_token_tree()?;
|
||||
self.parse_token_tree()?;
|
||||
break;
|
||||
}
|
||||
|
||||
_ => p.bump()
|
||||
_ => self.bump()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1288,17 +1303,17 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let ident = p.parse_ident()?;
|
||||
let mut generics = p.parse_generics()?;
|
||||
let ident = self.parse_ident()?;
|
||||
let mut generics = self.parse_generics()?;
|
||||
|
||||
let d = p.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
|
||||
let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
|
||||
// This is somewhat dubious; We don't want to allow
|
||||
// argument names to be left off if there is a
|
||||
// definition...
|
||||
p.parse_arg_general(false)
|
||||
})?;
|
||||
|
||||
generics.where_clause = p.parse_where_clause()?;
|
||||
generics.where_clause = self.parse_where_clause()?;
|
||||
let sig = ast::MethodSig {
|
||||
unsafety: unsafety,
|
||||
constness: constness,
|
||||
@ -1307,39 +1322,49 @@ impl<'a> Parser<'a> {
|
||||
abi: abi,
|
||||
};
|
||||
|
||||
let body = match p.token {
|
||||
token::Semi => {
|
||||
p.bump();
|
||||
debug!("parse_trait_methods(): parsing required method");
|
||||
None
|
||||
}
|
||||
token::OpenDelim(token::Brace) => {
|
||||
debug!("parse_trait_methods(): parsing provided method");
|
||||
let (inner_attrs, body) =
|
||||
p.parse_inner_attrs_and_block()?;
|
||||
attrs.extend(inner_attrs.iter().cloned());
|
||||
Some(body)
|
||||
}
|
||||
let body = match self.token {
|
||||
token::Semi => {
|
||||
self.bump();
|
||||
debug!("parse_trait_methods(): parsing required method");
|
||||
None
|
||||
}
|
||||
token::OpenDelim(token::Brace) => {
|
||||
debug!("parse_trait_methods(): parsing provided method");
|
||||
let (inner_attrs, body) =
|
||||
self.parse_inner_attrs_and_block()?;
|
||||
attrs.extend(inner_attrs.iter().cloned());
|
||||
Some(body)
|
||||
}
|
||||
|
||||
_ => {
|
||||
let token_str = p.this_token_to_string();
|
||||
return Err(p.fatal(&format!("expected `;` or `{{`, found `{}`",
|
||||
token_str)[..]))
|
||||
}
|
||||
_ => {
|
||||
let token_str = self.this_token_to_string();
|
||||
return Err(self.fatal(&format!("expected `;` or `{{`, found `{}`",
|
||||
token_str)[..]))
|
||||
}
|
||||
};
|
||||
(ident, ast::TraitItemKind::Method(sig, body))
|
||||
};
|
||||
|
||||
Ok(TraitItem {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
ident: name,
|
||||
attrs: attrs,
|
||||
node: node,
|
||||
span: mk_sp(lo, p.last_span.hi),
|
||||
})
|
||||
Ok(TraitItem {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
ident: name,
|
||||
attrs: attrs,
|
||||
node: node,
|
||||
span: mk_sp(lo, self.last_span.hi),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Parse the items in a trait declaration
|
||||
pub fn parse_trait_items(&mut self) -> PResult<'a, Vec<TraitItem>> {
|
||||
self.parse_unspanned_seq(
|
||||
&token::OpenDelim(token::Brace),
|
||||
&token::CloseDelim(token::Brace),
|
||||
SeqSep::none(),
|
||||
|p| -> PResult<'a, TraitItem> {
|
||||
p.parse_trait_item()
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a possibly mutable type
|
||||
pub fn parse_mt(&mut self) -> PResult<'a, MutTy> {
|
||||
let mutbl = self.parse_mutability()?;
|
||||
@ -4940,7 +4965,6 @@ impl<'a> Parser<'a> {
|
||||
|
||||
/// Parse trait Foo { ... }
|
||||
fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
|
||||
|
||||
let ident = self.parse_ident()?;
|
||||
let mut tps = self.parse_generics()?;
|
||||
|
||||
|
@ -1550,6 +1550,17 @@ impl<'a> State<'a> {
|
||||
try!(self.print_associated_type(ti.ident, Some(bounds),
|
||||
default.as_ref().map(|ty| &**ty)));
|
||||
}
|
||||
ast::TraitItemKind::Macro(codemap::Spanned { ref node, .. }) => {
|
||||
// code copied from ItemKind::Mac:
|
||||
self.print_path(&node.path, false, 0)?;
|
||||
word(&mut self.s, "! ")?;
|
||||
self.cbox(INDENT_UNIT)?;
|
||||
self.popen()?;
|
||||
self.print_tts(&node.tts[..])?;
|
||||
self.pclose()?;
|
||||
word(&mut self.s, ";")?;
|
||||
self.end()?
|
||||
}
|
||||
}
|
||||
self.ann.post(self, NodeSubItem(ti.id))
|
||||
}
|
||||
|
@ -568,6 +568,9 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
|
||||
walk_list!(visitor, visit_ty_param_bound, bounds);
|
||||
walk_list!(visitor, visit_ty, default);
|
||||
}
|
||||
TraitItemKind::Macro(ref mac) => {
|
||||
visitor.visit_mac(mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
20
src/test/parse-fail/trait-non-item-macros.rs
Normal file
20
src/test/parse-fail/trait-non-item-macros.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
macro_rules! bah {
|
||||
($a:expr) => ($a)
|
||||
//~^ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `2`
|
||||
}
|
||||
|
||||
trait bar {
|
||||
bah!(2);
|
||||
}
|
||||
|
||||
fn main() {}
|
39
src/test/run-pass/trait-item-inside-macro.rs
Normal file
39
src/test/run-pass/trait-item-inside-macro.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// Issue #34183
|
||||
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
fn foo() { }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bar {
|
||||
() => {
|
||||
fn bar();
|
||||
}
|
||||
}
|
||||
|
||||
trait Bleh {
|
||||
foo!();
|
||||
bar!();
|
||||
}
|
||||
|
||||
struct Test;
|
||||
|
||||
impl Bleh for Test {
|
||||
fn bar() {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Test::bar();
|
||||
Test::foo();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user