From 12e6bea3c4ebbb15a3091e17e1bc96a847326220 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 17 Aug 2019 23:55:41 +0300 Subject: [PATCH 1/2] syntax: Split `ast::Attribute` into container and inner parts --- src/librustc/hir/lowering.rs | 6 ++++-- src/librustc/ich/impls_syntax.rs | 13 +++++++------ src/libsyntax/ast.rs | 14 ++++++++++++-- src/libsyntax/attr/mod.rs | 20 +++++++++----------- src/libsyntax/config.rs | 3 +-- src/libsyntax/ext/expand.rs | 10 +++++----- src/libsyntax/mut_visit.rs | 3 ++- src/libsyntax/parse/attr.rs | 3 +-- 8 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 0f6e834ca26df..46ec4210114ce 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -986,10 +986,12 @@ impl<'a> LoweringContext<'a> { // lower attributes (we use the AST version) there is nowhere to keep // the `HirId`s. We don't actually need HIR version of attributes anyway. Attribute { + item: AttrItem { + path: attr.path.clone(), + tokens: self.lower_token_stream(attr.tokens.clone()), + }, id: attr.id, style: attr.style, - path: attr.path.clone(), - tokens: self.lower_token_stream(attr.tokens.clone()), is_sugared_doc: attr.is_sugared_doc, span: attr.span, } diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 7003f71c8baa0..c52560b322c78 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -227,6 +227,11 @@ impl<'a> HashStable> for ast::Path { } } +impl_stable_hash_for!(struct ::syntax::ast::AttrItem { + path, + tokens, +}); + impl<'a> HashStable> for ast::Attribute { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, @@ -236,19 +241,15 @@ impl<'a> HashStable> for ast::Attribute { debug_assert!(!self.is_sugared_doc); let ast::Attribute { + ref item, id: _, style, - ref path, - ref tokens, is_sugared_doc: _, span, } = *self; + item.hash_stable(hcx, hasher); style.hash_stable(hcx, hasher); - path.hash_stable(hcx, hasher); - for tt in tokens.trees() { - tt.hash_stable(hcx, hasher); - } span.hash_stable(hcx, hasher); } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 9091607629e0f..7a0d5fc1491ab 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -2124,18 +2124,28 @@ impl rustc_serialize::Decodable for AttrId { } } +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct AttrItem { + pub path: Path, + pub tokens: TokenStream, +} + /// Metadata associated with an item. /// Doc-comments are promoted to attributes that have `is_sugared_doc = true`. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Attribute { + pub item: AttrItem, pub id: AttrId, pub style: AttrStyle, - pub path: Path, - pub tokens: TokenStream, pub is_sugared_doc: bool, pub span: Span, } +impl std::ops::Deref for Attribute { + type Target = AttrItem; + fn deref(&self) -> &Self::Target { &self.item } +} + /// `TraitRef`s appear in impls. /// /// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index bcf03b5237a8d..249d88964068e 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -9,7 +9,7 @@ pub use StabilityLevel::*; pub use crate::ast::Attribute; use crate::ast; -use crate::ast::{AttrId, AttrStyle, Name, Ident, Path, PathSegment}; +use crate::ast::{AttrItem, AttrId, AttrStyle, Name, Ident, Path, PathSegment}; use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem}; use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam}; use crate::mut_visit::visit_clobber; @@ -332,10 +332,9 @@ impl Attribute { DUMMY_SP, ); f(&Attribute { + item: AttrItem { path: meta.path, tokens: meta.node.tokens(meta.span) }, id: self.id, style: self.style, - path: meta.path, - tokens: meta.node.tokens(meta.span), is_sugared_doc: true, span: self.span, }) @@ -384,10 +383,9 @@ crate fn mk_attr_id() -> AttrId { /// Returns an inner attribute with the given value and span. pub fn mk_attr_inner(item: MetaItem) -> Attribute { Attribute { + item: AttrItem { path: item.path, tokens: item.node.tokens(item.span) }, id: mk_attr_id(), style: ast::AttrStyle::Inner, - path: item.path, - tokens: item.node.tokens(item.span), is_sugared_doc: false, span: item.span, } @@ -396,10 +394,9 @@ pub fn mk_attr_inner(item: MetaItem) -> Attribute { /// Returns an outer attribute with the given value and span. pub fn mk_attr_outer(item: MetaItem) -> Attribute { Attribute { + item: AttrItem { path: item.path, tokens: item.node.tokens(item.span) }, id: mk_attr_id(), style: ast::AttrStyle::Outer, - path: item.path, - tokens: item.node.tokens(item.span), is_sugared_doc: false, span: item.span, } @@ -410,10 +407,12 @@ pub fn mk_sugared_doc_attr(text: Symbol, span: Span) -> Attribute { let lit_kind = LitKind::Str(text, ast::StrStyle::Cooked); let lit = Lit::from_lit_kind(lit_kind, span); Attribute { + item: AttrItem { + path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)), + tokens: MetaItemKind::NameValue(lit).tokens(span), + }, id: mk_attr_id(), style, - path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)), - tokens: MetaItemKind::NameValue(lit).tokens(span), is_sugared_doc: true, span, } @@ -735,10 +734,9 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - } krate.attrs.push(Attribute { + item: AttrItem { path, tokens }, id: mk_attr_id(), style: AttrStyle::Inner, - path, - tokens, is_sugared_doc: false, span: start_span.to(end_span), }); diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 7eeea4e7bdfe1..d6bc14bc816e3 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -151,10 +151,9 @@ impl<'a> StripUnconfigured<'a> { // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. expanded_attrs.into_iter() .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute { + item: ast::AttrItem { path, tokens }, id: attr::mk_attr_id(), style: attr.style, - path, - tokens, is_sugared_doc: false, span, })) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index dac402921b95c..e9a70625a3e00 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, Block, Ident, LitKind, NodeId, PatKind, Path}; +use crate::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path}; use crate::ast::{MacStmtStyle, StmtKind, ItemKind}; use crate::attr::{self, HasAttrs}; use crate::source_map::respan; @@ -526,9 +526,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()), Annotatable::Expr(expr) => token::NtExpr(expr), })), DUMMY_SP).into(); - let input = self.extract_proc_macro_attr_input(attr.tokens, span); + let input = self.extract_proc_macro_attr_input(attr.item.tokens, span); let tok_result = expander.expand(self.cx, span, input, item_tok); - let res = self.parse_ast_fragment(tok_result, fragment_kind, &attr.path, span); + let res = + self.parse_ast_fragment(tok_result, fragment_kind, &attr.item.path, span); self.gate_proc_macro_expansion(span, &res); res } @@ -1327,11 +1328,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items); *at = attr::Attribute { + item: AttrItem { path: meta.path, tokens: meta.node.tokens(meta.span) }, span: at.span, id: at.id, style: at.style, - path: meta.path, - tokens: meta.node.tokens(meta.span), is_sugared_doc: false, }; } else { diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 18d4a6463558d..ce149b064fccc 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -527,7 +527,8 @@ pub fn noop_visit_local(local: &mut P, vis: &mut T) { } pub fn noop_visit_attribute(attr: &mut Attribute, vis: &mut T) { - let Attribute { id: _, style: _, path, tokens, is_sugared_doc: _, span } = attr; + let Attribute { item: AttrItem { path, tokens }, id: _, style: _, is_sugared_doc: _, span } + = attr; vis.visit_path(path); vis.visit_tts(tokens); vis.visit_span(span); diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index a42da1123600a..25bf66cf44094 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -159,10 +159,9 @@ impl<'a> Parser<'a> { }; Ok(ast::Attribute { + item: ast::AttrItem { path, tokens }, id: attr::mk_attr_id(), style, - path, - tokens, is_sugared_doc: false, span, }) From d73c85ebc28ad0adad6f4b14183b8227e99daa46 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 18 Aug 2019 01:10:56 +0300 Subject: [PATCH 2/2] syntax: Support modern attribute syntax in the `meta` matcher --- src/libsyntax/attr/mod.rs | 20 +++++++++----- src/libsyntax/config.rs | 8 +++--- src/libsyntax/ext/tt/macro_parser.rs | 2 +- src/libsyntax/mut_visit.rs | 5 +++- src/libsyntax/parse/attr.rs | 31 ++++++++++++--------- src/libsyntax/parse/parser/path.rs | 6 ++-- src/libsyntax/parse/token.rs | 2 +- src/libsyntax/print/pprust.rs | 40 +++++++++++++++------------ src/test/ui/cfg/cfg_stmt_expr.rs | 4 +-- src/test/ui/macros/macro-first-set.rs | 6 ---- 10 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index 249d88964068e..62e13244ce1aa 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -254,9 +254,8 @@ impl MetaItem { } } -impl Attribute { - /// Extracts the MetaItem from inside this Attribute. - pub fn meta(&self) -> Option { +impl AttrItem { + crate fn meta(&self, span: Span) -> Option { let mut tokens = self.tokens.trees().peekable(); Some(MetaItem { path: self.path.clone(), @@ -268,9 +267,16 @@ impl Attribute { } else { return None; }, - span: self.span, + span, }) } +} + +impl Attribute { + /// Extracts the MetaItem from inside this Attribute. + pub fn meta(&self) -> Option { + self.item.meta(self.span) + } pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, @@ -501,7 +507,7 @@ impl MetaItem { } Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt { token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident), - token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()), + token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span), token::Nonterminal::NtPath(ref path) => path.clone(), _ => return None, }, @@ -725,7 +731,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - ); let start_span = parser.token.span; - let (path, tokens) = panictry!(parser.parse_meta_item_unrestricted()); + let item = panictry!(parser.parse_attr_item()); let end_span = parser.token.span; if parser.token != token::Eof { parse_sess.span_diagnostic @@ -734,7 +740,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - } krate.attrs.push(Attribute { - item: AttrItem { path, tokens }, + item, id: mk_attr_id(), style: AttrStyle::Inner, is_sugared_doc: false, diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index d6bc14bc816e3..49f6d0ca01606 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -122,8 +122,8 @@ impl<'a> StripUnconfigured<'a> { while !parser.check(&token::CloseDelim(token::Paren)) { let lo = parser.token.span.lo(); - let (path, tokens) = parser.parse_meta_item_unrestricted()?; - expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo))); + let item = parser.parse_attr_item()?; + expanded_attrs.push((item, parser.prev_span.with_lo(lo))); parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?; } @@ -150,8 +150,8 @@ impl<'a> StripUnconfigured<'a> { // `cfg_attr` inside of another `cfg_attr`. E.g. // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. expanded_attrs.into_iter() - .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute { - item: ast::AttrItem { path, tokens }, + .flat_map(|(item, span)| self.process_cfg_attr(ast::Attribute { + item, id: attr::mk_attr_id(), style: attr.style, is_sugared_doc: false, diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index dbf14daa30e75..6093e6cb6aff8 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -936,7 +936,7 @@ fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Nonterminal { FatalError.raise() } sym::path => token::NtPath(panictry!(p.parse_path(PathStyle::Type))), - sym::meta => token::NtMeta(panictry!(p.parse_meta_item())), + sym::meta => token::NtMeta(panictry!(p.parse_attr_item())), sym::vis => token::NtVis(panictry!(p.parse_visibility(true))), sym::lifetime => if p.check_lifetime() { token::NtLifetime(p.expect_lifetime().ident) diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index ce149b064fccc..2d1041923c06f 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -657,7 +657,10 @@ pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: token::NtIdent(ident, _is_raw) => vis.visit_ident(ident), token::NtLifetime(ident) => vis.visit_ident(ident), token::NtLiteral(expr) => vis.visit_expr(expr), - token::NtMeta(meta) => vis.visit_meta_item(meta), + token::NtMeta(AttrItem { path, tokens }) => { + vis.visit_path(path); + vis.visit_tts(tokens); + } token::NtPath(path) => vis.visit_path(path), token::NtTT(tt) => vis.visit_tt(tt), token::NtImplItem(item) => diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 25bf66cf44094..e20eddda43fd2 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -98,7 +98,7 @@ impl<'a> Parser<'a> { debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", inner_parse_policy, self.token); - let (span, path, tokens, style) = match self.token.kind { + let (span, item, style) = match self.token.kind { token::Pound => { let lo = self.token.span; self.bump(); @@ -115,7 +115,7 @@ impl<'a> Parser<'a> { }; self.expect(&token::OpenDelim(token::Bracket))?; - let (path, tokens) = self.parse_meta_item_unrestricted()?; + let item = self.parse_attr_item()?; self.expect(&token::CloseDelim(token::Bracket))?; let hi = self.prev_span; @@ -150,7 +150,7 @@ impl<'a> Parser<'a> { } } - (attr_sp, path, tokens, style) + (attr_sp, item, style) } _ => { let token_str = self.this_token_to_string(); @@ -159,7 +159,7 @@ impl<'a> Parser<'a> { }; Ok(ast::Attribute { - item: ast::AttrItem { path, tokens }, + item, id: attr::mk_attr_id(), style, is_sugared_doc: false, @@ -176,17 +176,17 @@ impl<'a> Parser<'a> { /// PATH /// PATH `=` TOKEN_TREE /// The delimiters or `=` are still put into the resulting token stream. - crate fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> { - let meta = match self.token.kind { + crate fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> { + let item = match self.token.kind { token::Interpolated(ref nt) => match **nt { - Nonterminal::NtMeta(ref meta) => Some(meta.clone()), + Nonterminal::NtMeta(ref item) => Some(item.clone()), _ => None, }, _ => None, }; - Ok(if let Some(meta) = meta { + Ok(if let Some(item) = item { self.bump(); - (meta.path, meta.node.tokens(meta.span)) + item } else { let path = self.parse_path(PathStyle::Mod)?; let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) || @@ -213,7 +213,7 @@ impl<'a> Parser<'a> { } else { TokenStream::empty() }; - (path, tokens) + ast::AttrItem { path, tokens } }) } @@ -281,9 +281,14 @@ impl<'a> Parser<'a> { _ => None, }; - if let Some(meta) = nt_meta { - self.bump(); - return Ok(meta); + if let Some(item) = nt_meta { + return match item.meta(item.path.span) { + Some(meta) => { + self.bump(); + Ok(meta) + } + None => self.unexpected(), + } } let lo = self.token.span; diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index 3eb4d45045a9e..c19b355e0da28 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -114,9 +114,9 @@ impl<'a> Parser<'a> { pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> { let meta_ident = match self.token.kind { token::Interpolated(ref nt) => match **nt { - token::NtMeta(ref meta) => match meta.node { - ast::MetaItemKind::Word => Some(meta.path.clone()), - _ => None, + token::NtMeta(ref item) => match item.tokens.is_empty() { + true => Some(item.path.clone()), + false => None, }, _ => None, }, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index be800b4de66af..07f16daa242ab 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -690,7 +690,7 @@ pub enum Nonterminal { NtLifetime(ast::Ident), NtLiteral(P), /// Stuff inside brackets for attributes - NtMeta(ast::MetaItem), + NtMeta(ast::AttrItem), NtPath(ast::Path), NtVis(ast::Visibility), NtTT(TokenTree), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8a7009828bc44..1ac6ac5e396cf 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -306,7 +306,7 @@ fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String { crate fn nonterminal_to_string(nt: &Nonterminal) -> String { match *nt { token::NtExpr(ref e) => expr_to_string(e), - token::NtMeta(ref e) => meta_item_to_string(e), + token::NtMeta(ref e) => attr_item_to_string(e), token::NtTy(ref e) => ty_to_string(e), token::NtPath(ref e) => path_to_string(e), token::NtItem(ref e) => item_to_string(e), @@ -398,8 +398,8 @@ pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String { to_string(|s| s.print_meta_list_item(li)) } -pub fn meta_item_to_string(mi: &ast::MetaItem) -> String { - to_string(|s| s.print_meta_item(mi)) +fn attr_item_to_string(ai: &ast::AttrItem) -> String { + to_string(|s| s.print_attr_item(ai, ai.path.span)) } pub fn attribute_to_string(attr: &ast::Attribute) -> String { @@ -603,24 +603,28 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefM ast::AttrStyle::Inner => self.word("#!["), ast::AttrStyle::Outer => self.word("#["), } - self.ibox(0); - match attr.tokens.trees().next() { - Some(TokenTree::Delimited(_, delim, tts)) => { - self.print_mac_common( - Some(MacHeader::Path(&attr.path)), false, None, delim, tts, true, attr.span - ); - } - tree => { - self.print_path(&attr.path, false, 0); - if tree.is_some() { - self.space(); - self.print_tts(attr.tokens.clone(), true); - } + self.print_attr_item(&attr.item, attr.span); + self.word("]"); + } + } + + fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) { + self.ibox(0); + match item.tokens.trees().next() { + Some(TokenTree::Delimited(_, delim, tts)) => { + self.print_mac_common( + Some(MacHeader::Path(&item.path)), false, None, delim, tts, true, span + ); + } + tree => { + self.print_path(&item.path, false, 0); + if tree.is_some() { + self.space(); + self.print_tts(item.tokens.clone(), true); } } - self.end(); - self.word("]"); } + self.end(); } fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) { diff --git a/src/test/ui/cfg/cfg_stmt_expr.rs b/src/test/ui/cfg/cfg_stmt_expr.rs index e466ad69f721c..6381bb2d58877 100644 --- a/src/test/ui/cfg/cfg_stmt_expr.rs +++ b/src/test/ui/cfg/cfg_stmt_expr.rs @@ -57,7 +57,7 @@ fn main() { // check that macro expanded code works macro_rules! if_cfg { - ($cfg:meta $ib:block else $eb:block) => { + ($cfg:meta? $ib:block else $eb:block) => { { let r; #[cfg($cfg)] @@ -69,7 +69,7 @@ fn main() { } } - let n = if_cfg!(unset { + let n = if_cfg!(unset? { 413 } else { 612 diff --git a/src/test/ui/macros/macro-first-set.rs b/src/test/ui/macros/macro-first-set.rs index a21e4cd201a4f..0d36332c94c16 100644 --- a/src/test/ui/macros/macro-first-set.rs +++ b/src/test/ui/macros/macro-first-set.rs @@ -252,12 +252,6 @@ test_path!(::std); test_path!(std::u8,); test_path!(any, super, super::super::self::path, X::Z<'a, T=U>); -macro_rules! test_meta_block { - ($($m:meta)* $b:block) => {}; -} - -test_meta_block!(windows {}); - macro_rules! test_lifetime { (1. $($l:lifetime)* $($b:block)*) => {}; (2. $($b:block)* $($l:lifetime)*) => {};