From dc3d878e0f513bde3dfad69d4f2722e2884c23a3 Mon Sep 17 00:00:00 2001 From: Joseph Dunne Date: Sat, 11 Jun 2016 02:00:07 +0100 Subject: [PATCH] Add support for macro expansion inside trait items --- src/librustc/hir/lowering.rs | 1 + src/librustc/hir/map/def_collector.rs | 1 + src/librustc_resolve/build_reduced_graph.rs | 1 + src/librustc_resolve/lib.rs | 1 + src/librustc_save_analysis/dump_visitor.rs | 3 +- src/libsyntax/ast.rs | 1 + src/libsyntax/ext/base.rs | 18 +++ src/libsyntax/ext/expand.rs | 50 +++--- src/libsyntax/ext/tt/macro_rules.rs | 15 ++ src/libsyntax/fold.rs | 3 + src/libsyntax/parse/parser.rs | 158 +++++++++++-------- src/libsyntax/print/pprust.rs | 11 ++ src/libsyntax/visit.rs | 3 + src/test/parse-fail/trait-non-item-macros.rs | 20 +++ src/test/run-pass/trait-item-inside-macro.rs | 39 +++++ 15 files changed, 238 insertions(+), 87 deletions(-) create mode 100644 src/test/parse-fail/trait-non-item-macros.rs create mode 100644 src/test/run-pass/trait-item-inside-macro.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 90dd2dad7203d..29293794bb8c1 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -700,6 +700,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, } diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index e3b6539b8ccab..c385db534a8c5 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -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); diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 775c24b6d4a67..71f894813060e 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -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)); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 0fab12c230c0f..6a82eaa890d03 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1692,6 +1692,7 @@ impl<'a> Resolver<'a> { visit::walk_trait_item(this, trait_item) }); } + TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"), }; } }); diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 4d79ddfe8cbe3..aae88bc4e3574 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -1204,7 +1204,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(_) => {} } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 40c98206c16e9..6853ba688d5f5 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1390,6 +1390,7 @@ pub enum TraitItemKind { Const(P, Option>), Method(MethodSig, Option>), Type(TyParamBounds, Option>), + Macro(Mac), } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 95624a433730a..dd07fdcf33569 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -227,6 +227,11 @@ pub trait MacResult { None } + /// Create zero or more trait items. + fn make_trait_items(self: Box) -> Option> { + None + } + /// Create a pattern. fn make_pat(self: Box) -> Option> { None @@ -274,6 +279,7 @@ make_MacEager! { pat: P, items: SmallVector>, impl_items: SmallVector, + trait_items: SmallVector, stmts: SmallVector, ty: P, } @@ -291,6 +297,10 @@ impl MacResult for MacEager { self.impl_items } + fn make_trait_items(self: Box) -> Option> { + self.trait_items + } + fn make_stmts(self: Box) -> Option> { match self.stmts.as_ref().map_or(0, |s| s.len()) { 0 => make_stmts_default!(self), @@ -399,6 +409,14 @@ impl MacResult for DummyResult { } } + fn make_trait_items(self: Box) -> Option> { + if self.expr_only { + None + } else { + Some(SmallVector::zero()) + } + } + fn make_stmts(self: Box) -> Option> { Some(SmallVector::one( codemap::respan(self.span, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 15d192b59b81e..ba389d5800628 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -70,6 +70,8 @@ impl_macro_generable! { P: "type", .make_ty, .fold_ty, |span| DummyResult::raw_ty(span); SmallVector: "impl item", .make_impl_items, lift .fold_impl_item, |_span| SmallVector::zero(); + SmallVector: + "trait item", .make_trait_items, lift .fold_trait_item, |_span| SmallVector::zero(); SmallVector>: "item", .make_items, lift .fold_item, |_span| SmallVector::zero(); SmallVector: @@ -760,25 +762,10 @@ fn expand_annotatable(a: Annotatable, _ => 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(). @@ -934,6 +921,31 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) } } +fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander) + -> SmallVector { + 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. diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index bbe989b0f40ab..fe98394c3e451 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -100,6 +100,21 @@ impl<'a> MacResult for ParserAnyMacro<'a> { Some(ret) } + fn make_trait_items(self: Box>) + -> Option> { + 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>) -> Option> { let mut ret = SmallVector::zero(); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index edf418e33325b..c44d5f368fe86 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -945,6 +945,9 @@ pub fn noop_fold_trait_item(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) }) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 22cc20b8f8c44..e0fad8f32b92a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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> { - 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; - - 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)) + 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 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 { + 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 p.parse_fn_front_matter() { + 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,37 +1322,47 @@ 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, self.last_span.hi), + }) + } - Ok(TraitItem { - id: ast::DUMMY_NODE_ID, - ident: name, - attrs: attrs, - node: node, - span: mk_sp(lo, p.last_span.hi), + + /// Parse the items in a trait declaration + pub fn parse_trait_items(&mut self) -> PResult<'a, Vec> { + 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 @@ -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()?; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 5b9ec924de953..7e9cc3763bf66 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1549,6 +1549,17 @@ impl<'a> State<'a> { 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)) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index a1d8e056b0257..45c3a6904bd3a 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -565,6 +565,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); + } } } diff --git a/src/test/parse-fail/trait-non-item-macros.rs b/src/test/parse-fail/trait-non-item-macros.rs new file mode 100644 index 0000000000000..fd356f4a81792 --- /dev/null +++ b/src/test/parse-fail/trait-non-item-macros.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/run-pass/trait-item-inside-macro.rs b/src/test/run-pass/trait-item-inside-macro.rs new file mode 100644 index 0000000000000..7c13576120b44 --- /dev/null +++ b/src/test/run-pass/trait-item-inside-macro.rs @@ -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 or the MIT license +// , 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(); +}