Rollup merge of #63674 - petrochenkov:meta2, r=Centril
syntax: Support modern attribute syntax in the `meta` matcher Where "modern" means https://github.com/rust-lang/rust/pull/57367: ``` PATH PATH `(` TOKEN_STREAM `)` PATH `[` TOKEN_STREAM `]` PATH `{` TOKEN_STREAM `}` ``` Unfortunately, `meta` wasn't future-proofed using the `FOLLOW` token set like other matchers (https://github.com/rust-lang/rust/issues/34011), so code like `$meta:meta {` or `$meta:meta [` may break, and we need a crater run to find out how often this happens in practice. Closes https://github.com/rust-lang/rust/issues/49629 (by fully supporting `meta` rather than removing it.)
This commit is contained in:
commit
64130fd2c4
@ -988,10 +988,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,
|
||||
}
|
||||
|
@ -196,6 +196,11 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Path {
|
||||
}
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct ::syntax::ast::AttrItem {
|
||||
path,
|
||||
tokens,
|
||||
});
|
||||
|
||||
impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
|
||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
||||
// Make sure that these have been filtered out.
|
||||
@ -203,19 +208,15 @@ impl<'a> HashStable<StableHashingContext<'a>> 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);
|
||||
}
|
||||
}
|
||||
|
@ -2139,18 +2139,29 @@ 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,
|
||||
}
|
||||
|
||||
// Compatibility impl to avoid churn, consider removing.
|
||||
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
|
||||
|
@ -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;
|
||||
@ -255,9 +255,8 @@ impl MetaItem {
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
/// Extracts the `MetaItem` from inside this `Attribute`.
|
||||
pub fn meta(&self) -> Option<MetaItem> {
|
||||
impl AttrItem {
|
||||
crate fn meta(&self, span: Span) -> Option<MetaItem> {
|
||||
let mut tokens = self.tokens.trees().peekable();
|
||||
Some(MetaItem {
|
||||
path: self.path.clone(),
|
||||
@ -269,9 +268,16 @@ impl Attribute {
|
||||
} else {
|
||||
return None;
|
||||
},
|
||||
span: self.span,
|
||||
span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
/// Extracts the MetaItem from inside this Attribute.
|
||||
pub fn meta(&self) -> Option<MetaItem> {
|
||||
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>,
|
||||
@ -333,10 +339,9 @@ impl Attribute {
|
||||
DUMMY_SP,
|
||||
);
|
||||
f(&Attribute {
|
||||
item: AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
|
||||
id: self.id,
|
||||
style: self.style,
|
||||
path: meta.path,
|
||||
tokens: meta.kind.tokens(meta.span),
|
||||
is_sugared_doc: true,
|
||||
span: self.span,
|
||||
})
|
||||
@ -384,10 +389,9 @@ crate fn mk_attr_id() -> AttrId {
|
||||
|
||||
pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
|
||||
Attribute {
|
||||
item: AttrItem { path, tokens },
|
||||
id: mk_attr_id(),
|
||||
style,
|
||||
path,
|
||||
tokens,
|
||||
is_sugared_doc: false,
|
||||
span,
|
||||
}
|
||||
@ -408,10 +412,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,
|
||||
}
|
||||
@ -524,7 +530,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,
|
||||
},
|
||||
|
@ -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,11 +150,10 @@ 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 {
|
||||
.flat_map(|(item, span)| self.process_cfg_attr(ast::Attribute {
|
||||
item,
|
||||
id: attr::mk_attr_id(),
|
||||
style: attr.style,
|
||||
path,
|
||||
tokens,
|
||||
is_sugared_doc: false,
|
||||
span,
|
||||
}))
|
||||
|
@ -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;
|
||||
@ -625,9 +625,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
| Annotatable::Variant(..)
|
||||
=> panic!("unexpected annotatable"),
|
||||
})), 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
|
||||
}
|
||||
@ -1530,11 +1531,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.kind.tokens(meta.span) },
|
||||
span: at.span,
|
||||
id: at.id,
|
||||
style: at.style,
|
||||
path: meta.path,
|
||||
tokens: meta.kind.tokens(meta.span),
|
||||
is_sugared_doc: false,
|
||||
};
|
||||
} else {
|
||||
|
@ -924,7 +924,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)
|
||||
|
@ -550,7 +550,8 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
|
||||
}
|
||||
|
||||
pub fn noop_visit_attribute<T: MutVisitor>(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);
|
||||
@ -681,7 +682,10 @@ pub fn noop_visit_interpolated<T: MutVisitor>(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) =>
|
||||
|
@ -90,7 +90,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();
|
||||
@ -107,7 +107,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;
|
||||
|
||||
@ -142,7 +142,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
(attr_sp, path, tokens, style)
|
||||
(attr_sp, item, style)
|
||||
}
|
||||
_ => {
|
||||
let token_str = self.this_token_to_string();
|
||||
@ -151,10 +151,9 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
|
||||
Ok(ast::Attribute {
|
||||
item,
|
||||
id: attr::mk_attr_id(),
|
||||
style,
|
||||
path,
|
||||
tokens,
|
||||
is_sugared_doc: false,
|
||||
span,
|
||||
})
|
||||
@ -167,19 +166,19 @@ impl<'a> Parser<'a> {
|
||||
/// PATH `[` TOKEN_STREAM `]`
|
||||
/// PATH `{` TOKEN_STREAM `}`
|
||||
/// PATH
|
||||
/// PATH `=` TOKEN_TREE
|
||||
/// PATH `=` UNSUFFIXED_LIT
|
||||
/// The delimiters or `=` are still put into the resulting token stream.
|
||||
pub fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> {
|
||||
let meta = match self.token.kind {
|
||||
pub 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.kind.tokens(meta.span))
|
||||
item
|
||||
} else {
|
||||
let path = self.parse_path(PathStyle::Mod)?;
|
||||
let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
|
||||
@ -206,7 +205,7 @@ impl<'a> Parser<'a> {
|
||||
} else {
|
||||
TokenStream::empty()
|
||||
};
|
||||
(path, tokens)
|
||||
ast::AttrItem { path, tokens }
|
||||
})
|
||||
}
|
||||
|
||||
@ -263,7 +262,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
/// Matches the following grammar (per RFC 1559).
|
||||
///
|
||||
/// meta_item : IDENT ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
|
||||
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
|
||||
/// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
|
||||
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
|
||||
let nt_meta = match self.token.kind {
|
||||
@ -274,9 +273,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;
|
||||
|
@ -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.kind {
|
||||
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,
|
||||
},
|
||||
|
@ -687,7 +687,7 @@ pub enum Nonterminal {
|
||||
NtLifetime(ast::Ident),
|
||||
NtLiteral(P<ast::Expr>),
|
||||
/// Stuff inside brackets for attributes
|
||||
NtMeta(ast::MetaItem),
|
||||
NtMeta(ast::AttrItem),
|
||||
NtPath(ast::Path),
|
||||
NtVis(ast::Visibility),
|
||||
NtTT(TokenTree),
|
||||
|
@ -324,7 +324,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),
|
||||
@ -412,8 +412,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 {
|
||||
@ -629,26 +629,30 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||
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.end();
|
||||
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();
|
||||
}
|
||||
|
||||
fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
|
||||
match item {
|
||||
ast::NestedMetaItem::MetaItem(ref mi) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Attributes injected into the crate root from command line using `-Z crate-attr`.
|
||||
|
||||
use syntax::ast::{self, AttrStyle};
|
||||
use syntax::ast::{self, AttrItem, AttrStyle};
|
||||
use syntax::attr::mk_attr;
|
||||
use syntax::panictry;
|
||||
use syntax::parse::{self, token, ParseSess};
|
||||
@ -15,7 +15,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 AttrItem { path, tokens } = panictry!(parser.parse_attr_item());
|
||||
let end_span = parser.token.span;
|
||||
if parser.token != token::Eof {
|
||||
parse_sess.span_diagnostic
|
||||
|
@ -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
|
||||
|
@ -252,12 +252,6 @@ test_path!(::std);
|
||||
test_path!(std::u8,);
|
||||
test_path!(any, super, super::super::self::path, X<Y>::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)*) => {};
|
||||
|
11
src/test/ui/macros/macro-meta-items-modern.rs
Normal file
11
src/test/ui/macros/macro-meta-items-modern.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// check-pass
|
||||
|
||||
macro_rules! check { ($meta:meta) => () }
|
||||
|
||||
check!(meta(a b c d));
|
||||
check!(meta[a b c d]);
|
||||
check!(meta { a b c d });
|
||||
check!(meta);
|
||||
check!(meta = 0);
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user