From 535d4743a4bc4807446cefbc2413e02d53aa0a85 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Sat, 17 Aug 2019 23:55:41 +0300
Subject: [PATCH 1/3] 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        | 14 +++++++-------
 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, 39 insertions(+), 27 deletions(-)

diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 4310bafd7812a..2238a56b29d04 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -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,
         }
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index bdcf9e42ac2a8..23a2f115e05e2 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -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);
     }
 }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 7a5c92167bc45..e2c8e37e11c30 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -2139,18 +2139,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 122cb7fb12b24..009259352ba68 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;
@@ -333,10 +333,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 +383,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 +406,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,
     }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 990358c674ff7..a9a5c3efd0608 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 02e7c6775a49d..2b0fc5d3677c7 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;
@@ -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 {
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 80dfe9e5be0ad..4b78282b78733 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -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);
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 44688bd36b5fb..707f903916c6d 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -151,10 +151,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 957986d05620238799ae7053d505f742a0c7d640 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Sun, 18 Aug 2019 01:10:56 +0300
Subject: [PATCH 2/3] syntax: Support modern attribute syntax in the `meta`
 matcher

---
 src/libsyntax/attr/mod.rs             | 16 +++++++----
 src/libsyntax/config.rs               |  8 +++---
 src/libsyntax/ext/mbe/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/libsyntax_ext/cmdline_attrs.rs    |  4 +--
 src/test/ui/cfg/cfg_stmt_expr.rs      |  4 +--
 src/test/ui/macros/macro-first-set.rs |  6 ----
 11 files changed, 68 insertions(+), 56 deletions(-)

diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 009259352ba68..7bef693a5be4c 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -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>,
@@ -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,
             },
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index a9a5c3efd0608..2923cc86ba029 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/mbe/macro_parser.rs b/src/libsyntax/ext/mbe/macro_parser.rs
index 8f49ba9572d77..d1c50fd85945d 100644
--- a/src/libsyntax/ext/mbe/macro_parser.rs
+++ b/src/libsyntax/ext/mbe/macro_parser.rs
@@ -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)
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 4b78282b78733..3923b9f297b9f 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -682,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) =>
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 707f903916c6d..f3298b6a65dcf 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -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,7 +151,7 @@ impl<'a> Parser<'a> {
         };
 
         Ok(ast::Attribute {
-            item: ast::AttrItem { path, tokens },
+            item,
             id: attr::mk_attr_id(),
             style,
             is_sugared_doc: false,
@@ -168,17 +168,17 @@ impl<'a> Parser<'a> {
     ///     PATH
     ///     PATH `=` TOKEN_TREE
     /// 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)) ||
@@ -205,7 +205,7 @@ impl<'a> Parser<'a> {
             } else {
                 TokenStream::empty()
             };
-            (path, tokens)
+            ast::AttrItem { path, tokens }
         })
     }
 
@@ -273,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;
diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs
index 463ae9124ca23..ca823991a2e5f 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.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,
             },
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index fe3b51aa246b8..fd78a2bd53442 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -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),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 4b9c2d13f26b9..7d4ffe493d709 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -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,24 +629,28 @@ 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.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/libsyntax_ext/cmdline_attrs.rs b/src/libsyntax_ext/cmdline_attrs.rs
index bb8e3df3db921..203c4a834899b 100644
--- a/src/libsyntax_ext/cmdline_attrs.rs
+++ b/src/libsyntax_ext/cmdline_attrs.rs
@@ -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
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 34529cdaa64f1..eb2504d4bfdb7 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<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)*) => {};

From 6ea4a52f47f3f66f9db9004d8ed55a819dd81523 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Tue, 1 Oct 2019 00:58:30 +0300
Subject: [PATCH 3/3] Address review comments

---
 src/libsyntax/ast.rs                          |  1 +
 src/libsyntax/parse/attr.rs                   |  4 ++--
 src/test/ui/macros/macro-meta-items-modern.rs | 11 +++++++++++
 3 files changed, 14 insertions(+), 2 deletions(-)
 create mode 100644 src/test/ui/macros/macro-meta-items-modern.rs

diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index e2c8e37e11c30..023952042e6d4 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -2156,6 +2156,7 @@ pub struct Attribute {
     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 }
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index f3298b6a65dcf..e74f3045db804 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -166,7 +166,7 @@ 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_attr_item(&mut self) -> PResult<'a, ast::AttrItem> {
         let item = match self.token.kind {
@@ -262,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 {
diff --git a/src/test/ui/macros/macro-meta-items-modern.rs b/src/test/ui/macros/macro-meta-items-modern.rs
new file mode 100644
index 0000000000000..bc6938d4a6c9a
--- /dev/null
+++ b/src/test/ui/macros/macro-meta-items-modern.rs
@@ -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() {}