diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs
index b1fa818d0a8cf..75938470b6fca 100644
--- a/src/librustc_ast_pretty/pprust.rs
+++ b/src/librustc_ast_pretty/pprust.rs
@@ -1269,6 +1269,7 @@ impl<'a> State<'a> {
                 self.print_where_clause(&generics.where_clause);
                 self.s.word(" ");
                 self.bopen();
+                self.print_inner_attributes(&item.attrs);
                 for trait_item in trait_items {
                     self.print_assoc_item(trait_item);
                 }
diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs
index 90692fe1ec9dd..0968e92c20314 100644
--- a/src/librustc_expand/expand.rs
+++ b/src/librustc_expand/expand.rs
@@ -867,7 +867,7 @@ pub fn parse_ast_fragment<'a>(
         AstFragmentKind::ForeignItems => {
             let mut items = SmallVec::new();
             while this.token != token::Eof {
-                items.push(this.parse_foreign_item()?);
+                items.push(this.parse_foreign_item(&mut false)?);
             }
             AstFragment::ForeignItems(items)
         }
diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs
index 8dec64c579e88..1c479295af3ed 100644
--- a/src/librustc_parse/config.rs
+++ b/src/librustc_parse/config.rs
@@ -562,14 +562,9 @@ fn is_cfg(attr: &Attribute) -> bool {
 
 /// Process the potential `cfg` attributes on a module.
 /// Also determine if the module should be included in this configuration.
-pub fn process_configure_mod(
-    sess: &ParseSess,
-    cfg_mods: bool,
-    attrs: &[Attribute],
-) -> (bool, Vec<Attribute>) {
+pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec<Attribute>) -> bool {
     // Don't perform gated feature checking.
     let mut strip_unconfigured = StripUnconfigured { sess, features: None };
-    let mut attrs = attrs.to_owned();
-    strip_unconfigured.process_cfg_attrs(&mut attrs);
-    (!cfg_mods || strip_unconfigured.in_cfg(&attrs), attrs)
+    strip_unconfigured.process_cfg_attrs(attrs);
+    !cfg_mods || strip_unconfigured.in_cfg(&attrs)
 }
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index ccd55c5c08a70..500aaaf43b92a 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -8,10 +8,9 @@ use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult, StashKey};
 use rustc_span::source_map::{self, Span};
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::BytePos;
-use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
+use syntax::ast::{self, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
 use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind};
-use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, StrLit, Unsafe};
+use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, Unsafe};
 use syntax::ast::{BindingMode, Block, FnDecl, FnSig, Mac, MacArgs, MacDelimiter, Param, SelfKind};
 use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
 use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
@@ -22,7 +21,7 @@ use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use log::debug;
 use std::mem;
 
-pub(super) type ItemInfo = (Ident, ItemKind, Option<Vec<Attribute>>);
+pub(super) type ItemInfo = (Ident, ItemKind);
 
 impl<'a> Parser<'a> {
     pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> {
@@ -83,71 +82,67 @@ impl<'a> Parser<'a> {
         });
 
         let lo = self.token.span;
-
         let vis = self.parse_visibility(FollowedByType::No)?;
 
-        if self.eat_keyword(kw::Use) {
-            // USE ITEM
-            let item_ = ItemKind::Use(P(self.parse_use_tree()?));
-            self.expect_semi()?;
+        if let Some((ident, kind)) = self.parse_item_kind(&mut attrs, macros_allowed, lo, &vis)? {
+            return Ok(Some(P(self.mk_item(lo, ident, kind, vis, attrs))));
+        }
 
-            let span = lo.to(self.prev_span);
-            let item = self.mk_item(span, Ident::invalid(), item_, vis, attrs);
-            return Ok(Some(item));
+        // FAILURE TO PARSE ITEM
+        if let VisibilityKind::Inherited = vis.node {
+        } else {
+            let vs = pprust::vis_to_string(&vis);
+            let vs = vs.trim_end();
+            self.struct_span_err(vis.span, &format!("unmatched visibility `{}`", vs))
+                .span_label(vis.span, "the unmatched visibility")
+                .help(&format!("you likely meant to define an item, e.g., `{} fn foo() {{}}`", vs))
+                .emit();
         }
 
-        if self.check_fn_front_matter() {
-            // FUNCTION ITEM
-            let (ident, sig, generics, body) = self.parse_fn(&mut false, &mut attrs, |_| true)?;
-            let kind = ItemKind::Fn(sig, generics, body);
-            return self.mk_item_with_info(attrs, lo, vis, (ident, kind, None));
+        if !attributes_allowed {
+            self.recover_attrs_no_item(&attrs)?;
         }
+        Ok(None)
+    }
 
-        if self.eat_keyword(kw::Extern) {
+    /// Parses one of the items allowed by the flags.
+    fn parse_item_kind(
+        &mut self,
+        attrs: &mut Vec<Attribute>,
+        macros_allowed: bool,
+        lo: Span,
+        vis: &Visibility,
+    ) -> PResult<'a, Option<ItemInfo>> {
+        let info = if self.eat_keyword(kw::Use) {
+            // USE ITEM
+            let tree = self.parse_use_tree()?;
+            self.expect_semi()?;
+            (Ident::invalid(), ItemKind::Use(P(tree)))
+        } else if self.check_fn_front_matter() {
+            // FUNCTION ITEM
+            let (ident, sig, generics, body) = self.parse_fn(&mut false, attrs, |_| true)?;
+            (ident, ItemKind::Fn(sig, generics, body))
+        } else if self.eat_keyword(kw::Extern) {
             if self.eat_keyword(kw::Crate) {
                 // EXTERN CRATE
-                return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
+                self.parse_item_extern_crate()?
+            } else {
+                // EXTERN BLOCK
+                self.parse_item_foreign_mod(attrs)?
             }
-            // EXTERN BLOCK
-            let abi = self.parse_abi();
-            return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
-        }
-
-        if self.is_static_global() {
+        } else if self.is_static_global() {
             // STATIC ITEM
-            self.bump();
+            self.bump(); // `static`
             let m = self.parse_mutability();
-            let info = self.parse_item_const(Some(m))?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if let Const::Yes(const_span) = self.parse_constness() {
+            self.parse_item_const(Some(m))?
+        } else if let Const::Yes(const_span) = self.parse_constness() {
             // CONST ITEM
-            if self.eat_keyword(kw::Mut) {
-                let prev_span = self.prev_span;
-                self.struct_span_err(prev_span, "const globals cannot be mutable")
-                    .span_label(prev_span, "cannot be mutable")
-                    .span_suggestion(
-                        const_span,
-                        "you might want to declare a static instead",
-                        "static".to_owned(),
-                        Applicability::MaybeIncorrect,
-                    )
-                    .emit();
-            }
-
-            let info = self.parse_item_const(None)?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) {
-            // UNSAFE TRAIT ITEM
-            let unsafety = self.parse_unsafety();
-            let info = self.parse_item_trait(lo, unsafety)?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.check_keyword(kw::Impl)
+            self.recover_const_mut(const_span);
+            self.parse_item_const(None)?
+        } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
+            // TRAIT ITEM
+            self.parse_item_trait(attrs, lo)?
+        } else if self.check_keyword(kw::Impl)
             || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl])
             || self.check_keyword(kw::Default) && self.is_keyword_ahead(1, &[kw::Impl, kw::Unsafe])
         {
@@ -155,273 +150,189 @@ impl<'a> Parser<'a> {
             let defaultness = self.parse_defaultness();
             let unsafety = self.parse_unsafety();
             self.expect_keyword(kw::Impl)?;
-            let info = self.parse_item_impl(unsafety, defaultness)?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.eat_keyword(kw::Mod) {
+            self.parse_item_impl(attrs, unsafety, defaultness)?
+        } else if self.eat_keyword(kw::Mod) {
             // MODULE ITEM
-            let info = self.parse_item_mod(&attrs[..])?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.eat_keyword(kw::Type) {
+            self.parse_item_mod(attrs)?
+        } else if self.eat_keyword(kw::Type) {
             // TYPE ITEM
             let (ident, ty, generics) = self.parse_type_alias()?;
-            let kind = ItemKind::TyAlias(ty, generics);
-            return self.mk_item_with_info(attrs, lo, vis, (ident, kind, None));
-        }
-
-        if self.eat_keyword(kw::Enum) {
+            (ident, ItemKind::TyAlias(ty, generics))
+        } else if self.eat_keyword(kw::Enum) {
             // ENUM ITEM
-            let info = self.parse_item_enum()?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.check_keyword(kw::Trait)
-            || (self.check_keyword(kw::Auto) && self.is_keyword_ahead(1, &[kw::Trait]))
-        {
-            // TRAIT ITEM
-            let info = self.parse_item_trait(lo, Unsafe::No)?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.eat_keyword(kw::Struct) {
+            self.parse_item_enum()?
+        } else if self.eat_keyword(kw::Struct) {
             // STRUCT ITEM
-            let info = self.parse_item_struct()?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
-
-        if self.is_union_item() {
+            self.parse_item_struct()?
+        } else if self.is_kw_followed_by_ident(kw::Union) {
             // UNION ITEM
-            self.bump();
-            let info = self.parse_item_union()?;
-            return self.mk_item_with_info(attrs, lo, vis, info);
-        }
+            self.bump(); // `union`
+            self.parse_item_union()?
+        } else if self.eat_keyword(kw::Macro) {
+            // MACROS 2.0 ITEM
+            self.parse_item_decl_macro(lo)?
+        } else if self.is_macro_rules_item() {
+            // MACRO_RULES ITEM
+            self.parse_item_macro_rules(vis)?
+        } else if vis.node.is_pub() && self.isnt_macro_invocation() {
+            self.recover_missing_kw_before_item()?;
+            return Ok(None);
+        } else if macros_allowed && self.token.is_path_start() {
+            // MACRO INVOCATION ITEM
+            (Ident::invalid(), ItemKind::Mac(self.parse_item_macro(vis)?))
+        } else {
+            return Ok(None);
+        };
+        Ok(Some(info))
+    }
 
-        if let Some(macro_def) = self.eat_macro_def(&attrs, &vis, lo)? {
-            return Ok(Some(macro_def));
-        }
+    /// When parsing a statement, would the start of a path be an item?
+    pub(super) fn is_path_start_item(&mut self) -> bool {
+        self.is_crate_vis() // no: `crate::b`, yes: `crate $item`
+        || self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
+        || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }`
+        || self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
+        || self.is_macro_rules_item() // no: `macro_rules::b`, yes: `macro_rules! mac`
+    }
 
-        // Verify whether we have encountered a struct or method definition where the user forgot to
-        // add the `struct` or `fn` keyword after writing `pub`: `pub S {}`
-        if vis.node.is_pub() && self.check_ident() && self.look_ahead(1, |t| *t != token::Not) {
-            // Space between `pub` keyword and the identifier
-            //
-            //     pub   S {}
-            //        ^^^ `sp` points here
-            let sp = self.prev_span.between(self.token.span);
-            let full_sp = self.prev_span.to(self.token.span);
-            let ident_sp = self.token.span;
-            if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) {
-                // possible public struct definition where `struct` was forgotten
-                let ident = self.parse_ident().unwrap();
-                let msg = format!("add `struct` here to parse `{}` as a public struct", ident);
-                let mut err = self.struct_span_err(sp, "missing `struct` for struct definition");
+    /// Are we sure this could not possibly be a macro invocation?
+    fn isnt_macro_invocation(&mut self) -> bool {
+        self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::ModSep)
+    }
+
+    /// Recover on encountering a struct or method definition where the user
+    /// forgot to add the `struct` or `fn` keyword after writing `pub`: `pub S {}`.
+    fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
+        // Space between `pub` keyword and the identifier
+        //
+        //     pub   S {}
+        //        ^^^ `sp` points here
+        let sp = self.prev_span.between(self.token.span);
+        let full_sp = self.prev_span.to(self.token.span);
+        let ident_sp = self.token.span;
+        if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) {
+            // possible public struct definition where `struct` was forgotten
+            let ident = self.parse_ident().unwrap();
+            let msg = format!("add `struct` here to parse `{}` as a public struct", ident);
+            let mut err = self.struct_span_err(sp, "missing `struct` for struct definition");
+            err.span_suggestion_short(
+                sp,
+                &msg,
+                " struct ".into(),
+                Applicability::MaybeIncorrect, // speculative
+            );
+            return Err(err);
+        } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
+            let ident = self.parse_ident().unwrap();
+            self.bump(); // `(`
+            let kw_name = self.recover_first_param();
+            self.consume_block(token::Paren, ConsumeClosingDelim::Yes);
+            let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
+                self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
+                self.bump(); // `{`
+                ("fn", kw_name, false)
+            } else if self.check(&token::OpenDelim(token::Brace)) {
+                self.bump(); // `{`
+                ("fn", kw_name, false)
+            } else if self.check(&token::Colon) {
+                let kw = "struct";
+                (kw, kw, false)
+            } else {
+                ("fn` or `struct", "function or struct", true)
+            };
+
+            let msg = format!("missing `{}` for {} definition", kw, kw_name);
+            let mut err = self.struct_span_err(sp, &msg);
+            if !ambiguous {
+                self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                let suggestion =
+                    format!("add `{}` here to parse `{}` as a public {}", kw, ident, kw_name);
                 err.span_suggestion_short(
                     sp,
-                    &msg,
-                    " struct ".into(),
-                    Applicability::MaybeIncorrect, // speculative
+                    &suggestion,
+                    format!(" {} ", kw),
+                    Applicability::MachineApplicable,
                 );
-                return Err(err);
-            } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
-                let ident = self.parse_ident().unwrap();
-                self.bump(); // `(`
-                let kw_name = self.recover_first_param();
-                self.consume_block(token::Paren, ConsumeClosingDelim::Yes);
-                let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
-                    self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
-                    self.bump(); // `{`
-                    ("fn", kw_name, false)
-                } else if self.check(&token::OpenDelim(token::Brace)) {
-                    self.bump(); // `{`
-                    ("fn", kw_name, false)
-                } else if self.check(&token::Colon) {
-                    let kw = "struct";
-                    (kw, kw, false)
-                } else {
-                    ("fn` or `struct", "function or struct", true)
-                };
-
-                let msg = format!("missing `{}` for {} definition", kw, kw_name);
-                let mut err = self.struct_span_err(sp, &msg);
-                if !ambiguous {
-                    self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
-                    let suggestion =
-                        format!("add `{}` here to parse `{}` as a public {}", kw, ident, kw_name);
-                    err.span_suggestion_short(
-                        sp,
-                        &suggestion,
-                        format!(" {} ", kw),
-                        Applicability::MachineApplicable,
+            } else {
+                if let Ok(snippet) = self.span_to_snippet(ident_sp) {
+                    err.span_suggestion(
+                        full_sp,
+                        "if you meant to call a macro, try",
+                        format!("{}!", snippet),
+                        // this is the `ambiguous` conditional branch
+                        Applicability::MaybeIncorrect,
                     );
                 } else {
-                    if let Ok(snippet) = self.span_to_snippet(ident_sp) {
-                        err.span_suggestion(
-                            full_sp,
-                            "if you meant to call a macro, try",
-                            format!("{}!", snippet),
-                            // this is the `ambiguous` conditional branch
-                            Applicability::MaybeIncorrect,
-                        );
-                    } else {
-                        err.help(
-                            "if you meant to call a macro, remove the `pub` \
+                    err.help(
+                        "if you meant to call a macro, remove the `pub` \
                                   and add a trailing `!` after the identifier",
-                        );
-                    }
-                }
-                return Err(err);
-            } else if self.look_ahead(1, |t| *t == token::Lt) {
-                let ident = self.parse_ident().unwrap();
-                self.eat_to_tokens(&[&token::Gt]);
-                self.bump(); // `>`
-                let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
-                    ("fn", self.recover_first_param(), false)
-                } else if self.check(&token::OpenDelim(token::Brace)) {
-                    ("struct", "struct", false)
-                } else {
-                    ("fn` or `struct", "function or struct", true)
-                };
-                let msg = format!("missing `{}` for {} definition", kw, kw_name);
-                let mut err = self.struct_span_err(sp, &msg);
-                if !ambiguous {
-                    err.span_suggestion_short(
-                        sp,
-                        &format!("add `{}` here to parse `{}` as a public {}", kw, ident, kw_name),
-                        format!(" {} ", kw),
-                        Applicability::MachineApplicable,
                     );
                 }
-                return Err(err);
             }
-        }
-        self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, vis)
-    }
-
-    pub(super) fn mk_item_with_info(
-        &self,
-        attrs: Vec<Attribute>,
-        lo: Span,
-        vis: Visibility,
-        info: ItemInfo,
-    ) -> PResult<'a, Option<P<Item>>> {
-        let (ident, item, extra_attrs) = info;
-        let span = lo.to(self.prev_span);
-        let attrs = Self::maybe_append(attrs, extra_attrs);
-        Ok(Some(self.mk_item(span, ident, item, vis, attrs)))
-    }
-
-    fn maybe_append<T>(mut lhs: Vec<T>, mut rhs: Option<Vec<T>>) -> Vec<T> {
-        if let Some(ref mut rhs) = rhs {
-            lhs.append(rhs);
-        }
-        lhs
-    }
-
-    /// This is the fall-through for parsing items.
-    fn parse_macro_use_or_failure(
-        &mut self,
-        attrs: Vec<Attribute>,
-        macros_allowed: bool,
-        attributes_allowed: bool,
-        lo: Span,
-        visibility: Visibility,
-    ) -> PResult<'a, Option<P<Item>>> {
-        if macros_allowed
-            && self.token.is_path_start()
-            && !(self.is_async_fn() && self.token.span.rust_2015())
-        {
-            // MACRO INVOCATION ITEM
-
-            let prev_span = self.prev_span;
-            self.complain_if_pub_macro(&visibility.node, prev_span);
-
-            // Item macro
-            let path = self.parse_path(PathStyle::Mod)?;
-            self.expect(&token::Not)?;
-            let args = self.parse_mac_args()?;
-            if args.need_semicolon() && !self.eat(&token::Semi) {
-                self.report_invalid_macro_expansion_item();
+            return Err(err);
+        } else if self.look_ahead(1, |t| *t == token::Lt) {
+            let ident = self.parse_ident().unwrap();
+            self.eat_to_tokens(&[&token::Gt]);
+            self.bump(); // `>`
+            let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
+                ("fn", self.recover_first_param(), false)
+            } else if self.check(&token::OpenDelim(token::Brace)) {
+                ("struct", "struct", false)
+            } else {
+                ("fn` or `struct", "function or struct", true)
+            };
+            let msg = format!("missing `{}` for {} definition", kw, kw_name);
+            let mut err = self.struct_span_err(sp, &msg);
+            if !ambiguous {
+                err.span_suggestion_short(
+                    sp,
+                    &format!("add `{}` here to parse `{}` as a public {}", kw, ident, kw_name),
+                    format!(" {} ", kw),
+                    Applicability::MachineApplicable,
+                );
             }
-
-            let hi = self.prev_span;
-            let mac = Mac { path, args, prior_type_ascription: self.last_type_ascription };
-            let item =
-                self.mk_item(lo.to(hi), Ident::invalid(), ItemKind::Mac(mac), visibility, attrs);
-            return Ok(Some(item));
-        }
-
-        // FAILURE TO PARSE ITEM
-        match visibility.node {
-            VisibilityKind::Inherited => {}
-            _ => return Err(self.struct_span_err(self.prev_span, "unmatched visibility `pub`")),
+            return Err(err);
+        } else {
+            Ok(())
         }
+    }
 
-        if !attributes_allowed && !attrs.is_empty() {
-            self.expected_item_err(&attrs)?;
-        }
-        Ok(None)
+    /// Parses an item macro, e.g., `item!();`.
+    fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, Mac> {
+        let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
+        self.expect(&token::Not)?; // `!`
+        let args = self.parse_mac_args()?; // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
+        self.eat_semi_for_macro_if_needed(&args);
+        self.complain_if_pub_macro(vis, false);
+        Ok(Mac { path, args, prior_type_ascription: self.last_type_ascription })
     }
 
-    /// Emits an expected-item-after-attributes error.
-    fn expected_item_err(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
-        let message = match attrs.last() {
-            Some(&Attribute { kind: AttrKind::DocComment(_), .. }) => {
-                "expected item after doc comment"
-            }
-            _ => "expected item after attributes",
+    /// Recover if we parsed attributes and expected an item but there was none.
+    fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
+        let (start, end) = match attrs {
+            [] => return Ok(()),
+            [x0] => (x0, x0),
+            [x0, .., xn] => (x0, xn),
         };
-
-        let mut err = self.struct_span_err(self.prev_span, message);
-        if attrs.last().unwrap().is_doc_comment() {
-            err.span_label(self.prev_span, "this doc comment doesn't document anything");
+        let msg = if end.is_doc_comment() {
+            "expected item after doc comment"
+        } else {
+            "expected item after attributes"
+        };
+        let mut err = self.struct_span_err(end.span, msg);
+        if end.is_doc_comment() {
+            err.span_label(end.span, "this doc comment doesn't document anything");
+        }
+        if let [.., penultimate, _] = attrs {
+            err.span_label(start.span.to(penultimate.span), "other attributes here");
         }
         Err(err)
     }
 
-    pub(super) fn is_async_fn(&self) -> bool {
+    fn is_async_fn(&self) -> bool {
         self.token.is_keyword(kw::Async) && self.is_keyword_ahead(1, &[kw::Fn])
     }
 
-    /// Parses a macro invocation inside a `trait`, `impl` or `extern` block.
-    fn parse_assoc_macro_invoc(
-        &mut self,
-        item_kind: &str,
-        vis: Option<&Visibility>,
-        at_end: &mut bool,
-    ) -> PResult<'a, Option<Mac>> {
-        if self.token.is_path_start() && !(self.is_async_fn() && self.token.span.rust_2015()) {
-            let prev_span = self.prev_span;
-            let path = self.parse_path(PathStyle::Mod)?;
-
-            if path.segments.len() == 1 {
-                if !self.eat(&token::Not) {
-                    return Err(self.missing_assoc_item_kind_err(item_kind, prev_span));
-                }
-            } else {
-                self.expect(&token::Not)?;
-            }
-
-            if let Some(vis) = vis {
-                self.complain_if_pub_macro(&vis.node, prev_span);
-            }
-
-            *at_end = true;
-
-            // eat a matched-delimiter token tree:
-            let args = self.parse_mac_args()?;
-            if args.need_semicolon() {
-                self.expect_semi()?;
-            }
-
-            Ok(Some(Mac { path, args, prior_type_ascription: self.last_type_ascription }))
-        } else {
-            Ok(None)
-        }
-    }
-
     fn missing_assoc_item_kind_err(
         &self,
         item_type: &str,
@@ -442,7 +353,7 @@ impl<'a> Parser<'a> {
         //   |        ^ missing `fn`, `type`, or `const`
         //     pub  path(
         //        ^^ `sp` below will point to this
-        let sp = prev_span.between(self.prev_span);
+        let sp = prev_span.between(self.token.span);
         let mut err = self
             .struct_span_err(sp, &format!("{} for {}-item declaration", expected_kinds, item_type));
         err.span_label(sp, expected_kinds);
@@ -451,16 +362,21 @@ impl<'a> Parser<'a> {
 
     /// Parses an implementation item, `impl` keyword is already parsed.
     ///
-    ///    impl<'a, T> TYPE { /* impl items */ }
-    ///    impl<'a, T> TRAIT for TYPE { /* impl items */ }
-    ///    impl<'a, T> !TRAIT for TYPE { /* impl items */ }
-    ///    impl<'a, T> const TRAIT for TYPE { /* impl items */ }
+    /// ```
+    /// impl<'a, T> TYPE { /* impl items */ }
+    /// impl<'a, T> TRAIT for TYPE { /* impl items */ }
+    /// impl<'a, T> !TRAIT for TYPE { /* impl items */ }
+    /// impl<'a, T> const TRAIT for TYPE { /* impl items */ }
+    /// ```
     ///
     /// We actually parse slightly more relaxed grammar for better error reporting and recovery.
-    ///   `impl` GENERICS `const`? `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
-    ///   `impl` GENERICS `const`? `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
+    /// ```
+    /// "impl" GENERICS "const"? "!"? TYPE "for"? (TYPE | "..") ("where" PREDICATES)? "{" BODY "}"
+    /// "impl" GENERICS "const"? "!"? TYPE ("where" PREDICATES)? "{" BODY "}"
+    /// ```
     fn parse_item_impl(
         &mut self,
+        attrs: &mut Vec<Attribute>,
         unsafety: Unsafe,
         defaultness: Defaultness,
     ) -> PResult<'a, ItemInfo> {
@@ -515,7 +431,7 @@ impl<'a> Parser<'a> {
 
         generics.where_clause = self.parse_where_clause()?;
 
-        let (impl_items, attrs) = self.parse_impl_body()?;
+        let impl_items = self.parse_item_list(attrs, |p, at_end| p.parse_impl_item(at_end))?;
 
         let item_kind = match ty_second {
             Some(ty_second) => {
@@ -568,18 +484,25 @@ impl<'a> Parser<'a> {
             }
         };
 
-        Ok((Ident::invalid(), item_kind, Some(attrs)))
+        Ok((Ident::invalid(), item_kind))
     }
 
-    fn parse_impl_body(&mut self) -> PResult<'a, (Vec<P<AssocItem>>, Vec<Attribute>)> {
+    fn parse_item_list<T>(
+        &mut self,
+        attrs: &mut Vec<Attribute>,
+        mut parse_item: impl FnMut(&mut Parser<'a>, &mut bool) -> PResult<'a, T>,
+    ) -> PResult<'a, Vec<T>> {
         self.expect(&token::OpenDelim(token::Brace))?;
-        let attrs = self.parse_inner_attributes()?;
+        attrs.append(&mut self.parse_inner_attributes()?);
 
-        let mut impl_items = Vec::new();
+        let mut items = Vec::new();
         while !self.eat(&token::CloseDelim(token::Brace)) {
+            if self.recover_doc_comment_before_brace() {
+                continue;
+            }
             let mut at_end = false;
-            match self.parse_impl_item(&mut at_end) {
-                Ok(impl_item) => impl_items.push(impl_item),
+            match parse_item(self, &mut at_end) {
+                Ok(item) => items.push(item),
                 Err(mut err) => {
                     err.emit();
                     if !at_end {
@@ -589,7 +512,30 @@ impl<'a> Parser<'a> {
                 }
             }
         }
-        Ok((impl_items, attrs))
+        Ok(items)
+    }
+
+    /// Recover on a doc comment before `}`.
+    fn recover_doc_comment_before_brace(&mut self) -> bool {
+        if let token::DocComment(_) = self.token.kind {
+            if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
+                struct_span_err!(
+                    self.diagnostic(),
+                    self.token.span,
+                    E0584,
+                    "found a documentation comment that doesn't document anything",
+                )
+                .span_label(self.token.span, "this doc comment doesn't document anything")
+                .help(
+                    "doc comments must come before what they document, maybe a \
+                    comment was intended with `//`?",
+                )
+                .emit();
+                self.bump();
+                return true;
+            }
+        }
+        false
     }
 
     /// Parses defaultness (i.e., `default` or nothing).
@@ -617,8 +563,17 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parses `auto? trait Foo { ... }` or `trait Foo = Bar;`.
-    fn parse_item_trait(&mut self, lo: Span, unsafety: Unsafe) -> PResult<'a, ItemInfo> {
+    /// Is this an `(unsafe auto? | auto) trait` item?
+    fn check_auto_or_unsafe_trait_item(&mut self) -> bool {
+        // auto trait
+        self.check_keyword(kw::Auto) && self.is_keyword_ahead(1, &[kw::Trait])
+            // unsafe auto trait
+            || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
+    }
+
+    /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
+    fn parse_item_trait(&mut self, attrs: &mut Vec<Attribute>, lo: Span) -> PResult<'a, ItemInfo> {
+        let unsafety = self.parse_unsafety();
         // Parse optional `auto` prefix.
         let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No };
 
@@ -656,43 +611,12 @@ impl<'a> Parser<'a> {
 
             self.sess.gated_spans.gate(sym::trait_alias, whole_span);
 
-            Ok((ident, ItemKind::TraitAlias(tps, bounds), None))
+            Ok((ident, ItemKind::TraitAlias(tps, bounds)))
         } else {
             // It's a normal trait.
             tps.where_clause = self.parse_where_clause()?;
-            self.expect(&token::OpenDelim(token::Brace))?;
-            let mut trait_items = vec![];
-            while !self.eat(&token::CloseDelim(token::Brace)) {
-                if let token::DocComment(_) = self.token.kind {
-                    if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
-                        struct_span_err!(
-                            self.diagnostic(),
-                            self.token.span,
-                            E0584,
-                            "found a documentation comment that doesn't document anything",
-                        )
-                        .help(
-                            "doc comments must come before what they document, maybe a \
-                            comment was intended with `//`?",
-                        )
-                        .emit();
-                        self.bump();
-                        continue;
-                    }
-                }
-                let mut at_end = false;
-                match self.parse_trait_item(&mut at_end) {
-                    Ok(item) => trait_items.push(item),
-                    Err(mut e) => {
-                        e.emit();
-                        if !at_end {
-                            self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
-                            break;
-                        }
-                    }
-                }
-            }
-            Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, trait_items), None))
+            let items = self.parse_item_list(attrs, |p, at_end| p.parse_trait_item(at_end))?;
+            Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, items)))
         }
     }
 
@@ -740,28 +664,28 @@ impl<'a> Parser<'a> {
         let lo = self.token.span;
         let vis = self.parse_visibility(FollowedByType::No)?;
         let defaultness = self.parse_defaultness();
-        let (name, kind, generics) = if self.eat_keyword(kw::Type) {
+
+        let (ident, kind, generics) = if self.eat_keyword(kw::Type) {
             self.parse_assoc_ty()?
         } else if self.check_fn_front_matter() {
             let (ident, sig, generics, body) = self.parse_fn(at_end, &mut attrs, req_name)?;
             (ident, AssocItemKind::Fn(sig, body), generics)
-        } else if let Some(mac) = self.parse_assoc_macro_invoc("associated", Some(&vis), at_end)? {
+        } else if self.check_keyword(kw::Const) {
+            self.parse_assoc_const()?
+        } else if self.isnt_macro_invocation() {
+            return Err(self.missing_assoc_item_kind_err("associated", self.prev_span));
+        } else if self.token.is_path_start() {
+            let mac = self.parse_item_macro(&vis)?;
+            *at_end = true;
             (Ident::invalid(), AssocItemKind::Macro(mac), Generics::default())
         } else {
-            self.parse_assoc_const()?
+            self.recover_attrs_no_item(&attrs)?;
+            self.unexpected()?
         };
 
-        Ok(AssocItem {
-            id: DUMMY_NODE_ID,
-            span: lo.to(self.prev_span),
-            ident: name,
-            attrs,
-            vis,
-            defaultness,
-            generics,
-            kind,
-            tokens: None,
-        })
+        let span = lo.to(self.prev_span);
+        let id = DUMMY_NODE_ID;
+        Ok(AssocItem { id, span, ident, attrs, vis, defaultness, generics, kind, tokens: None })
     }
 
     /// This parses the grammar:
@@ -860,7 +784,7 @@ impl<'a> Parser<'a> {
 
     fn parse_ident_or_underscore(&mut self) -> PResult<'a, ast::Ident> {
         match self.token.kind {
-            token::Ident(name, false) if name == kw::Underscore => {
+            token::Ident(name @ kw::Underscore, false) => {
                 let span = self.token.span;
                 self.bump();
                 Ok(Ident::new(name, span))
@@ -877,12 +801,7 @@ impl<'a> Parser<'a> {
     /// extern crate foo;
     /// extern crate bar as foo;
     /// ```
-    fn parse_item_extern_crate(
-        &mut self,
-        lo: Span,
-        visibility: Visibility,
-        attrs: Vec<Attribute>,
-    ) -> PResult<'a, P<Item>> {
+    fn parse_item_extern_crate(&mut self) -> PResult<'a, ItemInfo> {
         // Accept `extern crate name-like-this` for better diagnostics
         let orig_name = self.parse_crate_name_with_dashes()?;
         let (item_name, orig_name) = if let Some(rename) = self.parse_rename()? {
@@ -891,9 +810,7 @@ impl<'a> Parser<'a> {
             (orig_name, None)
         };
         self.expect_semi()?;
-
-        let span = lo.to(self.prev_span);
-        Ok(self.mk_item(span, item_name, ItemKind::ExternCrate(orig_name), visibility, attrs))
+        Ok((item_name, ItemKind::ExternCrate(orig_name)))
     }
 
     fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> {
@@ -936,8 +853,7 @@ impl<'a> Parser<'a> {
 
     /// Parses `extern` for foreign ABIs modules.
     ///
-    /// `extern` is expected to have been
-    /// consumed before calling this method.
+    /// `extern` is expected to have been consumed before calling this method.
     ///
     /// # Examples
     ///
@@ -945,57 +861,32 @@ impl<'a> Parser<'a> {
     /// extern "C" {}
     /// extern {}
     /// ```
-    fn parse_item_foreign_mod(
-        &mut self,
-        lo: Span,
-        abi: Option<StrLit>,
-        visibility: Visibility,
-        mut attrs: Vec<Attribute>,
-    ) -> PResult<'a, P<Item>> {
-        self.expect(&token::OpenDelim(token::Brace))?;
-
-        attrs.extend(self.parse_inner_attributes()?);
-
-        let mut foreign_items = vec![];
-        while !self.eat(&token::CloseDelim(token::Brace)) {
-            foreign_items.push(self.parse_foreign_item()?);
-        }
-
-        let prev_span = self.prev_span;
-        let m = ast::ForeignMod { abi, items: foreign_items };
-        let invalid = Ident::invalid();
-        Ok(self.mk_item(lo.to(prev_span), invalid, ItemKind::ForeignMod(m), visibility, attrs))
+    fn parse_item_foreign_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+        let abi = self.parse_abi(); // ABI?
+        let items = self.parse_item_list(attrs, |p, at_end| p.parse_foreign_item(at_end))?;
+        let module = ast::ForeignMod { abi, items };
+        Ok((Ident::invalid(), ItemKind::ForeignMod(module)))
     }
 
-    /// Parses a foreign item.
-    pub fn parse_foreign_item(&mut self) -> PResult<'a, P<ForeignItem>> {
+    /// Parses a foreign item (one in an `extern { ... }` block).
+    pub fn parse_foreign_item(&mut self, at_end: &mut bool) -> PResult<'a, P<ForeignItem>> {
         maybe_whole!(self, NtForeignItem, |ni| ni);
 
         let mut attrs = self.parse_outer_attributes()?;
         let lo = self.token.span;
         let vis = self.parse_visibility(FollowedByType::No)?;
 
-        if self.check_keyword(kw::Type) {
+        let (ident, kind) = if self.check_keyword(kw::Type) {
             // FOREIGN TYPE ITEM
-            self.parse_item_foreign_type(vis, lo, attrs)
+            self.parse_item_foreign_type()?
         } else if self.check_fn_front_matter() {
             // FOREIGN FUNCTION ITEM
-            let (ident, sig, generics, body) = self.parse_fn(&mut false, &mut attrs, |_| true)?;
-            let kind = ForeignItemKind::Fn(sig, generics, body);
-            let span = lo.to(self.prev_span);
-            Ok(P(ast::ForeignItem {
-                ident,
-                attrs,
-                kind,
-                id: DUMMY_NODE_ID,
-                span,
-                vis,
-                tokens: None,
-            }))
+            let (ident, sig, generics, body) = self.parse_fn(at_end, &mut attrs, |_| true)?;
+            (ident, ForeignItemKind::Fn(sig, generics, body))
         } else if self.is_static_global() {
             // FOREIGN STATIC ITEM
             self.bump(); // `static`
-            self.parse_item_foreign_static(vis, lo, attrs)
+            self.parse_item_foreign_static()?
         } else if self.token.is_keyword(kw::Const) {
             // Treat `const` as `static` for error recovery, but don't add it to expected tokens.
             self.bump(); // `const`
@@ -1007,66 +898,37 @@ impl<'a> Parser<'a> {
                     Applicability::MachineApplicable,
                 )
                 .emit();
-            self.parse_item_foreign_static(vis, lo, attrs)
-        } else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), &mut false)? {
-            let kind = ForeignItemKind::Macro(mac);
-            let span = lo.to(self.prev_span);
-            let ident = Ident::invalid();
-            Ok(P(ForeignItem { ident, span, id: DUMMY_NODE_ID, attrs, vis, kind, tokens: None }))
+            self.parse_item_foreign_static()?
+        } else if self.isnt_macro_invocation() {
+            return Err(self.missing_assoc_item_kind_err("extern", self.prev_span));
+        } else if self.token.is_path_start() {
+            let mac = self.parse_item_macro(&vis)?;
+            *at_end = true;
+            (Ident::invalid(), ForeignItemKind::Macro(mac))
         } else {
-            if !attrs.is_empty() {
-                self.expected_item_err(&attrs)?;
-            }
-            self.unexpected()
-        }
+            self.recover_attrs_no_item(&attrs)?;
+            self.unexpected()?
+        };
+        Ok(P(self.mk_item(lo, ident, kind, vis, attrs)))
     }
 
     /// Parses a static item from a foreign module.
     /// Assumes that the `static` keyword is already parsed.
-    fn parse_item_foreign_static(
-        &mut self,
-        vis: ast::Visibility,
-        lo: Span,
-        attrs: Vec<Attribute>,
-    ) -> PResult<'a, P<ForeignItem>> {
+    fn parse_item_foreign_static(&mut self) -> PResult<'a, (Ident, ForeignItemKind)> {
         let mutbl = self.parse_mutability();
         let ident = self.parse_ident()?;
         self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
-        let hi = self.token.span;
         self.expect_semi()?;
-        Ok(P(ForeignItem {
-            ident,
-            attrs,
-            kind: ForeignItemKind::Static(ty, mutbl),
-            id: DUMMY_NODE_ID,
-            span: lo.to(hi),
-            vis,
-            tokens: None,
-        }))
+        Ok((ident, ForeignItemKind::Static(ty, mutbl)))
     }
 
     /// Parses a type from a foreign module.
-    fn parse_item_foreign_type(
-        &mut self,
-        vis: ast::Visibility,
-        lo: Span,
-        attrs: Vec<Attribute>,
-    ) -> PResult<'a, P<ForeignItem>> {
+    fn parse_item_foreign_type(&mut self) -> PResult<'a, (Ident, ForeignItemKind)> {
         self.expect_keyword(kw::Type)?;
-
         let ident = self.parse_ident()?;
-        let hi = self.token.span;
         self.expect_semi()?;
-        Ok(P(ast::ForeignItem {
-            ident,
-            attrs,
-            kind: ForeignItemKind::Ty,
-            id: DUMMY_NODE_ID,
-            span: lo.to(hi),
-            vis,
-            tokens: None,
-        }))
+        Ok((ident, ForeignItemKind::Ty))
     }
 
     fn is_static_global(&mut self) -> bool {
@@ -1086,6 +948,22 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Recover on `const mut` with `const` already eaten.
+    fn recover_const_mut(&mut self, const_span: Span) {
+        if self.eat_keyword(kw::Mut) {
+            let span = self.prev_span;
+            self.struct_span_err(span, "const globals cannot be mutable")
+                .span_label(span, "cannot be mutable")
+                .span_suggestion(
+                    const_span,
+                    "you might want to declare a static instead",
+                    "static".to_owned(),
+                    Applicability::MaybeIncorrect,
+                )
+                .emit();
+        }
+    }
+
     /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty = $expr` with
     /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
     ///
@@ -1110,7 +988,7 @@ impl<'a> Parser<'a> {
             Some(m) => ItemKind::Static(ty, m, e),
             None => ItemKind::Const(ty, e),
         };
-        Ok((id, item, None))
+        Ok((id, item))
     }
 
     /// We were supposed to parse `:` but instead, we're already at `=`.
@@ -1163,7 +1041,7 @@ impl<'a> Parser<'a> {
 
         let enum_definition =
             EnumDef { variants: variants.into_iter().filter_map(|v| v).collect() };
-        Ok((id, ItemKind::Enum(enum_definition, generics), None))
+        Ok((id, ItemKind::Enum(enum_definition, generics)))
     }
 
     fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
@@ -1257,7 +1135,7 @@ impl<'a> Parser<'a> {
             return Err(err);
         };
 
-        Ok((class_name, ItemKind::Struct(vdata, generics), None))
+        Ok((class_name, ItemKind::Struct(vdata, generics)))
     }
 
     /// Parses `union Foo { ... }`.
@@ -1281,12 +1159,7 @@ impl<'a> Parser<'a> {
             return Err(err);
         };
 
-        Ok((class_name, ItemKind::Union(vdata, generics), None))
-    }
-
-    pub(super) fn is_union_item(&self) -> bool {
-        self.token.is_keyword(kw::Union)
-            && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
+        Ok((class_name, ItemKind::Union(vdata, generics)))
     }
 
     fn parse_record_struct_body(
@@ -1436,112 +1309,109 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub(super) fn eat_macro_def(
-        &mut self,
-        attrs: &[Attribute],
-        vis: &Visibility,
-        lo: Span,
-    ) -> PResult<'a, Option<P<Item>>> {
-        let (ident, def) = if self.eat_keyword(kw::Macro) {
-            let ident = self.parse_ident()?;
-            let body = if self.check(&token::OpenDelim(token::Brace)) {
-                self.parse_mac_args()?
-            } else if self.check(&token::OpenDelim(token::Paren)) {
-                let params = self.parse_token_tree();
-                let pspan = params.span();
-                let body = if self.check(&token::OpenDelim(token::Brace)) {
-                    self.parse_token_tree()
-                } else {
-                    return self.unexpected();
-                };
-                let bspan = body.span();
-                let tokens = TokenStream::new(vec![
-                    params.into(),
-                    TokenTree::token(token::FatArrow, pspan.between(bspan)).into(),
-                    body.into(),
-                ]);
-                let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
-                P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
-            } else {
+    /// Parses a declarative macro 2.0 definition.
+    /// The `macro` keyword has already been parsed.
+    /// ```
+    /// MacBody = "{" TOKEN_STREAM "}" ;
+    /// MacParams = "(" TOKEN_STREAM ")" ;
+    /// DeclMac = "macro" Ident MacParams? MacBody ;
+    /// ```
+    fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
+        let ident = self.parse_ident()?;
+        let body = if self.check(&token::OpenDelim(token::Brace)) {
+            self.parse_mac_args()? // `MacBody`
+        } else if self.check(&token::OpenDelim(token::Paren)) {
+            let params = self.parse_token_tree(); // `MacParams`
+            let pspan = params.span();
+            if !self.check(&token::OpenDelim(token::Brace)) {
                 return self.unexpected();
-            };
+            }
+            let body = self.parse_token_tree(); // `MacBody`
+            // Convert `MacParams MacBody` into `{ MacParams => MacBody }`.
+            let bspan = body.span();
+            let arrow = TokenTree::token(token::FatArrow, pspan.between(bspan)); // `=>`
+            let tokens = TokenStream::new(vec![params.into(), arrow.into(), body.into()]);
+            let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
+            P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
+        } else {
+            return self.unexpected();
+        };
+
+        self.sess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_span));
+        Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: false })))
+    }
 
-            (ident, ast::MacroDef { body, legacy: false })
-        } else if self.check_keyword(sym::macro_rules)
+    /// Is this unambiguously the start of a `macro_rules! foo` item defnition?
+    fn is_macro_rules_item(&mut self) -> bool {
+        self.check_keyword(sym::macro_rules)
             && self.look_ahead(1, |t| *t == token::Not)
             && self.look_ahead(2, |t| t.is_ident())
-        {
-            let prev_span = self.prev_span;
-            self.complain_if_pub_macro(&vis.node, prev_span);
-            self.bump();
-            self.bump();
-
-            let ident = self.parse_ident()?;
-            let body = self.parse_mac_args()?;
-            if body.need_semicolon() && !self.eat(&token::Semi) {
-                self.report_invalid_macro_expansion_item();
-            }
+    }
 
-            (ident, ast::MacroDef { body, legacy: true })
-        } else {
-            return Ok(None);
-        };
+    /// Parses a legacy `macro_rules! foo { ... }` declarative macro.
+    fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> {
+        self.expect_keyword(sym::macro_rules)?; // `macro_rules`
+        self.expect(&token::Not)?; // `!`
 
-        let span = lo.to(self.prev_span);
+        let ident = self.parse_ident()?;
+        let body = self.parse_mac_args()?;
+        self.eat_semi_for_macro_if_needed(&body);
+        self.complain_if_pub_macro(vis, true);
+
+        Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: true })))
+    }
 
-        if !def.legacy {
-            self.sess.gated_spans.gate(sym::decl_macro, span);
+    /// Item macro invocations or `macro_rules!` definitions need inherited visibility.
+    /// If that's not the case, emit an error.
+    fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) {
+        if let VisibilityKind::Inherited = vis.node {
+            return;
         }
 
-        Ok(Some(self.mk_item(span, ident, ItemKind::MacroDef(def), vis.clone(), attrs.to_vec())))
+        let vstr = pprust::vis_to_string(vis);
+        let vstr = vstr.trim_end();
+        if macro_rules {
+            let msg = format!("can't qualify macro_rules invocation with `{}`", vstr);
+            self.struct_span_err(vis.span, &msg)
+                .span_suggestion(
+                    vis.span,
+                    "try exporting the macro",
+                    "#[macro_export]".to_owned(),
+                    Applicability::MaybeIncorrect, // speculative
+                )
+                .emit();
+        } else {
+            self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`")
+                .span_suggestion(
+                    vis.span,
+                    "remove the visibility",
+                    String::new(),
+                    Applicability::MachineApplicable,
+                )
+                .help(&format!("try adjusting the macro to put `{}` inside the invocation", vstr))
+                .emit();
+        }
     }
 
-    fn complain_if_pub_macro(&self, vis: &VisibilityKind, sp: Span) {
-        match *vis {
-            VisibilityKind::Inherited => {}
-            _ => {
-                let mut err = if self.token.is_keyword(sym::macro_rules) {
-                    let mut err =
-                        self.struct_span_err(sp, "can't qualify macro_rules invocation with `pub`");
-                    err.span_suggestion(
-                        sp,
-                        "try exporting the macro",
-                        "#[macro_export]".to_owned(),
-                        Applicability::MaybeIncorrect, // speculative
-                    );
-                    err
-                } else {
-                    let mut err =
-                        self.struct_span_err(sp, "can't qualify macro invocation with `pub`");
-                    err.help("try adjusting the macro to put `pub` inside the invocation");
-                    err
-                };
-                err.emit();
-            }
+    fn eat_semi_for_macro_if_needed(&mut self, args: &MacArgs) {
+        if args.need_semicolon() && !self.eat(&token::Semi) {
+            self.report_invalid_macro_expansion_item(args);
         }
     }
 
-    fn report_invalid_macro_expansion_item(&self) {
-        let has_close_delim = self
-            .sess
-            .source_map()
-            .span_to_snippet(self.prev_span)
-            .map(|s| s.ends_with(")") || s.ends_with("]"))
-            .unwrap_or(false);
-
+    fn report_invalid_macro_expansion_item(&self, args: &MacArgs) {
         let mut err = self.struct_span_err(
             self.prev_span,
             "macros that expand to items must be delimited with braces or followed by a semicolon",
         );
-
-        // To avoid ICE, we shouldn't emit actual suggestions when it hasn't closing delims
-        if has_close_delim {
+        if self.unclosed_delims.is_empty() {
+            let DelimSpan { open, close } = match args {
+                MacArgs::Empty | MacArgs::Eq(..) => unreachable!(),
+                MacArgs::Delimited(dspan, ..) => *dspan,
+            };
             err.multipart_suggestion(
                 "change the delimiters to curly braces",
-                vec![
-                    (self.prev_span.with_hi(self.prev_span.lo() + BytePos(1)), '{'.to_string()),
-                    (self.prev_span.with_lo(self.prev_span.hi() - BytePos(1)), '}'.to_string()),
-                ],
+                vec![(open, "{".to_string()), (close, '}'.to_string())],
                 Applicability::MaybeIncorrect,
             );
         } else {
@@ -1552,14 +1422,13 @@ impl<'a> Parser<'a> {
                 Applicability::HasPlaceholders,
             );
         }
-
         err.span_suggestion(
             self.prev_span.shrink_to_hi(),
             "add a semicolon",
             ';'.to_string(),
             Applicability::MaybeIncorrect,
-        )
-        .emit();
+        );
+        err.emit();
     }
 
     /// Checks if current token is one of tokens which cannot be nested like `kw::Enum`. In case
@@ -1591,15 +1460,16 @@ impl<'a> Parser<'a> {
         Ok(true)
     }
 
-    fn mk_item(
+    fn mk_item<K>(
         &self,
-        span: Span,
+        lo: Span,
         ident: Ident,
-        kind: ItemKind,
+        kind: K,
         vis: Visibility,
         attrs: Vec<Attribute>,
-    ) -> P<Item> {
-        P(Item { ident, attrs, id: DUMMY_NODE_ID, kind, vis, span, tokens: None })
+    ) -> Item<K> {
+        let span = lo.to(self.prev_span);
+        Item { ident, attrs, id: DUMMY_NODE_ID, kind, vis, span, tokens: None }
     }
 }
 
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 4716d722778a6..79944dc35e523 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -572,6 +572,11 @@ impl<'a> Parser<'a> {
         if !self.eat_keyword(kw) { self.unexpected() } else { Ok(()) }
     }
 
+    /// Is the given keyword `kw` followed by a non-reserved identifier?
+    fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
+        self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
+    }
+
     fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool {
         if ok {
             true
diff --git a/src/librustc_parse/parser/module.rs b/src/librustc_parse/parser/module.rs
index 0c8fad03d8690..754923ae55e29 100644
--- a/src/librustc_parse/parser/module.rs
+++ b/src/librustc_parse/parser/module.rs
@@ -40,36 +40,34 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
-    pub(super) fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> {
-        let (in_cfg, outer_attrs) =
-            crate::config::process_configure_mod(self.sess, self.cfg_mods, outer_attrs);
+    pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec<Attribute>) -> PResult<'a, ItemInfo> {
+        let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs);
 
         let id_span = self.token.span;
         let id = self.parse_ident()?;
-        if self.eat(&token::Semi) {
+        let (module, mut inner_attrs) = if self.eat(&token::Semi) {
             if in_cfg && self.recurse_into_file_modules {
                 // This mod is in an external file. Let's go get it!
                 let ModulePathSuccess { path, directory_ownership } =
-                    self.submod_path(id, &outer_attrs, id_span)?;
-                let (module, attrs) =
-                    self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?;
-                Ok((id, ItemKind::Mod(module), Some(attrs)))
+                    self.submod_path(id, &attrs, id_span)?;
+                self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)?
             } else {
-                let placeholder = ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false };
-                Ok((id, ItemKind::Mod(placeholder), None))
+                (ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new())
             }
         } else {
             let old_directory = self.directory.clone();
-            self.push_directory(id, &outer_attrs);
+            self.push_directory(id, &attrs);
 
             self.expect(&token::OpenDelim(token::Brace))?;
             let mod_inner_lo = self.token.span;
-            let attrs = self.parse_inner_attributes()?;
+            let inner_attrs = self.parse_inner_attributes()?;
             let module = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?;
 
             self.directory = old_directory;
-            Ok((id, ItemKind::Mod(module), Some(attrs)))
-        }
+            (module, inner_attrs)
+        };
+        attrs.append(&mut inner_attrs);
+        Ok((id, ItemKind::Mod(module)))
     }
 
     /// Given a termination token, parses all of the items in a module.
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index e97af0dc00c69..742fc4802fd9d 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -7,10 +7,10 @@ use crate::maybe_whole;
 use crate::DirectoryOwnership;
 
 use rustc_errors::{Applicability, PResult};
-use rustc_span::source_map::{respan, BytePos, Span};
-use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::source_map::{BytePos, Span};
+use rustc_span::symbol::{kw, sym};
 use syntax::ast;
-use syntax::ast::{AttrStyle, AttrVec, Attribute, Mac, MacStmtStyle, VisibilityKind};
+use syntax::ast::{AttrStyle, AttrVec, Attribute, Mac, MacStmtStyle};
 use syntax::ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID};
 use syntax::ptr::P;
 use syntax::token::{self, TokenKind};
@@ -55,21 +55,11 @@ impl<'a> Parser<'a> {
             return self.recover_stmt_local(lo, attrs.into(), msg, "let");
         }
 
-        let mac_vis = respan(lo, VisibilityKind::Inherited);
-        if let Some(macro_def) = self.eat_macro_def(&attrs, &mac_vis, lo)? {
-            return Ok(Some(self.mk_stmt(lo.to(self.prev_span), StmtKind::Item(macro_def))));
-        }
-
-        // Starts like a simple path, being careful to avoid contextual keywords
-        // such as a union items, item with `crate` visibility or auto trait items.
-        // Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
-        // like a path (1 token), but it fact not a path.
-        if self.token.is_path_start()
-            && !self.token.is_qpath_start()
-            && !self.is_union_item() // `union::b::c` - path, `union U { ... }` - not a path.
-            && !self.is_crate_vis() // `crate::b::c` - path, `crate struct S;` - not a path.
-            && !self.is_auto_trait_item()
-            && !self.is_async_fn()
+        // Starts like a simple path, being careful to avoid contextual keywords,
+        // e.g., `union`, items with `crate` visibility, or `auto trait` items.
+        // We aim to parse an arbitrary path `a::b` but not something that starts like a path
+        // (1 token), but it fact not a path. Also, we avoid stealing syntax from `parse_item_`.
+        if self.token.is_path_start() && !self.token.is_qpath_start() && !self.is_path_start_item()
         {
             let path = self.parse_path(PathStyle::Expr)?;
 
@@ -199,10 +189,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
-        self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
-    }
-
     fn recover_stmt_local(
         &mut self,
         lo: Span,
@@ -299,16 +285,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn is_auto_trait_item(&self) -> bool {
-        // auto trait
-        (self.token.is_keyword(kw::Auto) &&
-            self.is_keyword_ahead(1, &[kw::Trait]))
-        || // unsafe auto trait
-        (self.token.is_keyword(kw::Unsafe) &&
-         self.is_keyword_ahead(1, &[kw::Auto]) &&
-         self.is_keyword_ahead(2, &[kw::Trait]))
-    }
-
     /// Parses a block. No inner attributes are allowed.
     pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
         maybe_whole!(self, NtBlock, |x| x);
diff --git a/src/test/pretty/trait-inner-attr.rs b/src/test/pretty/trait-inner-attr.rs
new file mode 100644
index 0000000000000..bb4fb1459bd64
--- /dev/null
+++ b/src/test/pretty/trait-inner-attr.rs
@@ -0,0 +1,7 @@
+// pp-exact
+
+trait Foo {
+    #![allow(bar)]
+}
+
+fn main() { }
diff --git a/src/test/ui/issues/issue-58856-2.stderr b/src/test/ui/issues/issue-58856-2.stderr
index 01d70d861e2c9..6221b90b31dd7 100644
--- a/src/test/ui/issues/issue-58856-2.stderr
+++ b/src/test/ui/issues/issue-58856-2.stderr
@@ -7,11 +7,11 @@ LL |     fn how_are_you(&self -> Empty {
    |                   |     help: `)` may belong here
    |                   unclosed delimiter
 
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `)`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, `}`, or identifier, found `)`
   --> $DIR/issue-58856-2.rs:11:1
    |
 LL |     }
-   |      - expected one of 10 possible tokens
+   |      - expected one of 11 possible tokens
 LL | }
    | ^ unexpected token
 
diff --git a/src/test/ui/issues/issue-60075.stderr b/src/test/ui/issues/issue-60075.stderr
index 60eb99b46b79f..b2beb73503bb0 100644
--- a/src/test/ui/issues/issue-60075.stderr
+++ b/src/test/ui/issues/issue-60075.stderr
@@ -4,7 +4,7 @@ error: expected one of `.`, `;`, `?`, `else`, or an operator, found `}`
 LL |         });
    |          ^ expected one of `.`, `;`, `?`, `else`, or an operator
 
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `;`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, `}`, or identifier, found `;`
   --> $DIR/issue-60075.rs:6:11
    |
 LL |     fn qux() -> Option<usize> {
diff --git a/src/test/ui/macros/issue-54441.rs b/src/test/ui/macros/issue-54441.rs
index afdf76b7b580f..a70163df1cb93 100644
--- a/src/test/ui/macros/issue-54441.rs
+++ b/src/test/ui/macros/issue-54441.rs
@@ -1,6 +1,7 @@
 macro_rules! m {
+    //~^ ERROR missing `fn`, `type`, or `static` for extern-item declaration
     () => {
-        let //~ ERROR expected
+        let
     };
 }
 
diff --git a/src/test/ui/macros/issue-54441.stderr b/src/test/ui/macros/issue-54441.stderr
index c94355f47161c..761e7aec7235a 100644
--- a/src/test/ui/macros/issue-54441.stderr
+++ b/src/test/ui/macros/issue-54441.stderr
@@ -1,13 +1,11 @@
-error: expected one of `async`, `const`, `crate`, `extern`, `fn`, `pub`, `static`, `type`, or `unsafe`, found keyword `let`
-  --> $DIR/issue-54441.rs:3:9
+error: missing `fn`, `type`, or `static` for extern-item declaration
+  --> $DIR/issue-54441.rs:1:1
    |
-LL |         let
-   |         ^^^ expected one of 9 possible tokens
-...
-LL |     m!();
-   |     ----- in this macro invocation
-   |
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+LL | / macro_rules! m {
+LL | |
+LL | |     () => {
+LL | |         let
+   | |________^ missing `fn`, `type`, or `static`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/attr-before-eof.stderr b/src/test/ui/parser/attr-before-eof.stderr
index eb5daf849811c..a2acb94372b8c 100644
--- a/src/test/ui/parser/attr-before-eof.stderr
+++ b/src/test/ui/parser/attr-before-eof.stderr
@@ -1,8 +1,8 @@
 error: expected item after attributes
-  --> $DIR/attr-before-eof.rs:3:16
+  --> $DIR/attr-before-eof.rs:3:1
    |
 LL | #[derive(Debug)]
-   |                ^
+   | ^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/attr-dangling-in-mod.stderr b/src/test/ui/parser/attr-dangling-in-mod.stderr
index d896b61ce49c4..1c892eac08f3b 100644
--- a/src/test/ui/parser/attr-dangling-in-mod.stderr
+++ b/src/test/ui/parser/attr-dangling-in-mod.stderr
@@ -1,8 +1,8 @@
 error: expected item after attributes
-  --> $DIR/attr-dangling-in-mod.rs:6:14
+  --> $DIR/attr-dangling-in-mod.rs:6:1
    |
 LL | #[foo = "bar"]
-   |              ^
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/attrs-after-extern-mod.rs b/src/test/ui/parser/attrs-after-extern-mod.rs
index 4bdd16471cd80..ea899dca7b221 100644
--- a/src/test/ui/parser/attrs-after-extern-mod.rs
+++ b/src/test/ui/parser/attrs-after-extern-mod.rs
@@ -1,13 +1,7 @@
-// Constants (static variables) can be used to match in patterns, but mutable
-// statics cannot. This ensures that there's some form of error if this is
-// attempted.
+// Make sure there's an error when given `extern { ... #[attr] }`.
 
-extern crate libc;
+fn main() {}
 
 extern {
-    static mut rust_dbg_static_mut: libc::c_int;
-    pub fn rust_dbg_static_mut_check_four();
     #[cfg(stage37)] //~ ERROR expected item after attributes
 }
-
-pub fn main() {}
diff --git a/src/test/ui/parser/attrs-after-extern-mod.stderr b/src/test/ui/parser/attrs-after-extern-mod.stderr
index cecdab4d63197..6060f3afe1e95 100644
--- a/src/test/ui/parser/attrs-after-extern-mod.stderr
+++ b/src/test/ui/parser/attrs-after-extern-mod.stderr
@@ -1,8 +1,8 @@
 error: expected item after attributes
-  --> $DIR/attrs-after-extern-mod.rs:10:19
+  --> $DIR/attrs-after-extern-mod.rs:6:5
    |
 LL |     #[cfg(stage37)]
-   |                   ^
+   |     ^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/default.rs b/src/test/ui/parser/default.rs
index 17cd06864bf1d..65ecb1ebbe919 100644
--- a/src/test/ui/parser/default.rs
+++ b/src/test/ui/parser/default.rs
@@ -19,7 +19,8 @@ impl Foo for u16 {
 }
 
 impl Foo for u32 { //~ ERROR not all trait items implemented, missing: `foo`
-    default pub fn foo<T: Default>() -> T { T::default() } //~ ERROR expected one of
+    default pub fn foo<T: Default>() -> T { T::default() }
+    //~^ ERROR missing `fn`, `type`, or `const` for associated-item declaration
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/default.stderr b/src/test/ui/parser/default.stderr
index dde36cf8ddeed..ede9e47151863 100644
--- a/src/test/ui/parser/default.stderr
+++ b/src/test/ui/parser/default.stderr
@@ -1,8 +1,8 @@
-error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found keyword `pub`
-  --> $DIR/default.rs:22:13
+error: missing `fn`, `type`, or `const` for associated-item declaration
+  --> $DIR/default.rs:22:12
    |
 LL |     default pub fn foo<T: Default>() -> T { T::default() }
-   |             ^^^ expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`
+   |            ^ missing `fn`, `type`, or `const`
 
 error[E0449]: unnecessary visibility qualifier
   --> $DIR/default.rs:16:5
diff --git a/src/test/ui/parser/doc-before-attr.stderr b/src/test/ui/parser/doc-before-attr.stderr
index 0fae44ce5c806..14fd01af2f98a 100644
--- a/src/test/ui/parser/doc-before-attr.stderr
+++ b/src/test/ui/parser/doc-before-attr.stderr
@@ -1,8 +1,10 @@
 error: expected item after attributes
-  --> $DIR/doc-before-attr.rs:4:16
+  --> $DIR/doc-before-attr.rs:4:1
    |
+LL | /// hi
+   | ------ other attributes here
 LL | #[derive(Debug)]
-   |                ^
+   | ^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/doc-before-extern-rbrace.rs b/src/test/ui/parser/doc-before-extern-rbrace.rs
index 695d4da1dca60..040206b80ffd0 100644
--- a/src/test/ui/parser/doc-before-extern-rbrace.rs
+++ b/src/test/ui/parser/doc-before-extern-rbrace.rs
@@ -1,4 +1,6 @@
+fn main() {}
+
 extern {
     /// hi
-    //~^ ERROR expected item after doc comment
+    //~^ ERROR found a documentation comment that doesn't document anything
 }
diff --git a/src/test/ui/parser/doc-before-extern-rbrace.stderr b/src/test/ui/parser/doc-before-extern-rbrace.stderr
index 8cc9c916a7afd..0edceb268a7f5 100644
--- a/src/test/ui/parser/doc-before-extern-rbrace.stderr
+++ b/src/test/ui/parser/doc-before-extern-rbrace.stderr
@@ -1,8 +1,11 @@
-error: expected item after doc comment
-  --> $DIR/doc-before-extern-rbrace.rs:2:5
+error[E0584]: found a documentation comment that doesn't document anything
+  --> $DIR/doc-before-extern-rbrace.rs:4:5
    |
 LL |     /// hi
    |     ^^^^^^ this doc comment doesn't document anything
+   |
+   = help: doc comments must come before what they document, maybe a comment was intended with `//`?
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0584`.
diff --git a/src/test/ui/parser/doc-inside-trait-item.stderr b/src/test/ui/parser/doc-inside-trait-item.stderr
index 261e27b6e0d18..246255a0a4675 100644
--- a/src/test/ui/parser/doc-inside-trait-item.stderr
+++ b/src/test/ui/parser/doc-inside-trait-item.stderr
@@ -2,7 +2,7 @@ error[E0584]: found a documentation comment that doesn't document anything
   --> $DIR/doc-inside-trait-item.rs:3:5
    |
 LL |     /// empty doc
-   |     ^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^ this doc comment doesn't document anything
    |
    = help: doc comments must come before what they document, maybe a comment was intended with `//`?
 
diff --git a/src/test/ui/parser/duplicate-visibility.rs b/src/test/ui/parser/duplicate-visibility.rs
index a8f0b7d61b985..1d271fa64b0ec 100644
--- a/src/test/ui/parser/duplicate-visibility.rs
+++ b/src/test/ui/parser/duplicate-visibility.rs
@@ -1,4 +1,6 @@
-// error-pattern: expected one of `(`, `async`, `const`, `extern`, `fn`
+fn main() {}
+
 extern {
     pub pub fn foo();
+    //~^ ERROR missing `fn`, `type`, or `static` for extern-item declaration
 }
diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr
index cba4058e48255..36a3a1ed5a0cc 100644
--- a/src/test/ui/parser/duplicate-visibility.stderr
+++ b/src/test/ui/parser/duplicate-visibility.stderr
@@ -1,8 +1,8 @@
-error: expected one of `(`, `async`, `const`, `extern`, `fn`, `static`, `type`, or `unsafe`, found keyword `pub`
-  --> $DIR/duplicate-visibility.rs:3:9
+error: missing `fn`, `type`, or `static` for extern-item declaration
+  --> $DIR/duplicate-visibility.rs:4:8
    |
 LL |     pub pub fn foo();
-   |         ^^^ expected one of 8 possible tokens
+   |        ^ missing `fn`, `type`, or `static`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/inner-attr-in-trait-def.rs b/src/test/ui/parser/inner-attr-in-trait-def.rs
new file mode 100644
index 0000000000000..8dba6b362cdfa
--- /dev/null
+++ b/src/test/ui/parser/inner-attr-in-trait-def.rs
@@ -0,0 +1,9 @@
+// check-pass
+
+#![deny(non_camel_case_types)]
+
+fn main() {}
+
+trait foo_bar {
+    #![allow(non_camel_case_types)]
+}
diff --git a/src/test/ui/parser/issue-19398.rs b/src/test/ui/parser/issue-19398.rs
index 2158a2fd6c11c..982a6be23ac5f 100644
--- a/src/test/ui/parser/issue-19398.rs
+++ b/src/test/ui/parser/issue-19398.rs
@@ -1,5 +1,6 @@
 trait T {
-    extern "Rust" unsafe fn foo(); //~ ERROR expected one of `async`, `const`
+    //~^ ERROR missing `fn`, `type`, or `const` for associated-item declaration
+    extern "Rust" unsafe fn foo();
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-19398.stderr b/src/test/ui/parser/issue-19398.stderr
index 201a6b2d66a7c..2bd6ac3a4b3a4 100644
--- a/src/test/ui/parser/issue-19398.stderr
+++ b/src/test/ui/parser/issue-19398.stderr
@@ -1,10 +1,11 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `extern`
-  --> $DIR/issue-19398.rs:2:5
+error: missing `fn`, `type`, or `const` for associated-item declaration
+  --> $DIR/issue-19398.rs:1:10
    |
-LL | trait T {
-   |          - expected one of 10 possible tokens
-LL |     extern "Rust" unsafe fn foo();
-   |     ^^^^^^ unexpected token
+LL |   trait T {
+   |  __________^
+LL | |
+LL | |     extern "Rust" unsafe fn foo();
+   | |____^ missing `fn`, `type`, or `const`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-20711-2.rs b/src/test/ui/parser/issue-20711-2.rs
index 0063a334182f4..168c7e76162bc 100644
--- a/src/test/ui/parser/issue-20711-2.rs
+++ b/src/test/ui/parser/issue-20711-2.rs
@@ -4,6 +4,7 @@ impl Foo {
     fn foo() {}
 
     #[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or
+    //~^ ERROR expected item after attributes
+}
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-20711-2.stderr b/src/test/ui/parser/issue-20711-2.stderr
index ee484890fada8..10ef31584dec1 100644
--- a/src/test/ui/parser/issue-20711-2.stderr
+++ b/src/test/ui/parser/issue-20711-2.stderr
@@ -1,10 +1,8 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
-  --> $DIR/issue-20711-2.rs:7:1
+error: expected item after attributes
+  --> $DIR/issue-20711-2.rs:6:5
    |
 LL |     #[stable(feature = "rust1", since = "1.0.0")]
-   |                                                  - expected one of 9 possible tokens
-LL | }
-   | ^ unexpected token
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-20711.rs b/src/test/ui/parser/issue-20711.rs
index dc216167b8a4f..020bb79d6e74e 100644
--- a/src/test/ui/parser/issue-20711.rs
+++ b/src/test/ui/parser/issue-20711.rs
@@ -2,6 +2,7 @@ struct Foo;
 
 impl Foo {
     #[stable(feature = "rust1", since = "1.0.0")]
-} //~ ERROR expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or
+    //~^ ERROR expected item after attributes
+}
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-20711.stderr b/src/test/ui/parser/issue-20711.stderr
index 152c9f1c68975..66768de569418 100644
--- a/src/test/ui/parser/issue-20711.stderr
+++ b/src/test/ui/parser/issue-20711.stderr
@@ -1,10 +1,8 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `}`
-  --> $DIR/issue-20711.rs:5:1
+error: expected item after attributes
+  --> $DIR/issue-20711.rs:4:5
    |
 LL |     #[stable(feature = "rust1", since = "1.0.0")]
-   |                                                  - expected one of 9 possible tokens
-LL | }
-   | ^ unexpected token
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-32446.stderr b/src/test/ui/parser/issue-32446.stderr
index 1a97f54160b24..25c1efe35ae11 100644
--- a/src/test/ui/parser/issue-32446.stderr
+++ b/src/test/ui/parser/issue-32446.stderr
@@ -1,8 +1,8 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `...`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, `}`, or identifier, found `...`
   --> $DIR/issue-32446.rs:4:11
    |
 LL | trait T { ... }
-   |           ^^^ expected one of 10 possible tokens
+   |           ^^^ expected one of 11 possible tokens
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-41155.stderr b/src/test/ui/parser/issue-41155.stderr
index 0e191eb7e0a04..327bc65818fa9 100644
--- a/src/test/ui/parser/issue-41155.stderr
+++ b/src/test/ui/parser/issue-41155.stderr
@@ -1,8 +1,8 @@
-error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `type`, or `unsafe`, found `}`
+error: expected one of `(`, `async`, `const`, `default`, `extern`, `fn`, `type`, `unsafe`, or identifier, found `}`
   --> $DIR/issue-41155.rs:5:1
    |
 LL |     pub
-   |        - expected one of 8 possible tokens
+   |        - expected one of 9 possible tokens
 LL | }
    | ^ unexpected token
 
diff --git a/src/test/ui/parser/issue-58094-missing-right-square-bracket.stderr b/src/test/ui/parser/issue-58094-missing-right-square-bracket.stderr
index 00f6652b311da..8a44ee761ed67 100644
--- a/src/test/ui/parser/issue-58094-missing-right-square-bracket.stderr
+++ b/src/test/ui/parser/issue-58094-missing-right-square-bracket.stderr
@@ -7,10 +7,10 @@ LL | #[Ѕ
    |  unclosed delimiter
 
 error: expected item after attributes
-  --> $DIR/issue-58094-missing-right-square-bracket.rs:4:4
+  --> $DIR/issue-58094-missing-right-square-bracket.rs:4:1
    |
 LL | #[Ѕ
-   |    ^
+   | ^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/parser/macro/pub-item-macro.rs b/src/test/ui/parser/macro/pub-item-macro.rs
index bae90227c62f4..f5f8a01e6a440 100644
--- a/src/test/ui/parser/macro/pub-item-macro.rs
+++ b/src/test/ui/parser/macro/pub-item-macro.rs
@@ -1,12 +1,15 @@
 // Issue #14660
 
-macro_rules! priv_x { () => {
-    static x: u32 = 0;
-}}
+macro_rules! priv_x {
+    () => {
+        static x: u32 = 0;
+    };
+}
 
 macro_rules! pub_x { () => {
     pub priv_x!(); //~ ERROR can't qualify macro invocation with `pub`
-    //~^ HELP try adjusting the macro to put `pub` inside the invocation
+    //~^ HELP remove the visibility
+    //~| HELP try adjusting the macro to put `pub` inside the invocation
 }}
 
 mod foo {
diff --git a/src/test/ui/parser/macro/pub-item-macro.stderr b/src/test/ui/parser/macro/pub-item-macro.stderr
index 49644cf6a0e64..4ff96532e03a8 100644
--- a/src/test/ui/parser/macro/pub-item-macro.stderr
+++ b/src/test/ui/parser/macro/pub-item-macro.stderr
@@ -1,8 +1,8 @@
 error: can't qualify macro invocation with `pub`
-  --> $DIR/pub-item-macro.rs:8:5
+  --> $DIR/pub-item-macro.rs:10:5
    |
 LL |     pub priv_x!();
-   |     ^^^
+   |     ^^^ help: remove the visibility
 ...
 LL |     pub_x!();
    |     --------- in this macro invocation
@@ -11,16 +11,16 @@ LL |     pub_x!();
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0603]: static `x` is private
-  --> $DIR/pub-item-macro.rs:17:23
+  --> $DIR/pub-item-macro.rs:20:23
    |
 LL |     let y: u32 = foo::x;
    |                       ^ this static is private
    |
 note: the static `x` is defined here
-  --> $DIR/pub-item-macro.rs:4:5
+  --> $DIR/pub-item-macro.rs:5:9
    |
-LL |     static x: u32 = 0;
-   |     ^^^^^^^^^^^^^^^^^^
+LL |         static x: u32 = 0;
+   |         ^^^^^^^^^^^^^^^^^^
 ...
 LL |     pub_x!();
    |     --------- in this macro invocation
diff --git a/src/test/ui/parser/macro/trait-non-item-macros.stderr b/src/test/ui/parser/macro/trait-non-item-macros.stderr
index 5a89b5b936f5b..9d05e85bcc00e 100644
--- a/src/test/ui/parser/macro/trait-non-item-macros.stderr
+++ b/src/test/ui/parser/macro/trait-non-item-macros.stderr
@@ -1,8 +1,8 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, or `unsafe`, found `2`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or identifier, found `2`
   --> $DIR/trait-non-item-macros.rs:2:19
    |
 LL |     ($a:expr) => ($a)
-   |                   ^^ expected one of 9 possible tokens
+   |                   ^^ expected one of 10 possible tokens
 ...
 LL |     bah!(2);
    |     -------- in this macro invocation
diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs
index 9f02a7a997bf2..592215030f552 100644
--- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs
+++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs
@@ -1,12 +1,14 @@
+fn main() {}
+
 impl T for () { //~ ERROR cannot find trait `T` in this scope
 
 fn foo(&self) {}
+//~^ ERROR missing `fn`, `type`, or `const`
 
-trait T { //~ ERROR expected one of
+trait T {
     fn foo(&self);
 }
 
 pub(crate) struct Bar<T>();
 
-fn main() {}
 //~ ERROR this file contains an unclosed delimiter
diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr
index a23cfeac58f84..1ec54525105f6 100644
--- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr
+++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr
@@ -1,5 +1,5 @@
 error: this file contains an unclosed delimiter
-  --> $DIR/missing-close-brace-in-impl-trait.rs:12:52
+  --> $DIR/missing-close-brace-in-impl-trait.rs:14:52
    |
 LL | impl T for () {
    |               - unclosed delimiter
@@ -7,23 +7,18 @@ LL | impl T for () {
 LL |
    |                                                    ^
 
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `trait`
-  --> $DIR/missing-close-brace-in-impl-trait.rs:5:1
+error: missing `fn`, `type`, or `const` for associated-item declaration
+  --> $DIR/missing-close-brace-in-impl-trait.rs:5:17
    |
-LL | impl T for () {
-   |               - unclosed delimiter
-LL | 
-LL | fn foo(&self) {}
-   |                 -
-   |                 |
-   |                 expected one of 10 possible tokens
-   |                 help: `}` may belong here
-LL | 
-LL | trait T {
-   | ^^^^^ unexpected token
+LL |   fn foo(&self) {}
+   |  _________________^
+LL | |
+LL | |
+LL | | trait T {
+   | |_ missing `fn`, `type`, or `const`
 
 error[E0405]: cannot find trait `T` in this scope
-  --> $DIR/missing-close-brace-in-impl-trait.rs:1:6
+  --> $DIR/missing-close-brace-in-impl-trait.rs:3:6
    |
 LL | impl T for () {
    |      ^ not found in this scope
diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs
index 5ec5d45bbe7b2..077e334719427 100644
--- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs
+++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs
@@ -3,7 +3,7 @@ trait T {
     fn foo(&self);
 
 pub(crate) struct Bar<T>();
-//~^ ERROR expected one of
+//~^ ERROR missing `fn`, `type`, or `const`
 
 impl T for Bar<usize> {
 fn foo(&self) {}
diff --git a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr
index 213640127829c..1bb153c461d90 100644
--- a/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr
+++ b/src/test/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr
@@ -7,11 +7,11 @@ LL | trait T {
 LL | fn main() {}
    |                                                                 ^
 
-error: expected one of `async`, `const`, `default`, `extern`, `fn`, `type`, or `unsafe`, found keyword `struct`
-  --> $DIR/missing-close-brace-in-trait.rs:5:12
+error: missing `fn`, `type`, or `const` for associated-item declaration
+  --> $DIR/missing-close-brace-in-trait.rs:5:11
    |
 LL | pub(crate) struct Bar<T>();
-   |            ^^^^^^ expected one of 7 possible tokens
+   |           ^ missing `fn`, `type`, or `const`
 
 error[E0601]: `main` function not found in crate `missing_close_brace_in_trait`
   --> $DIR/missing-close-brace-in-trait.rs:1:1
diff --git a/src/test/ui/parser/pub-method-macro.rs b/src/test/ui/parser/pub-method-macro.rs
index f04af1a0d65ff..0183bdcf6220b 100644
--- a/src/test/ui/parser/pub-method-macro.rs
+++ b/src/test/ui/parser/pub-method-macro.rs
@@ -15,7 +15,8 @@ mod bleh {
 
     impl S {
         pub defn!(f); //~ ERROR can't qualify macro invocation with `pub`
-        //~^ HELP try adjusting the macro to put `pub` inside the invocation
+        //~^ HELP remove the visibility
+        //~| HELP try adjusting the macro to put `pub` inside the invocation
     }
 }
 
diff --git a/src/test/ui/parser/pub-method-macro.stderr b/src/test/ui/parser/pub-method-macro.stderr
index 7b0fe4934610d..7c7a909267a1c 100644
--- a/src/test/ui/parser/pub-method-macro.stderr
+++ b/src/test/ui/parser/pub-method-macro.stderr
@@ -2,7 +2,7 @@ error: can't qualify macro invocation with `pub`
   --> $DIR/pub-method-macro.rs:17:9
    |
 LL |         pub defn!(f);
-   |         ^^^
+   |         ^^^ help: remove the visibility
    |
    = help: try adjusting the macro to put `pub` inside the invocation
 
diff --git a/src/test/ui/parser/removed-syntax-static-fn.rs b/src/test/ui/parser/removed-syntax-static-fn.rs
index 0caddb9855d83..9e12222f3fd8b 100644
--- a/src/test/ui/parser/removed-syntax-static-fn.rs
+++ b/src/test/ui/parser/removed-syntax-static-fn.rs
@@ -1,8 +1,8 @@
 struct S;
 
 impl S {
+    //~^ ERROR missing `fn`, `type`, or `const` for associated-item declaration
     static fn f() {}
-    //~^ ERROR expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`,
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/removed-syntax-static-fn.stderr b/src/test/ui/parser/removed-syntax-static-fn.stderr
index dfadefee23c15..5edf88026fbec 100644
--- a/src/test/ui/parser/removed-syntax-static-fn.stderr
+++ b/src/test/ui/parser/removed-syntax-static-fn.stderr
@@ -1,10 +1,11 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `static`
-  --> $DIR/removed-syntax-static-fn.rs:4:5
+error: missing `fn`, `type`, or `const` for associated-item declaration
+  --> $DIR/removed-syntax-static-fn.rs:3:9
    |
-LL | impl S {
-   |         - expected one of 10 possible tokens
-LL |     static fn f() {}
-   |     ^^^^^^ unexpected token
+LL |   impl S {
+   |  _________^
+LL | |
+LL | |     static fn f() {}
+   | |____^ missing `fn`, `type`, or `const`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/pub/pub-restricted-error-fn.rs b/src/test/ui/pub/pub-restricted-error-fn.rs
index 56ee02f517c7b..3f8904fbe79d8 100644
--- a/src/test/ui/pub/pub-restricted-error-fn.rs
+++ b/src/test/ui/pub/pub-restricted-error-fn.rs
@@ -1,3 +1,2 @@
-#![feature(pub_restricted)]
-
 pub(crate) () fn foo() {} //~ unmatched visibility
+//~^ ERROR expected item, found `(`
diff --git a/src/test/ui/pub/pub-restricted-error-fn.stderr b/src/test/ui/pub/pub-restricted-error-fn.stderr
index fcff5334890eb..c0168b02da607 100644
--- a/src/test/ui/pub/pub-restricted-error-fn.stderr
+++ b/src/test/ui/pub/pub-restricted-error-fn.stderr
@@ -1,8 +1,16 @@
-error: unmatched visibility `pub`
-  --> $DIR/pub-restricted-error-fn.rs:3:10
+error: unmatched visibility `pub(crate)`
+  --> $DIR/pub-restricted-error-fn.rs:1:1
    |
 LL | pub(crate) () fn foo() {}
-   |          ^
+   | ^^^^^^^^^^ the unmatched visibility
+   |
+   = help: you likely meant to define an item, e.g., `pub(crate) fn foo() {}`
+
+error: expected item, found `(`
+  --> $DIR/pub-restricted-error-fn.rs:1:12
+   |
+LL | pub(crate) () fn foo() {}
+   |            ^ expected item
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors