about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2020-11-05 20:27:48 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2020-11-09 01:47:11 +0300
commit12de1e8985016865ed6baa2262865336bbfdaa75 (patch)
tree0e45f3f1f9640b9a99a44e089648dd26ee06822b
parent1773f60ea5d42e86b8fdf78d2fc5221ead222bc1 (diff)
downloadrust-12de1e8985016865ed6baa2262865336bbfdaa75.tar.gz
rust-12de1e8985016865ed6baa2262865336bbfdaa75.zip
Do not collect tokens for doc comments
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs53
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs6
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs17
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs2
-rw-r--r--compiler/rustc_expand/src/config.rs12
-rw-r--r--compiler/rustc_expand/src/expand.rs10
-rw-r--r--compiler/rustc_middle/src/ich/impls_syntax.rs4
-rw-r--r--compiler/rustc_parse/src/lib.rs7
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs173
-rw-r--r--src/test/ui/ast-json/ast-json-noexpand-output.stdout2
-rw-r--r--src/test/ui/ast-json/ast-json-output.stdout2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/attrs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs2
17 files changed, 140 insertions, 161 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f13d67b9c15..6961905038f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2439,13 +2439,12 @@ pub struct Attribute {
     /// or the construct this attribute is contained within (inner).
     pub style: AttrStyle,
     pub span: Span,
-    pub tokens: Option<LazyTokenStream>,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum AttrKind {
     /// A normal attribute.
-    Normal(AttrItem),
+    Normal(AttrItem, Option<LazyTokenStream>),
 
     /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
     /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index ec87a88f4ab..5c0c8df2e02 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Token};
-use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
+use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing};
 
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::source_map::{BytePos, Spanned};
@@ -120,7 +120,7 @@ impl NestedMetaItem {
 impl Attribute {
     pub fn has_name(&self, name: Symbol) -> bool {
         match self.kind {
-            AttrKind::Normal(ref item) => item.path == name,
+            AttrKind::Normal(ref item, _) => item.path == name,
             AttrKind::DocComment(..) => false,
         }
     }
@@ -128,7 +128,7 @@ impl Attribute {
     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
     pub fn ident(&self) -> Option<Ident> {
         match self.kind {
-            AttrKind::Normal(ref item) => {
+            AttrKind::Normal(ref item, _) => {
                 if item.path.segments.len() == 1 {
                     Some(item.path.segments[0].ident)
                 } else {
@@ -144,14 +144,14 @@ impl Attribute {
 
     pub fn value_str(&self) -> Option<Symbol> {
         match self.kind {
-            AttrKind::Normal(ref item) => item.meta(self.span).and_then(|meta| meta.value_str()),
+            AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()),
             AttrKind::DocComment(..) => None,
         }
     }
 
     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
         match self.kind {
-            AttrKind::Normal(ref item) => match item.meta(self.span) {
+            AttrKind::Normal(ref item, _) => match item.meta(self.span) {
                 Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
                 _ => None,
             },
@@ -160,7 +160,7 @@ impl Attribute {
     }
 
     pub fn is_word(&self) -> bool {
-        if let AttrKind::Normal(item) = &self.kind {
+        if let AttrKind::Normal(item, _) = &self.kind {
             matches!(item.args, MacArgs::Empty)
         } else {
             false
@@ -246,7 +246,7 @@ impl AttrItem {
 impl Attribute {
     pub fn is_doc_comment(&self) -> bool {
         match self.kind {
-            AttrKind::Normal(_) => false,
+            AttrKind::Normal(..) => false,
             AttrKind::DocComment(..) => true,
         }
     }
@@ -254,7 +254,7 @@ impl Attribute {
     pub fn doc_str(&self) -> Option<Symbol> {
         match self.kind {
             AttrKind::DocComment(.., data) => Some(data),
-            AttrKind::Normal(ref item) if item.path == sym::doc => {
+            AttrKind::Normal(ref item, _) if item.path == sym::doc => {
                 item.meta(self.span).and_then(|meta| meta.value_str())
             }
             _ => None,
@@ -263,14 +263,14 @@ impl Attribute {
 
     pub fn get_normal_item(&self) -> &AttrItem {
         match self.kind {
-            AttrKind::Normal(ref item) => item,
+            AttrKind::Normal(ref item, _) => item,
             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
 
     pub fn unwrap_normal_item(self) -> AttrItem {
         match self.kind {
-            AttrKind::Normal(item) => item,
+            AttrKind::Normal(item, _) => item,
             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
@@ -278,10 +278,22 @@ impl Attribute {
     /// Extracts the MetaItem from inside this Attribute.
     pub fn meta(&self) -> Option<MetaItem> {
         match self.kind {
-            AttrKind::Normal(ref item) => item.meta(self.span),
+            AttrKind::Normal(ref item, _) => item.meta(self.span),
             AttrKind::DocComment(..) => None,
         }
     }
+
+    pub fn tokens(&self) -> TokenStream {
+        match self.kind {
+            AttrKind::Normal(_, ref tokens) => tokens
+                .as_ref()
+                .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
+                .create_token_stream(),
+            AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token(
+                Token::new(token::DocComment(comment_kind, self.style, data), self.span),
+            )),
+        }
+    }
 }
 
 /* Constructors */
@@ -321,11 +333,16 @@ crate fn mk_attr_id() -> AttrId {
 }
 
 pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
-    mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span)
+    mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span)
 }
 
-pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
-    Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span, tokens: None }
+pub fn mk_attr_from_item(
+    item: AttrItem,
+    tokens: Option<LazyTokenStream>,
+    style: AttrStyle,
+    span: Span,
+) -> Attribute {
+    Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
 }
 
 /// Returns an inner attribute with the given value and span.
@@ -344,13 +361,7 @@ pub fn mk_doc_comment(
     data: Symbol,
     span: Span,
 ) -> Attribute {
-    Attribute {
-        kind: AttrKind::DocComment(comment_kind, data),
-        id: mk_attr_id(),
-        style,
-        span,
-        tokens: None,
-    }
+    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
 }
 
 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 517717eebd9..91ba069cf60 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -589,17 +589,17 @@ 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 { kind, id: _, style: _, span, tokens } = attr;
+    let Attribute { kind, id: _, style: _, span } = attr;
     match kind {
-        AttrKind::Normal(AttrItem { path, args, tokens }) => {
+        AttrKind::Normal(AttrItem { path, args, tokens }, attr_tokens) => {
             vis.visit_path(path);
             visit_mac_args(args, vis);
             visit_lazy_tts(tokens, vis);
+            visit_lazy_tts(attr_tokens, vis);
         }
         AttrKind::DocComment(..) => {}
     }
     vis.visit_span(span);
-    visit_lazy_tts(tokens, vis);
 }
 
 pub fn noop_visit_mac<T: MutVisitor>(mac: &mut MacCall, vis: &mut T) {
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 2ab6667ac3c..6f65c0a1efc 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -886,7 +886,7 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
 
 pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
     match attr.kind {
-        AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
+        AttrKind::Normal(ref item, ref _tokens) => walk_mac_args(visitor, &item.args),
         AttrKind::DocComment(..) => {}
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 599599f415f..9a5fd2307d5 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -966,17 +966,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // Note that we explicitly do not walk the path. Since we don't really
         // 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.
+        // Tokens are also not needed after macro expansion and parsing.
         let kind = match attr.kind {
-            AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem {
-                path: item.path.clone(),
-                args: self.lower_mac_args(&item.args),
-                tokens: None,
-            }),
+            AttrKind::Normal(ref item, _) => AttrKind::Normal(
+                AttrItem {
+                    path: item.path.clone(),
+                    args: self.lower_mac_args(&item.args),
+                    tokens: None,
+                },
+                None,
+            ),
             AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
 
-        // Tokens aren't needed after macro expansion and parsing
-        Attribute { kind, id: attr.id, style: attr.style, span: attr.span, tokens: None }
+        Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
     }
 
     fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 9fcba902443..a64014f5acb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -426,7 +426,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
         self.maybe_print_comment(attr.span.lo());
         match attr.kind {
-            ast::AttrKind::Normal(ref item) => {
+            ast::AttrKind::Normal(ref item, _) => {
                 match attr.style {
                     ast::AttrStyle::Inner => self.word("#!["),
                     ast::AttrStyle::Outer => self.word("#["),
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index c124ab64218..c2239a2f659 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -291,8 +291,7 @@ impl<'a> StripUnconfigured<'a> {
         expanded_attrs
             .into_iter()
             .flat_map(|(item, span)| {
-                let orig_tokens =
-                    attr.tokens.as_ref().unwrap_or_else(|| panic!("Missing tokens for {:?}", attr));
+                let orig_tokens = attr.tokens();
 
                 // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
                 // and producing an attribute of the form `#[attr]`. We
@@ -302,7 +301,7 @@ impl<'a> StripUnconfigured<'a> {
 
                 // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
                 // for `attr` when we expand it to `#[attr]`
-                let pound_token = orig_tokens.create_token_stream().trees().next().unwrap();
+                let pound_token = orig_tokens.trees().next().unwrap();
                 if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) {
                     panic!("Bad tokens for attribute {:?}", attr);
                 }
@@ -316,13 +315,12 @@ impl<'a> StripUnconfigured<'a> {
                         .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
                         .create_token_stream(),
                 );
-
-                let mut attr = attr::mk_attr_from_item(attr.style, item, span);
-                attr.tokens = Some(LazyTokenStream::new(TokenStream::new(vec![
+                let tokens = Some(LazyTokenStream::new(TokenStream::new(vec![
                     (pound_token, Spacing::Alone),
                     (bracket_group, Spacing::Alone),
                 ])));
-                self.process_cfg_attr(attr)
+
+                self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span))
             })
             .collect()
     }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index f6959591b56..e06d81d49d1 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1778,15 +1778,13 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
 
             let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
             *at = ast::Attribute {
-                kind: ast::AttrKind::Normal(AttrItem {
-                    path: meta.path,
-                    args: meta.kind.mac_args(meta.span),
-                    tokens: None,
-                }),
+                kind: ast::AttrKind::Normal(
+                    AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), tokens: None },
+                    None,
+                ),
                 span: at.span,
                 id: at.id,
                 style: at.style,
-                tokens: None,
             };
         } else {
             noop_visit_attribute(at, self)
diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs
index cab2ca2919f..bfbe15749ee 100644
--- a/compiler/rustc_middle/src/ich/impls_syntax.rs
+++ b/compiler/rustc_middle/src/ich/impls_syntax.rs
@@ -40,8 +40,8 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {
         debug_assert!(!attr.ident().map_or(false, |ident| self.is_ignored_attr(ident.name)));
         debug_assert!(!attr.is_doc_comment());
 
-        let ast::Attribute { kind, id: _, style, span, tokens } = attr;
-        if let ast::AttrKind::Normal(item) = kind {
+        let ast::Attribute { kind, id: _, style, span } = attr;
+        if let ast::AttrKind::Normal(item, tokens) = kind {
             item.hash_stable(self, hasher);
             style.hash_stable(self, hasher);
             span.hash_stable(self, hasher);
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index bad43cd5350..f125a12147a 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -616,12 +616,7 @@ fn prepend_attrs(
         if attr.style == ast::AttrStyle::Inner {
             return None;
         }
-        builder.push(
-            attr.tokens
-                .as_ref()
-                .unwrap_or_else(|| panic!("Attribute {:?} is missing tokens!", attr))
-                .create_token_stream(),
-        );
+        builder.push(attr.tokens());
     }
     builder.push(tokens);
     Some(builder.build())
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 053b7e0b75f..3738fbaeac8 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -30,52 +30,44 @@ impl<'a> Parser<'a> {
         let mut just_parsed_doc_comment = false;
         loop {
             debug!("parse_outer_attributes: self.token={:?}", self.token);
-            let (attr, tokens) = if self.check(&token::Pound) {
-                self.collect_tokens(|this| {
-                    let inner_error_reason = if just_parsed_doc_comment {
-                        "an inner attribute is not permitted following an outer doc comment"
-                    } else if !attrs.is_empty() {
-                        "an inner attribute is not permitted following an outer attribute"
-                    } else {
-                        DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
-                    };
-                    let inner_parse_policy = InnerAttrPolicy::Forbidden {
-                        reason: inner_error_reason,
-                        saw_doc_comment: just_parsed_doc_comment,
-                        prev_attr_sp: attrs.last().map(|a| a.span),
-                    };
-                    let attr = this.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
-                    just_parsed_doc_comment = false;
-                    Ok(Some(attr))
-                })?
+            let attr = if self.check(&token::Pound) {
+                let inner_error_reason = if just_parsed_doc_comment {
+                    "an inner attribute is not permitted following an outer doc comment"
+                } else if !attrs.is_empty() {
+                    "an inner attribute is not permitted following an outer attribute"
+                } else {
+                    DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
+                };
+                let inner_parse_policy = InnerAttrPolicy::Forbidden {
+                    reason: inner_error_reason,
+                    saw_doc_comment: just_parsed_doc_comment,
+                    prev_attr_sp: attrs.last().map(|a| a.span),
+                };
+                just_parsed_doc_comment = false;
+                Some(self.parse_attribute(inner_parse_policy)?)
             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
-                self.collect_tokens(|this| {
-                    let attr =
-                        attr::mk_doc_comment(comment_kind, attr_style, data, this.token.span);
-                    if attr.style != ast::AttrStyle::Outer {
-                        this.sess
-                            .span_diagnostic
-                            .struct_span_err_with_code(
-                                this.token.span,
-                                "expected outer doc comment",
-                                error_code!(E0753),
-                            )
-                            .note(
-                                "inner doc comments like this (starting with \
-                                 `//!` or `/*!`) can only appear before items",
-                            )
-                            .emit();
-                    }
-                    this.bump();
-                    just_parsed_doc_comment = true;
-                    Ok(Some(attr))
-                })?
+                if attr_style != ast::AttrStyle::Outer {
+                    self.sess
+                        .span_diagnostic
+                        .struct_span_err_with_code(
+                            self.token.span,
+                            "expected outer doc comment",
+                            error_code!(E0753),
+                        )
+                        .note(
+                            "inner doc comments like this (starting with \
+                         `//!` or `/*!`) can only appear before items",
+                        )
+                        .emit();
+                }
+                self.bump();
+                just_parsed_doc_comment = true;
+                Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
             } else {
-                (None, None)
+                None
             };
 
-            if let Some(mut attr) = attr {
-                attr.tokens = tokens;
+            if let Some(attr) = attr {
                 attrs.push(attr);
             } else {
                 break;
@@ -85,49 +77,43 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches `attribute = # ! [ meta_item ]`.
-    ///
-    /// If `permit_inner` is `true`, then a leading `!` indicates an inner
-    /// attribute.
-    pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
-        debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token);
-        let inner_parse_policy =
-            if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN };
-        self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
-    }
-
-    /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy`
-    /// that prescribes how to handle inner attributes.
-    fn parse_attribute_with_inner_parse_policy(
+    /// `inner_parse_policy` prescribes how to handle inner attributes.
+    fn parse_attribute(
         &mut self,
         inner_parse_policy: InnerAttrPolicy<'_>,
     ) -> PResult<'a, ast::Attribute> {
         debug!(
-            "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
+            "parse_attribute: inner_parse_policy={:?} self.token={:?}",
             inner_parse_policy, self.token
         );
         let lo = self.token.span;
-        let (span, item, style) = if self.eat(&token::Pound) {
-            let style =
-                if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
-
-            self.expect(&token::OpenDelim(token::Bracket))?;
-            let item = self.parse_attr_item(false)?;
-            self.expect(&token::CloseDelim(token::Bracket))?;
-            let attr_sp = lo.to(self.prev_token.span);
-
-            // Emit error if inner attribute is encountered and forbidden.
-            if style == ast::AttrStyle::Inner {
-                self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
-            }
+        let ((item, style, span), tokens) = self.collect_tokens(|this| {
+            if this.eat(&token::Pound) {
+                let style = if this.eat(&token::Not) {
+                    ast::AttrStyle::Inner
+                } else {
+                    ast::AttrStyle::Outer
+                };
 
-            (attr_sp, item, style)
-        } else {
-            let token_str = pprust::token_to_string(&self.token);
-            let msg = &format!("expected `#`, found `{}`", token_str);
-            return Err(self.struct_span_err(self.token.span, msg));
-        };
+                this.expect(&token::OpenDelim(token::Bracket))?;
+                let item = this.parse_attr_item(false)?;
+                this.expect(&token::CloseDelim(token::Bracket))?;
+                let attr_sp = lo.to(this.prev_token.span);
 
-        Ok(attr::mk_attr_from_item(style, item, span))
+                // Emit error if inner attribute is encountered and forbidden.
+                if style == ast::AttrStyle::Inner {
+                    this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
+                }
+
+                Ok((item, style, attr_sp))
+            } else {
+                let token_str = pprust::token_to_string(&this.token);
+                let msg = &format!("expected `#`, found `{}`", token_str);
+                Err(this.struct_span_err(this.token.span, msg))
+            }
+        })?;
+
+        Ok(attr::mk_attr_from_item(item, tokens, style, span))
     }
 
     pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
@@ -196,30 +182,19 @@ impl<'a> Parser<'a> {
         let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
             // Only try to parse if it is an inner attribute (has `!`).
-            let (attr, tokens) =
-                if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
-                    self.collect_tokens(|this| {
-                        let attr = this.parse_attribute(true)?;
-                        assert_eq!(attr.style, ast::AttrStyle::Inner);
-                        Ok(Some(attr))
-                    })?
-                } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
-                    self.collect_tokens(|this| {
-                        // We need to get the position of this token before we bump.
-                        let attr =
-                            attr::mk_doc_comment(comment_kind, attr_style, data, this.token.span);
-                        if attr.style == ast::AttrStyle::Inner {
-                            this.bump();
-                            Ok(Some(attr))
-                        } else {
-                            Ok(None)
-                        }
-                    })?
+            let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
+                Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
+            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
+                if attr_style == ast::AttrStyle::Inner {
+                    self.bump();
+                    Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span))
                 } else {
-                    (None, None)
-                };
-            if let Some(mut attr) = attr {
-                attr.tokens = tokens;
+                    None
+                }
+            } else {
+                None
+            };
+            if let Some(attr) = attr {
                 attrs.push(attr);
             } else {
                 break;
diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout
index fb311a9045b..b19cfc4d5c3 100644
--- a/src/test/ui/ast-json/ast-json-noexpand-output.stdout
+++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout
@@ -1 +1 @@
-{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
+{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout
index d1e0f409948..65d6cd475cc 100644
--- a/src/test/ui/ast-json/ast-json-output.stdout
+++ b/src/test/ui/ast-json/ast-json-output.stdout
@@ -1 +1 @@
-{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null}]},"id":null,"style":"Outer","span":{"lo":0,"hi":0},"tokens":null}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0},"tokens":{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
+{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]}
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index a004abb58b8..55904a0ec0a 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -588,7 +588,7 @@ impl EarlyLintPass for EarlyAttributes {
 
 fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
     for attr in &item.attrs {
-        let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
+        let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind {
             attr
         } else {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
index 0e9feef3746..b68e33f101d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -509,7 +509,7 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
     l.style == r.style
         && match (&l.kind, &r.kind) {
             (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
-            (Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
+            (Normal(l, _), Normal(r, _)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
             _ => false,
         }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
index a3975683cb3..e6d41341a55 100644
--- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
@@ -57,7 +57,7 @@ pub fn get_attr<'a>(
     name: &'static str,
 ) -> impl Iterator<Item = &'a ast::Attribute> {
     attrs.iter().filter(move |attr| {
-        let attr = if let ast::AttrKind::Normal(ref attr) = attr.kind {
+        let attr = if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
             attr
         } else {
             return false;
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 85e7f055e79..0d43fd0392e 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1349,7 +1349,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 
 pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
     krate.item.attrs.iter().any(|attr| {
-        if let ast::AttrKind::Normal(ref attr) = attr.kind {
+        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
             attr.path == symbol::sym::no_std
         } else {
             false