about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-12-03 13:21:03 +0000
committerbors <bors@rust-lang.org>2019-12-03 13:21:03 +0000
commitf577b0ef6e637ab7a6095cdfe0b51fa3faf97050 (patch)
tree3acfc5e36ad8991bf613d5b85e3274c816a8b970 /src
parent7d808659cdc6c979a992fe3db345c0cfb53d973e (diff)
parentbce77980a2611da10b42dbd8a672c8cd17f79a94 (diff)
downloadrust-f577b0ef6e637ab7a6095cdfe0b51fa3faf97050.tar.gz
rust-f577b0ef6e637ab7a6095cdfe0b51fa3faf97050.zip
Auto merge of #66982 - Centril:rollup-yq2281i, r=Centril
Rollup of 6 pull requests

Successful merges:

 - #66148 (Show the sign for signed ops on `exact_div`)
 - #66651 (Add `enclosing scope` parameter to `rustc_on_unimplemented`)
 - #66904 (Adding docs for keyword match, move)
 - #66935 (syntax: Unify macro and attribute arguments in AST)
 - #66941 (Remove `ord` lang item)
 - #66967 (Remove hack for top-level or-patterns in match checking)

Failed merges:

r? @ghost
Diffstat (limited to 'src')
-rw-r--r--src/libcore/cmp.rs1
-rw-r--r--src/libcore/ops/try.rs7
-rw-r--r--src/librustc/hir/lowering.rs12
-rw-r--r--src/librustc/hir/lowering/item.rs2
-rw-r--r--src/librustc/middle/lang_items.rs1
-rw-r--r--src/librustc/traits/error_reporting.rs17
-rw-r--r--src/librustc/traits/on_unimplemented.rs46
-rw-r--r--src/librustc_lint/builtin.rs4
-rw-r--r--src/librustc_metadata/rmeta/decoder/cstore_impl.rs5
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs105
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs220
-rw-r--r--src/librustc_mir/interpret/intrinsics.rs6
-rw-r--r--src/librustc_mir/interpret/operand.rs37
-rw-r--r--src/librustc_parse/config.rs2
-rw-r--r--src/librustc_parse/lib.rs6
-rw-r--r--src/librustc_parse/parser/attr.rs32
-rw-r--r--src/librustc_parse/parser/expr.rs6
-rw-r--r--src/librustc_parse/parser/item.rs59
-rw-r--r--src/librustc_parse/parser/mod.rs64
-rw-r--r--src/librustc_parse/parser/pat.rs10
-rw-r--r--src/librustc_parse/parser/path.rs7
-rw-r--r--src/librustc_parse/parser/stmt.rs15
-rw-r--r--src/librustc_parse/parser/ty.rs6
-rw-r--r--src/librustc_parse/validate_attr.rs12
-rw-r--r--src/librustc_passes/ast_validation.rs8
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs8
-rw-r--r--src/librustdoc/clean/inline.rs2
-rw-r--r--src/libstd/keyword_docs.rs76
-rw-r--r--src/libsyntax/ast.rs109
-rw-r--r--src/libsyntax/attr/mod.rs113
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/mut_visit.rs37
-rw-r--r--src/libsyntax/print/pprust.rs55
-rw-r--r--src/libsyntax/tokenstream.rs8
-rw-r--r--src/libsyntax/visit.rs10
-rw-r--r--src/libsyntax_expand/expand.rs32
-rw-r--r--src/libsyntax_expand/mbe/macro_rules.rs12
-rw-r--r--src/libsyntax_expand/mbe/transcribe.rs9
-rw-r--r--src/libsyntax_expand/parse/tests.rs2
-rw-r--r--src/libsyntax_expand/placeholders.rs5
-rw-r--r--src/libsyntax_expand/proc_macro.rs4
-rw-r--r--src/libsyntax_ext/assert.rs24
-rw-r--r--src/libsyntax_ext/cmdline_attrs.rs4
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs15
-rw-r--r--src/libsyntax_pos/symbol.rs1
-rw-r--r--src/test/ui/async-await/try-on-option-in-async.stderr30
-rw-r--r--src/test/ui/issues/issue-10536.rs4
-rw-r--r--src/test/ui/issues/issue-10536.stderr8
-rw-r--r--src/test/ui/on-unimplemented/enclosing-scope.rs27
-rw-r--r--src/test/ui/on-unimplemented/enclosing-scope.stderr66
-rw-r--r--src/test/ui/or-patterns/exhaustiveness-pass.rs23
-rw-r--r--src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs28
-rw-r--r--src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr32
-rw-r--r--src/test/ui/parser/macro-bad-delimiter-ident.rs2
-rw-r--r--src/test/ui/parser/macro-bad-delimiter-ident.stderr4
-rw-r--r--src/test/ui/pattern/usefulness/top-level-alternation.rs56
-rw-r--r--src/test/ui/pattern/usefulness/top-level-alternation.stderr68
-rw-r--r--src/test/ui/proc-macro/proc-macro-gates.rs2
-rw-r--r--src/test/ui/proc-macro/proc-macro-gates.stderr2
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr39
-rw-r--r--src/test/ui/try-on-option-diagnostics.stderr19
-rw-r--r--src/test/ui/try-on-option.stderr9
-rw-r--r--src/test/ui/try-operator-on-main.stderr11
63 files changed, 1096 insertions, 551 deletions
diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs
index eea3dc39d34..a5f355cd9a7 100644
--- a/src/libcore/cmp.rs
+++ b/src/libcore/cmp.rs
@@ -534,7 +534,6 @@ impl<T: Ord> Ord for Reverse<T> {
 ///     }
 /// }
 /// ```
-#[lang = "ord"]
 #[doc(alias = "<")]
 #[doc(alias = ">")]
 #[doc(alias = "<=")]
diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs
index 4f4652084a8..a748ee87ef9 100644
--- a/src/libcore/ops/try.rs
+++ b/src/libcore/ops/try.rs
@@ -5,19 +5,20 @@
 /// extracting those success or failure values from an existing instance and
 /// creating a new instance from a success or failure value.
 #[unstable(feature = "try_trait", issue = "42327")]
-#[rustc_on_unimplemented(
+#[cfg_attr(not(bootstrap), rustc_on_unimplemented(
 on(all(
 any(from_method="from_error", from_method="from_ok"),
 from_desugaring="QuestionMark"),
 message="the `?` operator can only be used in {ItemContext} \
                that returns `Result` or `Option` \
                (or another type that implements `{Try}`)",
-label="cannot use the `?` operator in {ItemContext} that returns `{Self}`"),
+label="cannot use the `?` operator in {ItemContext} that returns `{Self}`",
+enclosing_scope="this function should return `Result` or `Option` to accept `?`"),
 on(all(from_method="into_result", from_desugaring="QuestionMark"),
 message="the `?` operator can only be applied to values \
                that implement `{Try}`",
 label="the `?` operator cannot be applied to type `{Self}`")
-)]
+))]
 #[doc(alias = "?")]
 pub trait Try {
     /// The type of this value when viewed as successful.
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index ac8173f101a..e13f6cabb52 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1003,7 +1003,7 @@ impl<'a> LoweringContext<'a> {
             AttrKind::Normal(ref item) => {
                 AttrKind::Normal(AttrItem {
                     path: item.path.clone(),
-                    tokens: self.lower_token_stream(item.tokens.clone()),
+                    args: self.lower_mac_args(&item.args),
                 })
             }
             AttrKind::DocComment(comment) => AttrKind::DocComment(comment)
@@ -1017,6 +1017,16 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
+    fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
+        match *args {
+            MacArgs::Empty => MacArgs::Empty,
+            MacArgs::Delimited(dspan, delim, ref tokens) =>
+                MacArgs::Delimited(dspan, delim, self.lower_token_stream(tokens.clone())),
+            MacArgs::Eq(eq_span, ref tokens) =>
+                MacArgs::Eq(eq_span, self.lower_token_stream(tokens.clone())),
+        }
+    }
+
     fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream {
         tokens
             .into_trees()
diff --git a/src/librustc/hir/lowering/item.rs b/src/librustc/hir/lowering/item.rs
index f689e7f9622..ff9d8c85df8 100644
--- a/src/librustc/hir/lowering/item.rs
+++ b/src/librustc/hir/lowering/item.rs
@@ -233,7 +233,7 @@ impl LoweringContext<'_> {
 
         if let ItemKind::MacroDef(ref def) = i.kind {
             if !def.legacy || attr::contains_name(&i.attrs, sym::macro_export) {
-                let body = self.lower_token_stream(def.stream());
+                let body = self.lower_token_stream(def.body.inner_tokens());
                 let hir_id = self.lower_node_id(i.id);
                 self.exported_macros.push(hir::MacroDef {
                     name: ident.name,
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index f6cd9b1c7ec..6f7a022eccf 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -358,7 +358,6 @@ language_item_table! {
     // Don't be fooled by the naming here: this lang item denotes `PartialEq`, not `Eq`.
     EqTraitLangItem,             "eq",                 eq_trait,                Target::Trait;
     PartialOrdTraitLangItem,     "partial_ord",        partial_ord_trait,       Target::Trait;
-    OrdTraitLangItem,            "ord",                ord_trait,               Target::Trait;
 
     // A number of panic-related lang items. The `panic` item corresponds to
     // divide-by-zero and various panic cases with `match`. The
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 90db1fe3195..ba44c6c3b9a 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -521,7 +521,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         ) {
             command.evaluate(self.tcx, trait_ref, &flags[..])
         } else {
-            OnUnimplementedNote::empty()
+            OnUnimplementedNote::default()
         }
     }
 
@@ -697,6 +697,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         fallback_has_occurred: bool,
         points_at_arg: bool,
     ) {
+        let tcx = self.tcx;
         let span = obligation.cause.span;
 
         let mut err = match *error {
@@ -732,6 +733,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             message,
                             label,
                             note,
+                            enclosing_scope,
                         } = self.on_unimplemented_note(trait_ref, obligation);
                         let have_alt_message = message.is_some() || label.is_some();
                         let is_try = self.tcx.sess.source_map().span_to_snippet(span)
@@ -798,6 +800,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             // If it has a custom `#[rustc_on_unimplemented]` note, let's display it
                             err.note(s.as_str());
                         }
+                        if let Some(ref s) = enclosing_scope {
+                            let enclosing_scope_span = tcx.def_span(
+                                tcx.hir()
+                                    .opt_local_def_id(obligation.cause.body_id)
+                                    .unwrap_or_else(|| {
+                                        tcx.hir().body_owner_def_id(hir::BodyId {
+                                            hir_id: obligation.cause.body_id,
+                                        })
+                                    }),
+                            );
+
+                            err.span_label(enclosing_scope_span, s.as_str());
+                        }
 
                         self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err);
                         self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg);
diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs
index 59aa0810975..604f39dcf29 100644
--- a/src/librustc/traits/on_unimplemented.rs
+++ b/src/librustc/traits/on_unimplemented.rs
@@ -22,18 +22,15 @@ pub struct OnUnimplementedDirective {
     pub message: Option<OnUnimplementedFormatString>,
     pub label: Option<OnUnimplementedFormatString>,
     pub note: Option<OnUnimplementedFormatString>,
+    pub enclosing_scope: Option<OnUnimplementedFormatString>,
 }
 
+#[derive(Default)]
 pub struct OnUnimplementedNote {
     pub message: Option<String>,
     pub label: Option<String>,
     pub note: Option<String>,
-}
-
-impl OnUnimplementedNote {
-    pub fn empty() -> Self {
-        OnUnimplementedNote { message: None, label: None, note: None }
-    }
+    pub enclosing_scope: Option<String>,
 }
 
 fn parse_error(
@@ -85,24 +82,33 @@ impl<'tcx> OnUnimplementedDirective {
         let mut message = None;
         let mut label = None;
         let mut note = None;
+        let mut enclosing_scope = None;
         let mut subcommands = vec![];
+
+        let parse_value = |value_str| {
+                OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span)
+                    .map(Some)
+            };
+
         for item in item_iter {
             if item.check_name(sym::message) && message.is_none() {
                 if let Some(message_) = item.value_str() {
-                    message = Some(OnUnimplementedFormatString::try_parse(
-                        tcx, trait_def_id, message_, span)?);
+                    message = parse_value(message_)?;
                     continue;
                 }
             } else if item.check_name(sym::label) && label.is_none() {
                 if let Some(label_) = item.value_str() {
-                    label = Some(OnUnimplementedFormatString::try_parse(
-                        tcx, trait_def_id, label_, span)?);
+                    label = parse_value(label_)?;
                     continue;
                 }
             } else if item.check_name(sym::note) && note.is_none() {
                 if let Some(note_) = item.value_str() {
-                    note = Some(OnUnimplementedFormatString::try_parse(
-                        tcx, trait_def_id, note_, span)?);
+                    note = parse_value(note_)?;
+                    continue;
+                }
+            } else if item.check_name(sym::enclosing_scope) && enclosing_scope.is_none() {
+                if let Some(enclosing_scope_) = item.value_str() {
+                    enclosing_scope = parse_value(enclosing_scope_)?;
                     continue;
                 }
             } else if item.check_name(sym::on) && is_root &&
@@ -130,7 +136,14 @@ impl<'tcx> OnUnimplementedDirective {
         if errored {
             Err(ErrorReported)
         } else {
-            Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
+            Ok(OnUnimplementedDirective {
+                condition,
+                subcommands,
+                message,
+                label,
+                note,
+                enclosing_scope
+            })
         }
     }
 
@@ -157,6 +170,7 @@ impl<'tcx> OnUnimplementedDirective {
                 label: Some(OnUnimplementedFormatString::try_parse(
                     tcx, trait_def_id, value, attr.span)?),
                 note: None,
+                enclosing_scope: None,
             }))
         } else {
             return Err(ErrorReported);
@@ -174,6 +188,7 @@ impl<'tcx> OnUnimplementedDirective {
         let mut message = None;
         let mut label = None;
         let mut note = None;
+        let mut enclosing_scope = None;
         info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
 
         for command in self.subcommands.iter().chain(Some(self)).rev() {
@@ -202,6 +217,10 @@ impl<'tcx> OnUnimplementedDirective {
             if let Some(ref note_) = command.note {
                 note = Some(note_.clone());
             }
+
+            if let Some(ref enclosing_scope_) = command.enclosing_scope {
+                enclosing_scope = Some(enclosing_scope_.clone());
+            }
         }
 
         let options: FxHashMap<Symbol, String> = options.into_iter()
@@ -211,6 +230,7 @@ impl<'tcx> OnUnimplementedDirective {
             label: label.map(|l| l.format(tcx, trait_ref, &options)),
             message: message.map(|m| m.format(tcx, trait_ref, &options)),
             note: note.map(|n| n.format(tcx, trait_ref, &options)),
+            enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)),
         }
     }
 }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index b08a095beac..0fd7145f425 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1450,10 +1450,10 @@ impl KeywordIdents {
 
 impl EarlyLintPass for KeywordIdents {
     fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
-        self.check_tokens(cx, mac_def.stream());
+        self.check_tokens(cx, mac_def.body.inner_tokens());
     }
     fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) {
-        self.check_tokens(cx, mac.tts.clone().into());
+        self.check_tokens(cx, mac.args.inner_tokens());
     }
     fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) {
         self.check_ident_token(cx, UnderMacro(false), ident);
diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
index 8214153f153..13db9a6fef9 100644
--- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
+++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
@@ -32,6 +32,8 @@ use syntax::source_map;
 use syntax::source_map::Spanned;
 use syntax::symbol::Symbol;
 use syntax::expand::allocator::AllocatorKind;
+use syntax::ptr::P;
+use syntax::tokenstream::DelimSpan;
 use syntax_pos::{Span, FileName};
 
 macro_rules! provide {
@@ -427,6 +429,7 @@ impl CStore {
 
         let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
         let local_span = Span::with_root_ctxt(source_file.start_pos, source_file.end_pos);
+        let dspan = DelimSpan::from_single(local_span);
         let (body, mut errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
         emit_unclosed_delims(&mut errors, &sess.parse_sess);
 
@@ -448,7 +451,7 @@ impl CStore {
             span: local_span,
             attrs: attrs.iter().cloned().collect(),
             kind: ast::ItemKind::MacroDef(ast::MacroDef {
-                tokens: body.into(),
+                body: P(ast::MacArgs::Delimited(dspan, ast::MacDelimiter::Brace, body)),
                 legacy: def.legacy,
             }),
             vis: source_map::respan(local_span.shrink_to_lo(), ast::VisibilityKind::Inherited),
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index f2c5bf1bf6d..37a9381271a 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -425,16 +425,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
     }
 
     /// This computes `S(constructor, self)`. See top of the file for explanations.
-    fn specialize_constructor<'a, 'q>(
+    fn specialize_constructor(
         &self,
-        cx: &mut MatchCheckCtxt<'a, 'tcx>,
+        cx: &mut MatchCheckCtxt<'p, 'tcx>,
         constructor: &Constructor<'tcx>,
-        ctor_wild_subpatterns: &[&'q Pat<'tcx>],
-    ) -> Option<PatStack<'q, 'tcx>>
-    where
-        'a: 'q,
-        'p: 'q,
-    {
+        ctor_wild_subpatterns: &'p [Pat<'tcx>],
+    ) -> Option<PatStack<'p, 'tcx>> {
         let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns);
         new_heads.map(|mut new_head| {
             new_head.0.extend_from_slice(&self.0[1..]);
@@ -459,6 +455,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
 }
 
 /// A 2D matrix.
+#[derive(Clone)]
 pub struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>);
 
 impl<'p, 'tcx> Matrix<'p, 'tcx> {
@@ -486,16 +483,12 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
     }
 
     /// This computes `S(constructor, self)`. See top of the file for explanations.
-    fn specialize_constructor<'a, 'q>(
+    fn specialize_constructor(
         &self,
-        cx: &mut MatchCheckCtxt<'a, 'tcx>,
+        cx: &mut MatchCheckCtxt<'p, 'tcx>,
         constructor: &Constructor<'tcx>,
-        ctor_wild_subpatterns: &[&'q Pat<'tcx>],
-    ) -> Matrix<'q, 'tcx>
-    where
-        'a: 'q,
-        'p: 'q,
-    {
+        ctor_wild_subpatterns: &'p [Pat<'tcx>],
+    ) -> Matrix<'p, 'tcx> {
         self.0
             .iter()
             .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
@@ -1033,17 +1026,19 @@ impl<'tcx> Constructor<'tcx> {
 }
 
 #[derive(Clone, Debug)]
-pub enum Usefulness<'tcx> {
-    Useful,
+pub enum Usefulness<'tcx, 'p> {
+    /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns.
+    Useful(Vec<&'p Pat<'tcx>>),
+    /// Carries a list of witnesses of non-exhaustiveness.
     UsefulWithWitness(Vec<Witness<'tcx>>),
     NotUseful,
 }
 
-impl<'tcx> Usefulness<'tcx> {
+impl<'tcx, 'p> Usefulness<'tcx, 'p> {
     fn new_useful(preference: WitnessPreference) -> Self {
         match preference {
             ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
-            LeaveOutWitness => Useful,
+            LeaveOutWitness => Useful(vec![]),
         }
     }
 
@@ -1604,13 +1599,13 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> {
 /// relation to preceding patterns, it is not reachable) and exhaustiveness
 /// checking (if a wildcard pattern is useful in relation to a matrix, the
 /// matrix isn't exhaustive).
-pub fn is_useful<'p, 'a, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
+pub fn is_useful<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
     matrix: &Matrix<'p, 'tcx>,
-    v: &PatStack<'_, 'tcx>,
+    v: &PatStack<'p, 'tcx>,
     witness_preference: WitnessPreference,
     hir_id: HirId,
-) -> Usefulness<'tcx> {
+) -> Usefulness<'tcx, 'p> {
     let &Matrix(ref rows) = matrix;
     debug!("is_useful({:#?}, {:#?})", matrix, v);
 
@@ -1631,11 +1626,26 @@ pub fn is_useful<'p, 'a, 'tcx>(
 
     // If the first pattern is an or-pattern, expand it.
     if let Some(vs) = v.expand_or_pat() {
-        return vs
-            .into_iter()
-            .map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id))
-            .find(|result| result.is_useful())
-            .unwrap_or(NotUseful);
+        // We need to push the already-seen patterns into the matrix in order to detect redundant
+        // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns.
+        let mut matrix = matrix.clone();
+        let mut unreachable_pats = Vec::new();
+        let mut any_is_useful = false;
+        for v in vs {
+            let res = is_useful(cx, &matrix, &v, witness_preference, hir_id);
+            match res {
+                Useful(pats) => {
+                    any_is_useful = true;
+                    unreachable_pats.extend(pats);
+                }
+                NotUseful => unreachable_pats.push(v.head()),
+                UsefulWithWitness(_) => {
+                    bug!("Encountered or-pat in `v` during exhaustiveness checking")
+                }
+            }
+            matrix.push(v);
+        }
+        return if any_is_useful { Useful(unreachable_pats) } else { NotUseful };
     }
 
     let (ty, span) = matrix
@@ -1768,21 +1778,21 @@ pub fn is_useful<'p, 'a, 'tcx>(
 
 /// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied
 /// to the specialised version of both the pattern matrix `P` and the new pattern `q`.
-fn is_useful_specialized<'p, 'a, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
+fn is_useful_specialized<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
     matrix: &Matrix<'p, 'tcx>,
-    v: &PatStack<'_, 'tcx>,
+    v: &PatStack<'p, 'tcx>,
     ctor: Constructor<'tcx>,
     lty: Ty<'tcx>,
     witness_preference: WitnessPreference,
     hir_id: HirId,
-) -> Usefulness<'tcx> {
+) -> Usefulness<'tcx, 'p> {
     debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
 
-    let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty);
-    let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect();
-    let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns);
-    v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns)
+    let ctor_wild_subpatterns =
+        cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty));
+    let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns);
+    v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns)
         .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id))
         .map(|u| u.apply_constructor(cx, &ctor, lty))
         .unwrap_or(NotUseful)
@@ -2250,13 +2260,13 @@ fn constructor_covered_by_range<'tcx>(
     if intersects { Some(()) } else { None }
 }
 
-fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
+fn patterns_for_variant<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
     subpatterns: &'p [FieldPat<'tcx>],
-    ctor_wild_subpatterns: &[&'p Pat<'tcx>],
+    ctor_wild_subpatterns: &'p [Pat<'tcx>],
     is_non_exhaustive: bool,
 ) -> PatStack<'p, 'tcx> {
-    let mut result = SmallVec::from_slice(ctor_wild_subpatterns);
+    let mut result: SmallVec<_> = ctor_wild_subpatterns.iter().collect();
 
     for subpat in subpatterns {
         if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) {
@@ -2280,11 +2290,11 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>(
 /// different patterns.
 /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
 /// fields filled with wild patterns.
-fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
-    cx: &mut MatchCheckCtxt<'a, 'tcx>,
-    pat: &'q Pat<'tcx>,
+fn specialize_one_pattern<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
+    pat: &'p Pat<'tcx>,
     constructor: &Constructor<'tcx>,
-    ctor_wild_subpatterns: &[&'p Pat<'tcx>],
+    ctor_wild_subpatterns: &'p [Pat<'tcx>],
 ) -> Option<PatStack<'p, 'tcx>> {
     if let NonExhaustive = constructor {
         // Only a wildcard pattern can match the special extra constructor
@@ -2294,9 +2304,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
     let result = match *pat.kind {
         PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
 
-        PatKind::Binding { .. } | PatKind::Wild => {
-            Some(PatStack::from_slice(ctor_wild_subpatterns))
-        }
+        PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()),
 
         PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
             let ref variant = adt_def.variants[variant_index];
@@ -2406,7 +2414,6 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>(
                                 .chain(
                                     ctor_wild_subpatterns
                                         .iter()
-                                        .map(|p| *p)
                                         .skip(prefix.len())
                                         .take(slice_count)
                                         .chain(suffix.iter()),
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index 30586e95acf..737af3e1358 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -139,39 +139,22 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
             let mut have_errors = false;
 
-            let inlined_arms: Vec<(Vec<_>, _)> = arms
+            let inlined_arms: Vec<_> = arms
                 .iter()
                 .map(|arm| {
-                    (
-                        // HACK(or_patterns; Centril | dlrobertson): Remove this and
-                        // correctly handle exhaustiveness checking for nested or-patterns.
-                        match &arm.pat.kind {
-                            hir::PatKind::Or(pats) => pats,
-                            _ => std::slice::from_ref(&arm.pat),
-                        }
-                        .iter()
-                        .map(|pat| {
-                            let mut patcx = PatCtxt::new(
-                                self.tcx,
-                                self.param_env.and(self.identity_substs),
-                                self.tables,
-                            );
-                            patcx.include_lint_checks();
-                            let pattern = cx
-                                .pattern_arena
-                                .alloc(expand_pattern(cx, patcx.lower_pattern(&pat)))
-                                as &_;
-                            if !patcx.errors.is_empty() {
-                                patcx.report_inlining_errors(pat.span);
-                                have_errors = true;
-                            }
-                            (pattern, &**pat)
-                        })
-                        .collect(),
-                        arm.guard.as_ref().map(|g| match g {
-                            hir::Guard::If(ref e) => &**e,
-                        }),
-                    )
+                    let mut patcx = PatCtxt::new(
+                        self.tcx,
+                        self.param_env.and(self.identity_substs),
+                        self.tables,
+                    );
+                    patcx.include_lint_checks();
+                    let pattern: &_ =
+                        cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&arm.pat)));
+                    if !patcx.errors.is_empty() {
+                        patcx.report_inlining_errors(arm.pat.span);
+                        have_errors = true;
+                    }
+                    (pattern, &*arm.pat, arm.guard.is_some())
                 })
                 .collect();
 
@@ -181,7 +164,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
             }
 
             // Fourth, check for unreachable arms.
-            check_arms(cx, &inlined_arms, source);
+            let matrix = check_arms(cx, &inlined_arms, source);
 
             // Then, if the match has no arms, check whether the scrutinee
             // is uninhabited.
@@ -248,12 +231,6 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
                 return;
             }
 
-            let matrix: Matrix<'_, '_> = inlined_arms
-                .iter()
-                .filter(|&&(_, guard)| guard.is_none())
-                .flat_map(|arm| &arm.0)
-                .map(|pat| PatStack::from_pattern(pat.0))
-                .collect();
             let scrut_ty = self.tables.node_type(scrut.hir_id);
             check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id);
         })
@@ -267,8 +244,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
             patcx.include_lint_checks();
             let pattern = patcx.lower_pattern(pat);
             let pattern_ty = pattern.ty;
-            let pattern = expand_pattern(cx, pattern);
-            let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(&pattern)].into_iter().collect();
+            let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+            let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
 
             let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
                 Ok(_) => return,
@@ -403,113 +380,120 @@ fn pat_is_catchall(pat: &Pat) -> bool {
 }
 
 // Check for unreachable patterns
-fn check_arms<'tcx>(
-    cx: &mut MatchCheckCtxt<'_, 'tcx>,
-    arms: &[(Vec<(&super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)],
+fn check_arms<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
+    arms: &[(&'p super::Pat<'tcx>, &hir::Pat, bool)],
     source: hir::MatchSource,
-) {
+) -> Matrix<'p, 'tcx> {
     let mut seen = Matrix::empty();
     let mut catchall = None;
-    for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() {
-        for &(pat, hir_pat) in pats {
-            let v = PatStack::from_pattern(pat);
-
-            match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) {
-                NotUseful => {
-                    match source {
-                        hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => {
-                            bug!()
-                        }
-                        hir::MatchSource::IfLetDesugar { .. } => {
-                            cx.tcx.lint_hir(
-                                lint::builtin::IRREFUTABLE_LET_PATTERNS,
-                                hir_pat.hir_id,
-                                pat.span,
-                                "irrefutable if-let pattern",
-                            );
-                        }
-
-                        hir::MatchSource::WhileLetDesugar => {
-                            // check which arm we're on.
-                            match arm_index {
-                                // The arm with the user-specified pattern.
-                                0 => {
-                                    cx.tcx.lint_hir(
-                                        lint::builtin::UNREACHABLE_PATTERNS,
-                                        hir_pat.hir_id,
-                                        pat.span,
-                                        "unreachable pattern",
-                                    );
-                                }
-                                // The arm with the wildcard pattern.
-                                1 => {
-                                    cx.tcx.lint_hir(
-                                        lint::builtin::IRREFUTABLE_LET_PATTERNS,
-                                        hir_pat.hir_id,
-                                        pat.span,
-                                        "irrefutable while-let pattern",
-                                    );
-                                }
-                                _ => bug!(),
+    for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
+        let v = PatStack::from_pattern(pat);
+
+        match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) {
+            NotUseful => {
+                match source {
+                    hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
+
+                    hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
+                        // check which arm we're on.
+                        match arm_index {
+                            // The arm with the user-specified pattern.
+                            0 => {
+                                cx.tcx.lint_hir(
+                                    lint::builtin::UNREACHABLE_PATTERNS,
+                                    hir_pat.hir_id,
+                                    pat.span,
+                                    "unreachable pattern",
+                                );
                             }
-                        }
-
-                        hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
-                            let mut err = cx.tcx.struct_span_lint_hir(
-                                lint::builtin::UNREACHABLE_PATTERNS,
-                                hir_pat.hir_id,
-                                pat.span,
-                                "unreachable pattern",
-                            );
-                            // if we had a catchall pattern, hint at that
-                            if let Some(catchall) = catchall {
-                                err.span_label(pat.span, "unreachable pattern");
-                                err.span_label(catchall, "matches any value");
+                            // The arm with the wildcard pattern.
+                            1 => {
+                                let msg = match source {
+                                    hir::MatchSource::IfLetDesugar { .. } => {
+                                        "irrefutable if-let pattern"
+                                    }
+                                    hir::MatchSource::WhileLetDesugar => {
+                                        "irrefutable while-let pattern"
+                                    }
+                                    _ => bug!(),
+                                };
+                                cx.tcx.lint_hir(
+                                    lint::builtin::IRREFUTABLE_LET_PATTERNS,
+                                    hir_pat.hir_id,
+                                    pat.span,
+                                    msg,
+                                );
                             }
-                            err.emit();
+                            _ => bug!(),
                         }
+                    }
 
-                        // Unreachable patterns in try and await expressions occur when one of
-                        // the arms are an uninhabited type. Which is OK.
-                        hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+                    hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+                        let mut err = cx.tcx.struct_span_lint_hir(
+                            lint::builtin::UNREACHABLE_PATTERNS,
+                            hir_pat.hir_id,
+                            pat.span,
+                            "unreachable pattern",
+                        );
+                        // if we had a catchall pattern, hint at that
+                        if let Some(catchall) = catchall {
+                            err.span_label(pat.span, "unreachable pattern");
+                            err.span_label(catchall, "matches any value");
+                        }
+                        err.emit();
                     }
+
+                    // Unreachable patterns in try and await expressions occur when one of
+                    // the arms are an uninhabited type. Which is OK.
+                    hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
                 }
-                Useful => (),
-                UsefulWithWitness(_) => bug!(),
             }
-            if guard.is_none() {
-                seen.push(v);
-                if catchall.is_none() && pat_is_catchall(hir_pat) {
-                    catchall = Some(pat.span);
+            Useful(unreachable_subpatterns) => {
+                for pat in unreachable_subpatterns {
+                    cx.tcx.lint_hir(
+                        lint::builtin::UNREACHABLE_PATTERNS,
+                        hir_pat.hir_id,
+                        pat.span,
+                        "unreachable pattern",
+                    );
                 }
             }
+            UsefulWithWitness(_) => bug!(),
+        }
+        if !has_guard {
+            seen.push(v);
+            if catchall.is_none() && pat_is_catchall(hir_pat) {
+                catchall = Some(pat.span);
+            }
         }
     }
+    seen
 }
 
-fn check_not_useful(
-    cx: &mut MatchCheckCtxt<'_, 'tcx>,
+fn check_not_useful<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
     ty: Ty<'tcx>,
-    matrix: &Matrix<'_, 'tcx>,
+    matrix: &Matrix<'p, 'tcx>,
     hir_id: HirId,
 ) -> Result<(), Vec<super::Pat<'tcx>>> {
-    let wild_pattern = super::Pat::wildcard_from_ty(ty);
-    match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness, hir_id) {
+    let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
+    match is_useful(cx, matrix, &PatStack::from_pattern(wild_pattern), ConstructWitness, hir_id) {
         NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
         UsefulWithWitness(pats) => Err(if pats.is_empty() {
-            vec![wild_pattern]
+            bug!("Exhaustiveness check returned no witnesses")
         } else {
             pats.into_iter().map(|w| w.single_pattern()).collect()
         }),
-        Useful => bug!(),
+        Useful(_) => bug!(),
     }
 }
 
-fn check_exhaustive<'tcx>(
-    cx: &mut MatchCheckCtxt<'_, 'tcx>,
+fn check_exhaustive<'p, 'tcx>(
+    cx: &mut MatchCheckCtxt<'p, 'tcx>,
     scrut_ty: Ty<'tcx>,
     sp: Span,
-    matrix: &Matrix<'_, 'tcx>,
+    matrix: &Matrix<'p, 'tcx>,
     hir_id: HirId,
 ) {
     let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs
index 118dfcb3d9a..75fc6b389e8 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -424,13 +424,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 {
             // Then, check if `b` is -1, which is the "min_value / -1" case.
             let minus1 = Scalar::from_int(-1, dest.layout.size);
-            let b = b.to_scalar().unwrap();
-            if b == minus1 {
+            let b_scalar = b.to_scalar().unwrap();
+            if b_scalar == minus1 {
                 throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
             } else {
                 throw_ub_format!(
                     "exact_div: {} cannot be divided by {} without remainder",
-                    a.to_scalar().unwrap(),
+                    a,
                     b,
                 )
             }
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 9e94ae2c160..48e7193ec39 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -19,6 +19,7 @@ use super::{
 };
 pub use rustc::mir::interpret::ScalarMaybeUndef;
 use rustc_macros::HashStable;
+use syntax::ast;
 
 /// An `Immediate` represents a single immediate self-contained Rust value.
 ///
@@ -100,6 +101,42 @@ pub struct ImmTy<'tcx, Tag=()> {
     pub layout: TyLayout<'tcx>,
 }
 
+// `Tag: Copy` because some methods on `Scalar` consume them by value
+impl<Tag: Copy> std::fmt::Display for ImmTy<'tcx, Tag> {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match &self.imm {
+            Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) => match s.to_bits(self.layout.size) {
+                Ok(s) => {
+                    match self.layout.ty.kind {
+                        ty::Int(_) => return write!(
+                            fmt, "{}",
+                            super::sign_extend(s, self.layout.size) as i128,
+                        ),
+                        ty::Uint(_) => return write!(fmt, "{}", s),
+                        ty::Bool if s == 0 => return fmt.write_str("false"),
+                        ty::Bool if s == 1 => return fmt.write_str("true"),
+                        ty::Char => if let Some(c) =
+                            u32::try_from(s).ok().and_then(std::char::from_u32) {
+                            return write!(fmt, "{}", c);
+                        },
+                        ty::Float(ast::FloatTy::F32) => if let Ok(u) = u32::try_from(s) {
+                            return write!(fmt, "{}", f32::from_bits(u));
+                        },
+                        ty::Float(ast::FloatTy::F64) => if let Ok(u) = u64::try_from(s) {
+                            return write!(fmt, "{}", f64::from_bits(u));
+                        },
+                        _ => {},
+                    }
+                    write!(fmt, "{:x}", s)
+                },
+                Err(_) => fmt.write_str("{pointer}"),
+            },
+            Immediate::Scalar(ScalarMaybeUndef::Undef) => fmt.write_str("{undef}"),
+            Immediate::ScalarPair(..) => fmt.write_str("{wide pointer or tuple}"),
+        }
+    }
+}
+
 impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> {
     type Target = Immediate<Tag>;
     #[inline(always)]
diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs
index 26e51e83d62..1bf6e9ecbc0 100644
--- a/src/librustc_parse/config.rs
+++ b/src/librustc_parse/config.rs
@@ -101,7 +101,7 @@ impl<'a> StripUnconfigured<'a> {
         if !attr.has_name(sym::cfg_attr) {
             return vec![attr];
         }
-        if attr.get_normal_item().tokens.is_empty() {
+        if let ast::MacArgs::Empty = attr.get_normal_item().args {
             self.sess.span_diagnostic
                 .struct_span_err(
                     attr.span,
diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs
index 1215c7a199a..a22b383e5f3 100644
--- a/src/librustc_parse/lib.rs
+++ b/src/librustc_parse/lib.rs
@@ -277,7 +277,9 @@ pub fn parse_in_attr<'a, T>(
 ) -> PResult<'a, T> {
     let mut parser = Parser::new(
         sess,
-        attr.get_normal_item().tokens.clone(),
+        // FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
+        // require reconstructing and immediately re-parsing delimiters.
+        attr.get_normal_item().args.outer_tokens(),
         None,
         false,
         false,
@@ -409,7 +411,7 @@ fn prepend_attrs(
             brackets.push(stream);
         }
 
-        brackets.push(item.tokens.clone());
+        brackets.push(item.args.outer_tokens());
 
         // The span we list here for `#` and for `[ ... ]` are both wrong in
         // that it encompasses more than each token, but it hopefully is "good
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index 524b551e54c..c7261404f54 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -2,8 +2,7 @@ use super::{SeqSep, Parser, TokenType, PathStyle};
 use syntax::attr;
 use syntax::ast;
 use syntax::util::comments;
-use syntax::token::{self, Nonterminal, DelimToken};
-use syntax::tokenstream::{TokenStream, TokenTree};
+use syntax::token::{self, Nonterminal};
 use syntax_pos::{Span, Symbol};
 use errors::PResult;
 
@@ -181,31 +180,8 @@ impl<'a> Parser<'a> {
             item
         } else {
             let path = self.parse_path(PathStyle::Mod)?;
-            let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
-               self.check(&token::OpenDelim(DelimToken::Bracket)) ||
-               self.check(&token::OpenDelim(DelimToken::Brace)) {
-                   self.parse_token_tree().into()
-            } else if self.eat(&token::Eq) {
-                let eq = TokenTree::token(token::Eq, self.prev_span);
-                let mut is_interpolated_expr = false;
-                if let token::Interpolated(nt) = &self.token.kind {
-                    if let token::NtExpr(..) = **nt {
-                        is_interpolated_expr = true;
-                    }
-                }
-                let token_tree = if is_interpolated_expr {
-                    // We need to accept arbitrary interpolated expressions to continue
-                    // supporting things like `doc = $expr` that work on stable.
-                    // Non-literal interpolated expressions are rejected after expansion.
-                    self.parse_token_tree()
-                } else {
-                    self.parse_unsuffixed_lit()?.token_tree()
-                };
-                TokenStream::new(vec![eq.into(), token_tree.into()])
-            } else {
-                TokenStream::default()
-            };
-            ast::AttrItem { path, tokens }
+            let args = self.parse_attr_args()?;
+            ast::AttrItem { path, args }
         })
     }
 
@@ -244,7 +220,7 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
-    fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
+    pub(super) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
         let lit = self.parse_lit()?;
         debug!("checking if {:?} is unusuffixed", lit);
 
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index 43c740f7f93..1112274dc46 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -922,13 +922,11 @@ impl<'a> Parser<'a> {
                     // `!`, as an operator, is prefix, so we know this isn't that.
                     if self.eat(&token::Not) {
                         // MACRO INVOCATION expression
-                        let (delim, tts) = self.expect_delimited_token_tree()?;
+                        let args = self.parse_mac_args()?;
                         hi = self.prev_span;
                         ex = ExprKind::Mac(Mac {
                             path,
-                            tts,
-                            delim,
-                            span: lo.to(hi),
+                            args,
                             prior_type_ascription: self.last_type_ascription,
                         });
                     } else if self.check(&token::OpenDelim(token::Brace)) {
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index a0669a2a174..46addba57c6 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -8,12 +8,12 @@ use syntax::ast::{ItemKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind, Us
 use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit};
 use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind};
 use syntax::ast::{Ty, TyKind, Generics, TraitRef, EnumDef, Variant, VariantData, StructField};
-use syntax::ast::{Mac, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
+use syntax::ast::{Mac, MacArgs, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
 use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::ThinVec;
 use syntax::token;
-use syntax::tokenstream::{TokenTree, TokenStream};
+use syntax::tokenstream::{DelimSpan, TokenTree, TokenStream};
 use syntax::source_map::{self, respan, Span};
 use syntax::struct_span_err;
 use syntax_pos::BytePos;
@@ -432,22 +432,18 @@ impl<'a> Parser<'a> {
             let prev_span = self.prev_span;
             self.complain_if_pub_macro(&visibility.node, prev_span);
 
-            let mac_lo = self.token.span;
-
             // Item macro
             let path = self.parse_path(PathStyle::Mod)?;
             self.expect(&token::Not)?;
-            let (delim, tts) = self.expect_delimited_token_tree()?;
-            if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
+            let args = self.parse_mac_args()?;
+            if args.need_semicolon() && !self.eat(&token::Semi) {
                 self.report_invalid_macro_expansion_item();
             }
 
             let hi = self.prev_span;
             let mac = Mac {
                 path,
-                tts,
-                delim,
-                span: mac_lo.to(hi),
+                args,
                 prior_type_ascription: self.last_type_ascription,
             };
             let item =
@@ -500,7 +496,6 @@ impl<'a> Parser<'a> {
         if self.token.is_path_start() &&
                 !(self.is_async_fn() && self.token.span.rust_2015()) {
             let prev_span = self.prev_span;
-            let lo = self.token.span;
             let path = self.parse_path(PathStyle::Mod)?;
 
             if path.segments.len() == 1 {
@@ -518,16 +513,14 @@ impl<'a> Parser<'a> {
             *at_end = true;
 
             // eat a matched-delimiter token tree:
-            let (delim, tts) = self.expect_delimited_token_tree()?;
-            if delim != MacDelimiter::Brace {
+            let args = self.parse_mac_args()?;
+            if args.need_semicolon() {
                 self.expect_semi()?;
             }
 
             Ok(Some(Mac {
                 path,
-                tts,
-                delim,
-                span: lo.to(self.prev_span),
+                args,
                 prior_type_ascription: self.last_type_ascription,
             }))
         } else {
@@ -1624,33 +1617,31 @@ impl<'a> Parser<'a> {
         vis: &Visibility,
         lo: Span
     ) -> PResult<'a, Option<P<Item>>> {
-        let token_lo = self.token.span;
         let (ident, def) = if self.eat_keyword(kw::Macro) {
             let ident = self.parse_ident()?;
-            let tokens = if self.check(&token::OpenDelim(token::Brace)) {
-                match self.parse_token_tree() {
-                    TokenTree::Delimited(_, _, tts) => tts,
-                    _ => unreachable!(),
-                }
+            let body = if self.check(&token::OpenDelim(token::Brace)) {
+                self.parse_mac_args()?
             } else if self.check(&token::OpenDelim(token::Paren)) {
-                let args = self.parse_token_tree();
+                let params = self.parse_token_tree();
+                let pspan = params.span();
                 let body = if self.check(&token::OpenDelim(token::Brace)) {
                     self.parse_token_tree()
                 } else {
-                    self.unexpected()?;
-                    unreachable!()
+                    return self.unexpected();
                 };
-                TokenStream::new(vec![
-                    args.into(),
-                    TokenTree::token(token::FatArrow, token_lo.to(self.prev_span)).into(),
+                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 {
-                self.unexpected()?;
-                unreachable!()
+                return self.unexpected();
             };
 
-            (ident, ast::MacroDef { tokens: tokens.into(), legacy: false })
+            (ident, ast::MacroDef { body, legacy: false })
         } else if self.check_keyword(sym::macro_rules) &&
                   self.look_ahead(1, |t| *t == token::Not) &&
                   self.look_ahead(2, |t| t.is_ident()) {
@@ -1660,12 +1651,12 @@ impl<'a> Parser<'a> {
             self.bump();
 
             let ident = self.parse_ident()?;
-            let (delim, tokens) = self.expect_delimited_token_tree()?;
-            if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
+            let body = self.parse_mac_args()?;
+            if body.need_semicolon() && !self.eat(&token::Semi) {
                 self.report_invalid_macro_expansion_item();
             }
 
-            (ident, ast::MacroDef { tokens, legacy: true })
+            (ident, ast::MacroDef { body, legacy: true })
         } else {
             return Ok(None);
         };
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index ea7673767d0..28689720044 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -16,7 +16,7 @@ use crate::lexer::UnmatchedBrace;
 
 use syntax::ast::{
     self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit,
-    IsAsync, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
+    IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
 };
 
 use syntax::print::pprust;
@@ -1010,27 +1010,49 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStream)> {
-        let delim = match self.token.kind {
-            token::OpenDelim(delim) => delim,
-            _ => {
-                let msg = "expected open delimiter";
-                let mut err = self.fatal(msg);
-                err.span_label(self.token.span, msg);
-                return Err(err)
+    fn parse_mac_args(&mut self) -> PResult<'a, P<MacArgs>> {
+        self.parse_mac_args_common(true).map(P)
+    }
+
+    fn parse_attr_args(&mut self) -> PResult<'a, MacArgs> {
+        self.parse_mac_args_common(false)
+    }
+
+    fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs> {
+        Ok(if self.check(&token::OpenDelim(DelimToken::Paren)) ||
+                       self.check(&token::OpenDelim(DelimToken::Bracket)) ||
+                       self.check(&token::OpenDelim(DelimToken::Brace)) {
+            match self.parse_token_tree() {
+                TokenTree::Delimited(dspan, delim, tokens) =>
+                    // We've confirmed above that there is a delimiter so unwrapping is OK.
+                    MacArgs::Delimited(dspan, MacDelimiter::from_token(delim).unwrap(), tokens),
+                _ => unreachable!(),
             }
-        };
-        let tts = match self.parse_token_tree() {
-            TokenTree::Delimited(_, _, tts) => tts,
-            _ => unreachable!(),
-        };
-        let delim = match delim {
-            token::Paren => MacDelimiter::Parenthesis,
-            token::Bracket => MacDelimiter::Bracket,
-            token::Brace => MacDelimiter::Brace,
-            token::NoDelim => self.bug("unexpected no delimiter"),
-        };
-        Ok((delim, tts.into()))
+        } else if !delimited_only {
+            if self.eat(&token::Eq) {
+                let eq_span = self.prev_span;
+                let mut is_interpolated_expr = false;
+                if let token::Interpolated(nt) = &self.token.kind {
+                    if let token::NtExpr(..) = **nt {
+                        is_interpolated_expr = true;
+                    }
+                }
+                let token_tree = if is_interpolated_expr {
+                    // We need to accept arbitrary interpolated expressions to continue
+                    // supporting things like `doc = $expr` that work on stable.
+                    // Non-literal interpolated expressions are rejected after expansion.
+                    self.parse_token_tree()
+                } else {
+                    self.parse_unsuffixed_lit()?.token_tree()
+                };
+
+                MacArgs::Eq(eq_span, token_tree.into())
+            } else {
+                MacArgs::Empty
+            }
+        } else {
+            return self.unexpected();
+        })
     }
 
     fn parse_or_use_outer_attributes(
diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs
index b068a4f16a5..1127c4b2d5f 100644
--- a/src/librustc_parse/parser/pat.rs
+++ b/src/librustc_parse/parser/pat.rs
@@ -338,7 +338,7 @@ impl<'a> Parser<'a> {
                     (None, self.parse_path(PathStyle::Expr)?)
                 };
                 match self.token.kind {
-                    token::Not if qself.is_none() => self.parse_pat_mac_invoc(lo, path)?,
+                    token::Not if qself.is_none() => self.parse_pat_mac_invoc(path)?,
                     token::DotDotDot | token::DotDotEq | token::DotDot => {
                         self.parse_pat_range_starting_with_path(lo, qself, path)?
                     }
@@ -593,14 +593,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse macro invocation
-    fn parse_pat_mac_invoc(&mut self, lo: Span, path: Path) -> PResult<'a, PatKind> {
+    fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
         self.bump();
-        let (delim, tts) = self.expect_delimited_token_tree()?;
+        let args = self.parse_mac_args()?;
         let mac = Mac {
             path,
-            tts,
-            delim,
-            span: lo.to(self.prev_span),
+            args,
             prior_type_ascription: self.last_type_ascription,
         };
         Ok(PatKind::Mac(mac))
diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs
index 68307440712..75bb67d47bc 100644
--- a/src/librustc_parse/parser/path.rs
+++ b/src/librustc_parse/parser/path.rs
@@ -2,6 +2,7 @@ use super::{Parser, TokenType};
 use crate::maybe_whole;
 use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
 use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
+use syntax::ast::MacArgs;
 use syntax::ThinVec;
 use syntax::token::{self, Token};
 use syntax::source_map::{Span, BytePos};
@@ -114,9 +115,9 @@ impl<'a> Parser<'a> {
     fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
         let meta_ident = match self.token.kind {
             token::Interpolated(ref nt) => match **nt {
-                token::NtMeta(ref item) => match item.tokens.is_empty() {
-                    true => Some(item.path.clone()),
-                    false => None,
+                token::NtMeta(ref item) => match item.args {
+                    MacArgs::Empty => Some(item.path.clone()),
+                    _ => None,
                 },
                 _ => None,
             },
diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs
index a5f20691d07..b952e8814a3 100644
--- a/src/librustc_parse/parser/stmt.rs
+++ b/src/librustc_parse/parser/stmt.rs
@@ -10,7 +10,7 @@ use syntax::ThinVec;
 use syntax::ptr::P;
 use syntax::ast;
 use syntax::ast::{DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind};
-use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter};
+use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac};
 use syntax::util::classify;
 use syntax::token;
 use syntax::source_map::{respan, Span};
@@ -93,10 +93,11 @@ impl<'a> Parser<'a> {
                 }));
             }
 
-            let (delim, tts) = self.expect_delimited_token_tree()?;
+            let args = self.parse_mac_args()?;
+            let delim = args.delim();
             let hi = self.prev_span;
 
-            let style = if delim == MacDelimiter::Brace {
+            let style = if delim == token::Brace {
                 MacStmtStyle::Braces
             } else {
                 MacStmtStyle::NoBraces
@@ -104,12 +105,10 @@ impl<'a> Parser<'a> {
 
             let mac = Mac {
                 path,
-                tts,
-                delim,
-                span: lo.to(hi),
+                args,
                 prior_type_ascription: self.last_type_ascription,
             };
-            let kind = if delim == MacDelimiter::Brace ||
+            let kind = if delim == token::Brace ||
                           self.token == token::Semi || self.token == token::Eof {
                 StmtKind::Mac(P((mac, style, attrs.into())))
             }
@@ -130,7 +129,7 @@ impl<'a> Parser<'a> {
                 self.warn_missing_semicolon();
                 StmtKind::Mac(P((mac, style, attrs.into())))
             } else {
-                let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
+                let e = self.mk_expr(lo.to(hi), ExprKind::Mac(mac), ThinVec::new());
                 let e = self.maybe_recover_from_bad_qpath(e, true)?;
                 let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
                 let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs
index 8e6bc29be52..32142796905 100644
--- a/src/librustc_parse/parser/ty.rs
+++ b/src/librustc_parse/parser/ty.rs
@@ -177,12 +177,10 @@ impl<'a> Parser<'a> {
             let path = self.parse_path(PathStyle::Type)?;
             if self.eat(&token::Not) {
                 // Macro invocation in type position
-                let (delim, tts) = self.expect_delimited_token_tree()?;
+                let args = self.parse_mac_args()?;
                 let mac = Mac {
                     path,
-                    tts,
-                    delim,
-                    span: lo.to(self.prev_span),
+                    args,
                     prior_type_ascription: self.last_type_ascription,
                 };
                 TyKind::Mac(mac)
diff --git a/src/librustc_parse/validate_attr.rs b/src/librustc_parse/validate_attr.rs
index a3c9e266593..0fb348efece 100644
--- a/src/librustc_parse/validate_attr.rs
+++ b/src/librustc_parse/validate_attr.rs
@@ -2,11 +2,9 @@
 
 use errors::{PResult, Applicability};
 use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
-use syntax::ast::{self, Attribute, AttrKind, Ident, MetaItem, MetaItemKind};
+use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
 use syntax::attr::mk_name_value_item_str;
 use syntax::early_buffered_lints::BufferedEarlyLintId;
-use syntax::token;
-use syntax::tokenstream::TokenTree;
 use syntax::sess::ParseSess;
 use syntax_pos::{Symbol, sym};
 
@@ -19,11 +17,9 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
         // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
         Some((name, _, template, _)) if name != sym::rustc_dummy =>
             check_builtin_attribute(sess, attr, name, template),
-        _ => if let Some(TokenTree::Token(token)) = attr.get_normal_item().tokens.trees().next() {
-            if token == token::Eq {
-                // All key-value attributes are restricted to meta-item syntax.
-                parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
-            }
+        _ => if let MacArgs::Eq(..) = attr.get_normal_item().args {
+            // All key-value attributes are restricted to meta-item syntax.
+            parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
         }
     }
 }
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 5a29a56ad54..29cfee8408f 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -737,14 +737,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             |this| visit::walk_enum_def(this, enum_definition, generics, item_id))
     }
 
-    fn visit_mac(&mut self, mac: &Mac) {
-        // when a new macro kind is added but the author forgets to set it up for expansion
-        // because that's the only part that won't cause a compiler error
-        self.session.diagnostic()
-            .span_bug(mac.span, "macro invocation missed in expansion; did you forget to override \
-                                 the relevant `fold_*()` method in `PlaceholderExpander`?");
-    }
-
     fn visit_impl_item(&mut self, ii: &'a ImplItem) {
         if let ImplItemKind::Method(ref sig, _) = ii.kind {
             self.check_fn_decl(&sig.decl);
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index 5bec5b5eb6b..396d9484339 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -1515,14 +1515,6 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
         }
     }
 
-    fn visit_mac(&mut self, mac: &'l ast::Mac) {
-        // These shouldn't exist in the AST at this point, log a span bug.
-        span_bug!(
-            mac.span,
-            "macro invocation should have been expanded out of AST"
-        );
-    }
-
     fn visit_pat(&mut self, p: &'l ast::Pat) {
         self.process_macro_use(p.span);
         self.process_pat(p);
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4b5fc7c2a1e..7ee1054dc48 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -482,7 +482,7 @@ fn build_macro(cx: &DocContext<'_>, did: DefId, name: ast::Name) -> clean::ItemE
     match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
         LoadedMacro::MacroDef(def, _) => {
             let matchers: hir::HirVec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.kind {
-                let tts: Vec<_> = def.stream().into_trees().collect();
+                let tts: Vec<_> = def.body.inner_tokens().into_trees().collect();
                 tts.chunks(4).map(|arm| arm[0].span()).collect()
             } else {
                 unreachable!()
diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs
index b0baf36308e..5b7bef930d1 100644
--- a/src/libstd/keyword_docs.rs
+++ b/src/libstd/keyword_docs.rs
@@ -812,9 +812,50 @@ mod loop_keyword { }
 //
 /// Control flow based on pattern matching.
 ///
-/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
+/// `match` can be used to run code conditionally. Every pattern must
+/// be handled exhaustively either explicitly or by using wildcards like
+/// `_` in the `match`. Since `match` is an expression, values can also be
+/// returned.
 ///
-/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
+/// ```rust
+/// let opt = Option::None::<usize>;
+/// let x = match opt {
+///     Some(int) => int,
+///     None => 10,
+/// };
+/// assert_eq!(x, 10);
+///
+/// let a_number = Option::Some(10);
+/// match a_number {
+///     Some(x) if x <= 5 => println!("0 to 5 num = {}", x),
+///     Some(x @ 6..=10) => println!("6 to 10 num = {}", x),
+///     None => panic!(),
+///     // all other numbers
+///     _ => panic!(),
+/// }
+/// ```
+///
+/// `match` can be used to gain access to the inner members of an enum
+/// and use them directly.
+///
+/// ```rust
+/// enum Outer {
+///     Double(Option<u8>, Option<String>),
+///     Single(Option<u8>),
+///     Empty
+/// }
+///
+/// let get_inner = Outer::Double(None, Some(String::new()));
+/// match get_inner {
+///     Outer::Double(None, Some(st)) => println!("{}", st),
+///     Outer::Single(opt) => println!("{:?}", opt),
+///     _ => panic!(),
+/// }
+/// ```
+///
+/// For more information on `match` and matching in general, see the [Reference].
+///
+/// [Reference]: ../reference/expressions/match-expr.html
 mod match_keyword { }
 
 #[doc(keyword = "mod")]
@@ -831,10 +872,35 @@ mod mod_keyword { }
 //
 /// Capture a [closure]'s environment by value.
 ///
-/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
+/// `move` converts any variables captured by reference or mutable reference
+/// to owned by value variables. The three [`Fn` trait]'s mirror the ways to capture
+/// variables, when `move` is used, the closures is represented by the `FnOnce` trait.
 ///
-/// [closure]: ../book/second-edition/ch13-01-closures.html
-/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
+/// ```rust
+/// let capture = "hello";
+/// let closure = move || {
+///     println!("rust says {}", capture);
+/// };
+/// ```
+///
+/// `move` is often used when [threads] are involved.
+///
+/// ```rust
+/// let x = 5;
+///
+/// std::thread::spawn(move || {
+///     println!("captured {} by value", x)
+/// }).join().unwrap();
+///
+/// // x is no longer available
+/// ```
+///
+/// For more information on the `move` keyword, see the [closure]'s section
+/// of the Rust book or the [threads] section
+///
+/// [`Fn` trait]: ../std/ops/trait.Fn.html
+/// [closure]: ../book/ch13-01-closures.html
+/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
 mod move_keyword { }
 
 #[doc(keyword = "mut")]
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 512f43c86ca..8018e005b12 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -27,7 +27,7 @@ pub use syntax_pos::symbol::{Ident, Symbol as Name};
 use crate::ptr::P;
 use crate::source_map::{dummy_spanned, respan, Spanned};
 use crate::token::{self, DelimToken};
-use crate::tokenstream::TokenStream;
+use crate::tokenstream::{TokenStream, TokenTree, DelimSpan};
 
 use syntax_pos::symbol::{kw, sym, Symbol};
 use syntax_pos::{Span, DUMMY_SP, ExpnId};
@@ -40,6 +40,7 @@ use rustc_index::vec::Idx;
 use rustc_serialize::{self, Decoder, Encoder};
 use rustc_macros::HashStable_Generic;
 
+use std::iter;
 use std::fmt;
 
 #[cfg(test)]
@@ -1372,34 +1373,89 @@ pub enum Movability {
     Movable,
 }
 
-/// Represents a macro invocation. The `Path` indicates which macro
-/// is being invoked, and the vector of token-trees contains the source
-/// of the macro invocation.
-///
-/// N.B., the additional ident for a `macro_rules`-style macro is actually
-/// stored in the enclosing item.
+/// Represents a macro invocation. The `path` indicates which macro
+/// is being invoked, and the `args` are arguments passed to it.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Mac {
     pub path: Path,
-    pub delim: MacDelimiter,
-    pub tts: TokenStream,
-    pub span: Span,
+    pub args: P<MacArgs>,
     pub prior_type_ascription: Option<(Span, bool)>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
+impl Mac {
+    pub fn span(&self) -> Span {
+        self.path.span.to(self.args.span().unwrap_or(self.path.span))
+    }
+}
+
+/// Arguments passed to an attribute or a function-like macro.
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+pub enum MacArgs {
+    /// No arguments - `#[attr]`.
+    Empty,
+    /// Delimited arguments - `#[attr()/[]/{}]` or `mac!()/[]/{}`.
+    Delimited(DelimSpan, MacDelimiter, TokenStream),
+    /// Arguments of a key-value attribute - `#[attr = "value"]`.
+    Eq(
+        /// Span of the `=` token.
+        Span,
+        /// Token stream of the "value".
+        TokenStream,
+    ),
+}
+
+impl MacArgs {
+    pub fn delim(&self) -> DelimToken {
+        match self {
+            MacArgs::Delimited(_, delim, _) => delim.to_token(),
+            MacArgs::Empty | MacArgs::Eq(..) => token::NoDelim,
+        }
+    }
+
+    pub fn span(&self) -> Option<Span> {
+        match *self {
+            MacArgs::Empty => None,
+            MacArgs::Delimited(dspan, ..) => Some(dspan.entire()),
+            MacArgs::Eq(eq_span, ref tokens) => Some(eq_span.to(tokens.span().unwrap_or(eq_span))),
+        }
+    }
+
+    /// Tokens inside the delimiters or after `=`.
+    /// Proc macros see these tokens, for example.
+    pub fn inner_tokens(&self) -> TokenStream {
+        match self {
+            MacArgs::Empty => TokenStream::default(),
+            MacArgs::Delimited(.., tokens) |
+            MacArgs::Eq(.., tokens) => tokens.clone(),
+        }
+    }
+
+    /// Tokens together with the delimiters or `=`.
+    /// Use of this method generally means that something suboptimal or hacky is happening.
+    pub fn outer_tokens(&self) -> TokenStream {
+        match *self {
+            MacArgs::Empty => TokenStream::default(),
+            MacArgs::Delimited(dspan, delim, ref tokens) =>
+                TokenTree::Delimited(dspan, delim.to_token(), tokens.clone()).into(),
+            MacArgs::Eq(eq_span, ref tokens) => iter::once(TokenTree::token(token::Eq, eq_span))
+                                                .chain(tokens.trees()).collect(),
+        }
+    }
+
+    /// Whether a macro with these arguments needs a semicolon
+    /// when used as a standalone item or statement.
+    pub fn need_semicolon(&self) -> bool {
+        !matches!(self, MacArgs::Delimited(_, MacDelimiter::Brace ,_))
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
 pub enum MacDelimiter {
     Parenthesis,
     Bracket,
     Brace,
 }
 
-impl Mac {
-    pub fn stream(&self) -> TokenStream {
-        self.tts.clone()
-    }
-}
-
 impl MacDelimiter {
     crate fn to_token(self) -> DelimToken {
         match self {
@@ -1408,22 +1464,25 @@ impl MacDelimiter {
             MacDelimiter::Brace => DelimToken::Brace,
         }
     }
+
+    pub fn from_token(delim: DelimToken) -> Option<MacDelimiter> {
+        match delim {
+            token::Paren => Some(MacDelimiter::Parenthesis),
+            token::Bracket => Some(MacDelimiter::Bracket),
+            token::Brace => Some(MacDelimiter::Brace),
+            token::NoDelim => None,
+        }
+    }
 }
 
 /// Represents a macro definition.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct MacroDef {
-    pub tokens: TokenStream,
+    pub body: P<MacArgs>,
     /// `true` if macro was defined with `macro_rules`.
     pub legacy: bool,
 }
 
-impl MacroDef {
-    pub fn stream(&self) -> TokenStream {
-        self.tokens.clone().into()
-    }
-}
-
 // Clippy uses Hash and PartialEq
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, PartialEq, HashStable_Generic)]
 pub enum StrStyle {
@@ -2323,7 +2382,7 @@ impl rustc_serialize::Decodable for AttrId {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
 pub struct AttrItem {
     pub path: Path,
-    pub tokens: TokenStream,
+    pub args: MacArgs,
 }
 
 /// Metadata associated with an item.
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 29eff5c2981..079a0f6fafa 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -10,7 +10,7 @@ pub use crate::ast::Attribute;
 
 use crate::ast;
 use crate::ast::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment};
-use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
+use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam};
 use crate::mut_visit::visit_clobber;
 use crate::source_map::{BytePos, Spanned};
@@ -198,7 +198,7 @@ impl Attribute {
 
     pub fn is_word(&self) -> bool {
         if let AttrKind::Normal(item) = &self.kind {
-            item.tokens.is_empty()
+            matches!(item.args, MacArgs::Empty)
         } else {
             false
         }
@@ -278,17 +278,9 @@ impl MetaItem {
 
 impl AttrItem {
     pub fn meta(&self, span: Span) -> Option<MetaItem> {
-        let mut tokens = self.tokens.trees().peekable();
         Some(MetaItem {
             path: self.path.clone(),
-            kind: if let Some(kind) = MetaItemKind::from_tokens(&mut tokens) {
-                if tokens.peek().is_some() {
-                    return None;
-                }
-                kind
-            } else {
-                return None;
-            },
+            kind: MetaItemKind::from_mac_args(&self.args)?,
             span,
         })
     }
@@ -362,8 +354,8 @@ crate fn mk_attr_id() -> AttrId {
     AttrId(id)
 }
 
-pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
-    mk_attr_from_item(style, AttrItem { path, tokens }, span)
+pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
+    mk_attr_from_item(style, AttrItem { path, args }, span)
 }
 
 pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
@@ -377,12 +369,12 @@ pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attrib
 
 /// Returns an inner attribute with the given value and span.
 pub fn mk_attr_inner(item: MetaItem) -> Attribute {
-    mk_attr(AttrStyle::Inner, item.path, item.kind.tokens(item.span), item.span)
+    mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span)
 }
 
 /// Returns an outer attribute with the given value and span.
 pub fn mk_attr_outer(item: MetaItem) -> Attribute {
-    mk_attr(AttrStyle::Outer, item.path, item.kind.tokens(item.span), item.span)
+    mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
 }
 
 pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
@@ -520,7 +512,26 @@ impl MetaItem {
 }
 
 impl MetaItemKind {
-    pub fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
+    pub fn mac_args(&self, span: Span) -> MacArgs {
+        match self {
+            MetaItemKind::Word => MacArgs::Empty,
+            MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()),
+            MetaItemKind::List(list) => {
+                let mut tts = Vec::new();
+                for (i, item) in list.iter().enumerate() {
+                    if i > 0 {
+                        tts.push(TokenTree::token(token::Comma, span).into());
+                    }
+                    tts.extend(item.token_trees_and_joints())
+                }
+                MacArgs::Delimited(
+                    DelimSpan::from_single(span), MacDelimiter::Parenthesis, TokenStream::new(tts)
+                )
+            }
+        }
+    }
+
+    fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
         match *self {
             MetaItemKind::Word => vec![],
             MetaItemKind::NameValue(ref lit) => {
@@ -548,33 +559,8 @@ impl MetaItemKind {
         }
     }
 
-    // Premature conversions of `TokenTree`s to `TokenStream`s can hurt
-    // performance. Do not use this function if `token_trees_and_joints()` can
-    // be used instead.
-    pub fn tokens(&self, span: Span) -> TokenStream {
-        TokenStream::new(self.token_trees_and_joints(span))
-    }
-
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
-        where I: Iterator<Item = TokenTree>,
-    {
-        let delimited = match tokens.peek().cloned() {
-            Some(TokenTree::Token(token)) if token == token::Eq => {
-                tokens.next();
-                return if let Some(TokenTree::Token(token)) = tokens.next() {
-                    Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
-                } else {
-                    None
-                };
-            }
-            Some(TokenTree::Delimited(_, delim, ref tts)) if delim == token::Paren => {
-                tokens.next();
-                tts.clone()
-            }
-            _ => return Some(MetaItemKind::Word),
-        };
-
-        let mut tokens = delimited.into_trees().peekable();
+    fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+        let mut tokens = tokens.into_trees().peekable();
         let mut result = Vec::new();
         while let Some(..) = tokens.peek() {
             let item = NestedMetaItem::from_tokens(&mut tokens)?;
@@ -586,6 +572,47 @@ impl MetaItemKind {
         }
         Some(MetaItemKind::List(result))
     }
+
+    fn name_value_from_tokens(
+        tokens: &mut impl Iterator<Item = TokenTree>,
+    ) -> Option<MetaItemKind> {
+        match tokens.next() {
+            Some(TokenTree::Token(token)) =>
+                Lit::from_token(&token).ok().map(MetaItemKind::NameValue),
+            _ => None,
+        }
+    }
+
+    fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
+        match args {
+            MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) =>
+                MetaItemKind::list_from_tokens(tokens.clone()),
+            MacArgs::Delimited(..) => None,
+            MacArgs::Eq(_, tokens) => {
+                assert!(tokens.len() == 1);
+                MetaItemKind::name_value_from_tokens(&mut tokens.trees())
+            }
+            MacArgs::Empty => Some(MetaItemKind::Word),
+        }
+    }
+
+    fn from_tokens(
+        tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
+    ) -> Option<MetaItemKind> {
+        match tokens.peek() {
+            Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => {
+                let inner_tokens = inner_tokens.clone();
+                tokens.next();
+                MetaItemKind::list_from_tokens(inner_tokens)
+            }
+            Some(TokenTree::Delimited(..)) => None,
+            Some(TokenTree::Token(Token { kind: token::Eq, .. })) => {
+                tokens.next();
+                MetaItemKind::name_value_from_tokens(tokens)
+            }
+            _ => Some(MetaItemKind::Word),
+        }
+    }
 }
 
 impl NestedMetaItem {
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 3d4a5d624c1..3dcdd4db637 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -12,6 +12,7 @@
 #![feature(const_transmute)]
 #![feature(crate_visibility_modifier)]
 #![feature(label_break_value)]
+#![feature(matches_macro)]
 #![feature(nll)]
 #![feature(try_trait)]
 #![feature(slice_patterns)]
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index fbe28215a56..8889e5df26c 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -359,6 +359,26 @@ pub fn visit_fn_sig<T: MutVisitor>(FnSig { header, decl }: &mut FnSig, vis: &mut
     vis.visit_fn_decl(decl);
 }
 
+// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
+pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
+    match args {
+        MacArgs::Empty => {}
+        MacArgs::Delimited(dspan, _delim, tokens) => {
+            visit_delim_span(dspan, vis);
+            vis.visit_tts(tokens);
+        }
+        MacArgs::Eq(eq_span, tokens) => {
+            vis.visit_span(eq_span);
+            vis.visit_tts(tokens);
+        }
+    }
+}
+
+pub fn visit_delim_span<T: MutVisitor>(dspan: &mut DelimSpan, vis: &mut T) {
+    vis.visit_span(&mut dspan.open);
+    vis.visit_span(&mut dspan.close);
+}
+
 pub fn noop_flat_map_field_pattern<T: MutVisitor>(
     mut fp: FieldPat,
     vis: &mut T,
@@ -550,9 +570,9 @@ 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 } = attr;
     match kind {
-        AttrKind::Normal(AttrItem { path, tokens }) => {
+        AttrKind::Normal(AttrItem { path, args }) => {
             vis.visit_path(path);
-            vis.visit_tts(tokens);
+            visit_mac_args(args, vis);
         }
         AttrKind::DocComment(_) => {}
     }
@@ -560,15 +580,14 @@ pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
 }
 
 pub fn noop_visit_mac<T: MutVisitor>(mac: &mut Mac, vis: &mut T) {
-    let Mac { path, delim: _, tts, span, prior_type_ascription: _ } = mac;
+    let Mac { path, args, prior_type_ascription: _ } = mac;
     vis.visit_path(path);
-    vis.visit_tts(tts);
-    vis.visit_span(span);
+    visit_mac_args(args, vis);
 }
 
 pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T) {
-    let MacroDef { tokens, legacy: _ } = macro_def;
-    vis.visit_tts(tokens);
+    let MacroDef { body, legacy: _ } = macro_def;
+    visit_mac_args(body, vis);
 }
 
 pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
@@ -682,9 +701,9 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
         token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
         token::NtLifetime(ident) => vis.visit_ident(ident),
         token::NtLiteral(expr) => vis.visit_expr(expr),
-        token::NtMeta(AttrItem { path, tokens }) => {
+        token::NtMeta(AttrItem { path, args }) => {
             vis.visit_path(path);
-            vis.visit_tts(tokens);
+            visit_mac_args(args, vis);
         }
         token::NtPath(path) => vis.visit_path(path),
         token::NtTT(tt) => vis.visit_tt(tt),
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 0d2e8dddce6..4821bbd9ec6 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1,6 +1,6 @@
 use crate::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
 use crate::ast::{SelfKind, GenericBound, TraitBoundModifier};
-use crate::ast::{Attribute, MacDelimiter, GenericArg};
+use crate::ast::{Attribute, GenericArg, MacArgs};
 use crate::util::parser::{self, AssocOp, Fixity};
 use crate::util::comments;
 use crate::attr;
@@ -639,17 +639,22 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
     fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
         self.ibox(0);
-        match item.tokens.trees().next() {
-            Some(TokenTree::Delimited(_, delim, tts)) => {
-                self.print_mac_common(
-                    Some(MacHeader::Path(&item.path)), false, None, delim, tts, true, span
-                );
-            }
-            tree => {
+        match &item.args {
+            MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+                Some(MacHeader::Path(&item.path)),
+                false,
+                None,
+                delim.to_token(),
+                tokens.clone(),
+                true,
+                span,
+            ),
+            MacArgs::Empty | MacArgs::Eq(..) => {
                 self.print_path(&item.path, false, 0);
-                if tree.is_some() {
+                if let MacArgs::Eq(_, tokens) = &item.args {
                     self.space();
-                    self.print_tts(item.tokens.clone(), true);
+                    self.word_space("=");
+                    self.print_tts(tokens.clone(), true);
                 }
             }
         }
@@ -1097,9 +1102,8 @@ impl<'a> State<'a> {
             }
             ast::ForeignItemKind::Macro(ref m) => {
                 self.print_mac(m);
-                match m.delim {
-                    MacDelimiter::Brace => {},
-                    _ => self.s.word(";")
+                if m.args.need_semicolon() {
+                    self.s.word(";");
                 }
             }
         }
@@ -1361,9 +1365,8 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::Mac(ref mac) => {
                 self.print_mac(mac);
-                match mac.delim {
-                    MacDelimiter::Brace => {}
-                    _ => self.s.word(";"),
+                if mac.args.need_semicolon() {
+                    self.s.word(";");
                 }
             }
             ast::ItemKind::MacroDef(ref macro_def) => {
@@ -1377,8 +1380,8 @@ impl<'a> State<'a> {
                     Some(MacHeader::Keyword(kw)),
                     has_bang,
                     Some(item.ident),
-                    DelimToken::Brace,
-                    macro_def.stream(),
+                    macro_def.body.delim(),
+                    macro_def.body.inner_tokens(),
                     true,
                     item.span,
                 );
@@ -1578,9 +1581,8 @@ impl<'a> State<'a> {
             }
             ast::TraitItemKind::Macro(ref mac) => {
                 self.print_mac(mac);
-                match mac.delim {
-                    MacDelimiter::Brace => {}
-                    _ => self.s.word(";"),
+                if mac.args.need_semicolon() {
+                    self.s.word(";");
                 }
             }
         }
@@ -1608,9 +1610,8 @@ impl<'a> State<'a> {
             }
             ast::ImplItemKind::Macro(ref mac) => {
                 self.print_mac(mac);
-                match mac.delim {
-                    MacDelimiter::Brace => {}
-                    _ => self.s.word(";"),
+                if mac.args.need_semicolon() {
+                    self.s.word(";");
                 }
             }
         }
@@ -1775,10 +1776,10 @@ impl<'a> State<'a> {
             Some(MacHeader::Path(&m.path)),
             true,
             None,
-            m.delim.to_token(),
-            m.stream(),
+            m.args.delim(),
+            m.args.inner_tokens(),
             true,
-            m.span,
+            m.span(),
         );
     }
 
diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs
index 6a0523dd655..491b9a9ade4 100644
--- a/src/libsyntax/tokenstream.rs
+++ b/src/libsyntax/tokenstream.rs
@@ -225,6 +225,14 @@ impl TokenStream {
         self.0.len()
     }
 
+    pub fn span(&self) -> Option<Span> {
+        match &**self.0 {
+            [] => None,
+            [(tt, _)] => Some(tt.span()),
+            [(tt_start, _), .., (tt_end, _)] => Some(tt_start.span().to(tt_end.span())),
+        }
+    }
+
     pub fn from_streams(mut streams: SmallVec<[TokenStream; 2]>) -> TokenStream {
         match streams.len() {
             0 => TokenStream::default(),
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 5ff337fb60e..4ee09b4b87a 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -841,11 +841,19 @@ 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) => visitor.visit_tts(item.tokens.clone()),
+        AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
         AttrKind::DocComment(_) => {}
     }
 }
 
+pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
+    match args {
+        MacArgs::Empty => {}
+        MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()),
+        MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()),
+    }
+}
+
 pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {
     match tt {
         TokenTree::Token(token) => visitor.visit_token(token),
diff --git a/src/libsyntax_expand/expand.rs b/src/libsyntax_expand/expand.rs
index a6ced1439c5..9bfedb3b617 100644
--- a/src/libsyntax_expand/expand.rs
+++ b/src/libsyntax_expand/expand.rs
@@ -11,7 +11,7 @@ use rustc_parse::DirectoryOwnership;
 use rustc_parse::parser::Parser;
 use rustc_parse::validate_attr;
 use syntax::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
-use syntax::ast::{MacStmtStyle, StmtKind, ItemKind};
+use syntax::ast::{MacArgs, MacStmtStyle, StmtKind, ItemKind};
 use syntax::attr::{self, HasAttrs, is_builtin_attr};
 use syntax::source_map::respan;
 use syntax::feature_gate::{self, feature_err};
@@ -597,13 +597,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             InvocationKind::Bang { mac, .. } => match ext {
                 SyntaxExtensionKind::Bang(expander) => {
                     self.gate_proc_macro_expansion_kind(span, fragment_kind);
-                    let tok_result = expander.expand(self.cx, span, mac.stream());
+                    let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens());
                     self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
                 }
                 SyntaxExtensionKind::LegacyBang(expander) => {
                     let prev = self.cx.current_expansion.prior_type_ascription;
                     self.cx.current_expansion.prior_type_ascription = mac.prior_type_ascription;
-                    let tok_result = expander.expand(self.cx, span, mac.stream());
+                    let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens());
                     let result = if let Some(result) = fragment_kind.make_from(tok_result) {
                         result
                     } else {
@@ -642,8 +642,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             => panic!("unexpected annotatable"),
                     })), DUMMY_SP).into();
                     let item = attr.unwrap_normal_item();
-                    let input = self.extract_proc_macro_attr_input(item.tokens, span);
-                    let tok_result = expander.expand(self.cx, span, input, item_tok);
+                    if let MacArgs::Eq(..) = item.args {
+                        self.cx.span_err(span, "key-value macro attributes are not supported");
+                    }
+                    let tok_result =
+                        expander.expand(self.cx, span, item.args.inner_tokens(), item_tok);
                     self.parse_ast_fragment(tok_result, fragment_kind, &item.path, span)
                 }
                 SyntaxExtensionKind::LegacyAttr(expander) => {
@@ -687,23 +690,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
     }
 
-    fn extract_proc_macro_attr_input(&self, tokens: TokenStream, span: Span) -> TokenStream {
-        let mut trees = tokens.trees();
-        match trees.next() {
-            Some(TokenTree::Delimited(_, _, tts)) => {
-                if trees.next().is_none() {
-                    return tts.into()
-                }
-            }
-            Some(TokenTree::Token(..)) => {}
-            None => return TokenStream::default(),
-        }
-        self.cx.span_err(span, "custom attribute invocations must be \
-            of the form `#[foo]` or `#[foo(..)]`, the macro name must only be \
-            followed by a delimiter token");
-        TokenStream::default()
-    }
-
     fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
         let kind = match item {
             Annotatable::Item(item) => match &item.kind {
@@ -1560,7 +1546,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
             let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
             *at = attr::Attribute {
                 kind: ast::AttrKind::Normal(
-                    AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
+                    AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span) },
                 ),
                 span: at.span,
                 id: at.id,
diff --git a/src/libsyntax_expand/mbe/macro_rules.rs b/src/libsyntax_expand/mbe/macro_rules.rs
index b191527df19..e3c3655bcf8 100644
--- a/src/libsyntax_expand/mbe/macro_rules.rs
+++ b/src/libsyntax_expand/mbe/macro_rules.rs
@@ -318,8 +318,8 @@ pub fn compile_declarative_macro(
     let tt_spec = ast::Ident::new(sym::tt, def.span);
 
     // Parse the macro_rules! invocation
-    let body = match def.kind {
-        ast::ItemKind::MacroDef(ref body) => body,
+    let (is_legacy, body) = match &def.kind {
+        ast::ItemKind::MacroDef(macro_def) => (macro_def.legacy, macro_def.body.inner_tokens()),
         _ => unreachable!(),
     };
 
@@ -338,7 +338,7 @@ pub fn compile_declarative_macro(
                     mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
                 ],
                 separator: Some(Token::new(
-                    if body.legacy { token::Semi } else { token::Comma },
+                    if is_legacy { token::Semi } else { token::Comma },
                     def.span,
                 )),
                 kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
@@ -350,7 +350,7 @@ pub fn compile_declarative_macro(
             DelimSpan::dummy(),
             Lrc::new(mbe::SequenceRepetition {
                 tts: vec![mbe::TokenTree::token(
-                    if body.legacy { token::Semi } else { token::Comma },
+                    if is_legacy { token::Semi } else { token::Comma },
                     def.span,
                 )],
                 separator: None,
@@ -360,7 +360,7 @@ pub fn compile_declarative_macro(
         ),
     ];
 
-    let argument_map = match parse(sess, body.stream(), &argument_gram, None, true) {
+    let argument_map = match parse(sess, body, &argument_gram, None, true) {
         Success(m) => m,
         Failure(token, msg) => {
             let s = parse_failure_msg(&token);
@@ -435,7 +435,7 @@ pub fn compile_declarative_macro(
     // that is not lint-checked and trigger the "failed to process buffered lint here" bug.
     valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
 
-    let (transparency, transparency_error) = attr::find_transparency(&def.attrs, body.legacy);
+    let (transparency, transparency_error) = attr::find_transparency(&def.attrs, is_legacy);
     match transparency_error {
         Some(TransparencyError::UnknownTransparency(value, span)) =>
             diag.span_err(span, &format!("unknown macro transparency: `{}`", value)),
diff --git a/src/libsyntax_expand/mbe/transcribe.rs b/src/libsyntax_expand/mbe/transcribe.rs
index 4092d4b97de..a1157667df1 100644
--- a/src/libsyntax_expand/mbe/transcribe.rs
+++ b/src/libsyntax_expand/mbe/transcribe.rs
@@ -30,13 +30,6 @@ impl MutVisitor for Marker {
     }
 }
 
-impl Marker {
-    fn visit_delim_span(&mut self, dspan: &mut DelimSpan) {
-        self.visit_span(&mut dspan.open);
-        self.visit_span(&mut dspan.close);
-    }
-}
-
 /// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
 enum Frame {
     Delimited { forest: Lrc<mbe::Delimited>, idx: usize, span: DelimSpan },
@@ -271,7 +264,7 @@ pub(super) fn transcribe(
             // jump back out of the Delimited, pop the result_stack and add the new results back to
             // the previous results (from outside the Delimited).
             mbe::TokenTree::Delimited(mut span, delimited) => {
-                marker.visit_delim_span(&mut span);
+                mut_visit::visit_delim_span(&mut span, &mut marker);
                 stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
                 result_stack.push(mem::take(&mut result));
             }
diff --git a/src/libsyntax_expand/parse/tests.rs b/src/libsyntax_expand/parse/tests.rs
index 08950ddefba..30e83c151e2 100644
--- a/src/libsyntax_expand/parse/tests.rs
+++ b/src/libsyntax_expand/parse/tests.rs
@@ -272,7 +272,7 @@ fn ttdelim_span() {
             "foo!( fn main() { body } )".to_string(), &sess).unwrap();
 
         let tts: Vec<_> = match expr.kind {
-            ast::ExprKind::Mac(ref mac) => mac.stream().trees().collect(),
+            ast::ExprKind::Mac(ref mac) => mac.args.inner_tokens().trees().collect(),
             _ => panic!("not a macro"),
         };
 
diff --git a/src/libsyntax_expand/placeholders.rs b/src/libsyntax_expand/placeholders.rs
index 6cbe8c13245..74ade1de20e 100644
--- a/src/libsyntax_expand/placeholders.rs
+++ b/src/libsyntax_expand/placeholders.rs
@@ -3,7 +3,6 @@ use crate::expand::{AstFragment, AstFragmentKind};
 
 use syntax::ast;
 use syntax::source_map::{DUMMY_SP, dummy_spanned};
-use syntax::tokenstream::TokenStream;
 use syntax::mut_visit::*;
 use syntax::ptr::P;
 use syntax::ThinVec;
@@ -17,9 +16,7 @@ pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId, vis: Option<ast::Visi
     fn mac_placeholder() -> ast::Mac {
         ast::Mac {
             path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
-            tts: TokenStream::default().into(),
-            delim: ast::MacDelimiter::Brace,
-            span: DUMMY_SP,
+            args: P(ast::MacArgs::Empty),
             prior_type_ascription: None,
         }
     }
diff --git a/src/libsyntax_expand/proc_macro.rs b/src/libsyntax_expand/proc_macro.rs
index 099cf0a4be9..8e56e2bb00b 100644
--- a/src/libsyntax_expand/proc_macro.rs
+++ b/src/libsyntax_expand/proc_macro.rs
@@ -1,7 +1,7 @@
 use crate::base::{self, *};
 use crate::proc_macro_server;
 
-use syntax::ast::{self, ItemKind};
+use syntax::ast::{self, ItemKind, MacArgs};
 use syntax::errors::{Applicability, FatalError};
 use syntax::symbol::sym;
 use syntax::token;
@@ -183,7 +183,7 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>)
         }
 
         let parse_derive_paths = |attr: &ast::Attribute| {
-            if attr.get_normal_item().tokens.is_empty() {
+            if let MacArgs::Empty = attr.get_normal_item().args {
                 return Ok(Vec::new());
             }
             rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
diff --git a/src/libsyntax_ext/assert.rs b/src/libsyntax_ext/assert.rs
index c4f3c03813f..c788d062994 100644
--- a/src/libsyntax_ext/assert.rs
+++ b/src/libsyntax_ext/assert.rs
@@ -6,7 +6,7 @@ use syntax::token::{self, TokenKind};
 use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::symbol::{sym, Symbol};
-use syntax::tokenstream::{TokenStream, TokenTree};
+use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use syntax_expand::base::*;
 use syntax_pos::{Span, DUMMY_SP};
 
@@ -26,19 +26,19 @@ pub fn expand_assert<'cx>(
     // `core::panic` and `std::panic` are different macros, so we use call-site
     // context to pick up whichever is currently in scope.
     let sp = cx.with_call_site_ctxt(sp);
+    let tokens = custom_message.unwrap_or_else(|| {
+        TokenStream::from(TokenTree::token(
+            TokenKind::lit(token::Str, Symbol::intern(&format!(
+                "assertion failed: {}",
+                pprust::expr_to_string(&cond_expr).escape_debug()
+            )), None),
+            DUMMY_SP,
+        ))
+    });
+    let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
     let panic_call = Mac {
         path: Path::from_ident(Ident::new(sym::panic, sp)),
-        tts: custom_message.unwrap_or_else(|| {
-            TokenStream::from(TokenTree::token(
-                TokenKind::lit(token::Str, Symbol::intern(&format!(
-                    "assertion failed: {}",
-                    pprust::expr_to_string(&cond_expr).escape_debug()
-                )), None),
-                DUMMY_SP,
-            ))
-        }).into(),
-        delim: MacDelimiter::Parenthesis,
-        span: sp,
+        args,
         prior_type_ascription: None,
     };
     let if_expr = cx.expr_if(
diff --git a/src/libsyntax_ext/cmdline_attrs.rs b/src/libsyntax_ext/cmdline_attrs.rs
index 5c3009c432c..98cf8a34742 100644
--- a/src/libsyntax_ext/cmdline_attrs.rs
+++ b/src/libsyntax_ext/cmdline_attrs.rs
@@ -16,7 +16,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
         );
 
         let start_span = parser.token.span;
-        let AttrItem { path, tokens } = panictry!(parser.parse_attr_item());
+        let AttrItem { path, args } = panictry!(parser.parse_attr_item());
         let end_span = parser.token.span;
         if parser.token != token::Eof {
             parse_sess.span_diagnostic
@@ -24,7 +24,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
             continue;
         }
 
-        krate.attrs.push(mk_attr(AttrStyle::Inner, path, tokens, start_span.to(end_span)));
+        krate.attrs.push(mk_attr(AttrStyle::Inner, path, args, start_span.to(end_span)));
     }
 
     krate
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index b6bf2f88161..5bd84b43a78 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -340,14 +340,12 @@ pub fn combine_substructure(f: CombineSubstructureFunc<'_>)
 fn find_type_parameters(
     ty: &ast::Ty,
     ty_param_names: &[ast::Name],
-    span: Span,
     cx: &ExtCtxt<'_>,
 ) -> Vec<P<ast::Ty>> {
     use syntax::visit;
 
     struct Visitor<'a, 'b> {
         cx: &'a ExtCtxt<'b>,
-        span: Span,
         ty_param_names: &'a [ast::Name],
         types: Vec<P<ast::Ty>>,
     }
@@ -366,18 +364,11 @@ fn find_type_parameters(
         }
 
         fn visit_mac(&mut self, mac: &ast::Mac) {
-            let span = mac.span.with_ctxt(self.span.ctxt());
-            self.cx.span_err(span, "`derive` cannot be used on items with type macros");
+            self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros");
         }
     }
 
-    let mut visitor = Visitor {
-        ty_param_names,
-        types: Vec::new(),
-        span,
-        cx,
-    };
-
+    let mut visitor = Visitor { cx, ty_param_names, types: Vec::new() };
     visit::Visitor::visit_ty(&mut visitor, ty);
 
     visitor.types
@@ -605,7 +596,7 @@ impl<'a> TraitDef<'a> {
                     .collect();
 
                 for field_ty in field_tys {
-                    let tys = find_type_parameters(&field_ty, &ty_param_names, self.span, cx);
+                    let tys = find_type_parameters(&field_ty, &ty_param_names, cx);
 
                     for ty in tys {
                         // if we have already handled this type, skip it
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 3059b059691..88a325112ac 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -280,6 +280,7 @@ symbols! {
         Err,
         Eq,
         Equal,
+        enclosing_scope,
         except,
         exclusive_range_pattern,
         exhaustive_integer_patterns,
diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr
index 7d31f60efdc..46f8f41076b 100644
--- a/src/test/ui/async-await/try-on-option-in-async.stderr
+++ b/src/test/ui/async-await/try-on-option-in-async.stderr
@@ -1,8 +1,14 @@
 error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option-in-async.rs:8:9
    |
-LL |         x?;
-   |         ^^ cannot use the `?` operator in an async block that returns `{integer}`
+LL |       async {
+   |  ___________-
+LL | |         let x: Option<u32> = None;
+LL | |         x?;
+   | |         ^^ cannot use the `?` operator in an async block that returns `{integer}`
+LL | |         22
+LL | |     }.await
+   | |_____- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `{integer}`
    = note: required by `std::ops::Try::from_error`
@@ -10,8 +16,14 @@ LL |         x?;
 error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option-in-async.rs:16:9
    |
-LL |         x?;
-   |         ^^ cannot use the `?` operator in an async closure that returns `u32`
+LL |       let async_closure = async || {
+   |  __________________________________-
+LL | |         let x: Option<u32> = None;
+LL | |         x?;
+   | |         ^^ cannot use the `?` operator in an async closure that returns `u32`
+LL | |         22_u32
+LL | |     };
+   | |_____- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `u32`
    = note: required by `std::ops::Try::from_error`
@@ -19,8 +31,14 @@ LL |         x?;
 error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option-in-async.rs:25:5
    |
-LL |     x?;
-   |     ^^ cannot use the `?` operator in an async function that returns `u32`
+LL |   async fn an_async_function() -> u32 {
+   |  _____________________________________-
+LL | |     let x: Option<u32> = None;
+LL | |     x?;
+   | |     ^^ cannot use the `?` operator in an async function that returns `u32`
+LL | |     22
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `u32`
    = note: required by `std::ops::Try::from_error`
diff --git a/src/test/ui/issues/issue-10536.rs b/src/test/ui/issues/issue-10536.rs
index 111078abb37..f536d8f940a 100644
--- a/src/test/ui/issues/issue-10536.rs
+++ b/src/test/ui/issues/issue-10536.rs
@@ -11,9 +11,9 @@ macro_rules! foo{
 pub fn main() {
     foo!();
 
-    assert!({one! two()}); //~ ERROR expected open delimiter
+    assert!({one! two()}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
 
     // regardless of whether nested macro_rules works, the following should at
     // least throw a conventional error.
-    assert!({one! two}); //~ ERROR expected open delimiter
+    assert!({one! two}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
 }
diff --git a/src/test/ui/issues/issue-10536.stderr b/src/test/ui/issues/issue-10536.stderr
index 73f948107f1..cc048445871 100644
--- a/src/test/ui/issues/issue-10536.stderr
+++ b/src/test/ui/issues/issue-10536.stderr
@@ -1,14 +1,14 @@
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `two`
   --> $DIR/issue-10536.rs:14:19
    |
 LL |     assert!({one! two()});
-   |                   ^^^ expected open delimiter
+   |                   ^^^ expected one of `(`, `[`, or `{`
 
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `two`
   --> $DIR/issue-10536.rs:18:19
    |
 LL |     assert!({one! two});
-   |                   ^^^ expected open delimiter
+   |                   ^^^ expected one of `(`, `[`, or `{`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/on-unimplemented/enclosing-scope.rs b/src/test/ui/on-unimplemented/enclosing-scope.rs
new file mode 100644
index 00000000000..881bff63f5f
--- /dev/null
+++ b/src/test/ui/on-unimplemented/enclosing-scope.rs
@@ -0,0 +1,27 @@
+// Test scope annotations from `enclosing_scope` parameter
+
+#![feature(rustc_attrs)]
+
+#[rustc_on_unimplemented(enclosing_scope="in this scope")]
+trait Trait{}
+
+struct Foo;
+
+fn f<T: Trait>(x: T) {}
+
+fn main() {
+    let x = || {
+        f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+        let y = || {
+            f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+        };
+    };
+
+    {
+        {
+            f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+        }
+    }
+
+    f(Foo{}); //~ ERROR the trait bound `Foo: Trait` is not satisfied
+}
diff --git a/src/test/ui/on-unimplemented/enclosing-scope.stderr b/src/test/ui/on-unimplemented/enclosing-scope.stderr
new file mode 100644
index 00000000000..092e560330b
--- /dev/null
+++ b/src/test/ui/on-unimplemented/enclosing-scope.stderr
@@ -0,0 +1,66 @@
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+  --> $DIR/enclosing-scope.rs:14:11
+   |
+LL |   fn f<T: Trait>(x: T) {}
+   |      -    ----- required by this bound in `f`
+...
+LL |       let x = || {
+   |  _____________-
+LL | |         f(Foo{});
+   | |           ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | |         let y = || {
+LL | |             f(Foo{});
+LL | |         };
+LL | |     };
+   | |_____- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+  --> $DIR/enclosing-scope.rs:16:15
+   |
+LL |   fn f<T: Trait>(x: T) {}
+   |      -    ----- required by this bound in `f`
+...
+LL |           let y = || {
+   |  _________________-
+LL | |             f(Foo{});
+   | |               ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | |         };
+   | |_________- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+  --> $DIR/enclosing-scope.rs:22:15
+   |
+LL |   fn f<T: Trait>(x: T) {}
+   |      -    ----- required by this bound in `f`
+LL | 
+LL | / fn main() {
+LL | |     let x = || {
+LL | |         f(Foo{});
+LL | |         let y = || {
+...  |
+LL | |             f(Foo{});
+   | |               ^^^^^ the trait `Trait` is not implemented for `Foo`
+...  |
+LL | |     f(Foo{});
+LL | | }
+   | |_- in this scope
+
+error[E0277]: the trait bound `Foo: Trait` is not satisfied
+  --> $DIR/enclosing-scope.rs:26:7
+   |
+LL |   fn f<T: Trait>(x: T) {}
+   |      -    ----- required by this bound in `f`
+LL | 
+LL | / fn main() {
+LL | |     let x = || {
+LL | |         f(Foo{});
+LL | |         let y = || {
+...  |
+LL | |     f(Foo{});
+   | |       ^^^^^ the trait `Trait` is not implemented for `Foo`
+LL | | }
+   | |_- in this scope
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs
index 62a851719f9..ce0fe6fc2a3 100644
--- a/src/test/ui/or-patterns/exhaustiveness-pass.rs
+++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs
@@ -6,35 +6,40 @@
 // We wrap patterns in a tuple because top-level or-patterns are special-cased for now.
 fn main() {
     // Get the fatal error out of the way
-    match (0u8,) {
+    match (0,) {
         (0 | _,) => {}
         //~^ ERROR or-patterns are not fully implemented yet
     }
 
-    match (0u8,) {
+    match (0,) {
         (1 | 2,) => {}
         _ => {}
     }
 
-    match (0u8,) {
-        (1 | 1,) => {} // FIXME(or_patterns): redundancy not detected for now.
-        _ => {}
-    }
-    match (0u8, 0u8) {
+    match (0, 0) {
         (1 | 2, 3 | 4) => {}
         (1, 2) => {}
-        (2, 1) => {}
+        (3, 1) => {}
         _ => {}
     }
     match (Some(0u8),) {
         (None | Some(0 | 1),) => {}
         (Some(2..=255),) => {}
     }
-    match ((0u8,),) {
+    match ((0,),) {
         ((0 | 1,) | (2 | 3,),) => {},
         ((_,),) => {},
     }
     match (&[0u8][..],) {
         ([] | [0 | 1..=255] | [_, ..],) => {},
     }
+
+    match ((0, 0),) {
+        ((0, 0) | (0, 1),) => {}
+        _ => {}
+    }
+    match ((0, 0),) {
+        ((0, 0) | (1, 0),) => {}
+        _ => {}
+    }
 }
diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
index 2cd8ca2dbac..860c7a1bde5 100644
--- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
+++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
@@ -48,4 +48,32 @@ fn main() {
         ((1..=4,),) => {}, //~ ERROR unreachable pattern
         _ => {},
     }
+
+    match (0,) {
+        (1
+         | 1,) => {} //~ ERROR unreachable
+        _ => {}
+    }
+    match [0; 2] {
+        [0
+            | 0 //~ ERROR unreachable
+        , 0
+            | 0] => {} //~ ERROR unreachable
+        _ => {}
+    }
+    match &[][..] {
+        [0] => {}
+        [0, _] => {}
+        [0, _, _] => {}
+        [1, ..] => {}
+        [1 //~ ERROR unreachable
+            | 2, ..] => {}
+        _ => {}
+    }
+    match Some(0) {
+        Some(0) => {}
+        Some(0 //~ ERROR unreachable
+             | 1) => {}
+        _ => {}
+    }
 }
diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
index a4d55d805c3..87f69a484bb 100644
--- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
+++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
@@ -70,11 +70,41 @@ error: unreachable pattern
 LL |         ((1..=4,),) => {},
    |         ^^^^^^^^^^^
 
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:54:12
+   |
+LL |          | 1,) => {}
+   |            ^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:61:15
+   |
+LL |             | 0] => {}
+   |               ^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:59:15
+   |
+LL |             | 0
+   |               ^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:69:10
+   |
+LL |         [1
+   |          ^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:75:14
+   |
+LL |         Some(0
+   |              ^
+
 error: or-patterns are not fully implemented yet
   --> $DIR/exhaustiveness-unreachable-pattern.rs:10:10
    |
 LL |         (0 | _,) => {}
    |          ^^^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 17 previous errors
 
diff --git a/src/test/ui/parser/macro-bad-delimiter-ident.rs b/src/test/ui/parser/macro-bad-delimiter-ident.rs
index 13dec95435b..f461f06b4dc 100644
--- a/src/test/ui/parser/macro-bad-delimiter-ident.rs
+++ b/src/test/ui/parser/macro-bad-delimiter-ident.rs
@@ -1,3 +1,3 @@
 fn main() {
-    foo! bar < //~ ERROR expected open delimiter
+    foo! bar < //~ ERROR expected one of `(`, `[`, or `{`, found `bar`
 }
diff --git a/src/test/ui/parser/macro-bad-delimiter-ident.stderr b/src/test/ui/parser/macro-bad-delimiter-ident.stderr
index e97839a4f4a..f2365fed273 100644
--- a/src/test/ui/parser/macro-bad-delimiter-ident.stderr
+++ b/src/test/ui/parser/macro-bad-delimiter-ident.stderr
@@ -1,8 +1,8 @@
-error: expected open delimiter
+error: expected one of `(`, `[`, or `{`, found `bar`
   --> $DIR/macro-bad-delimiter-ident.rs:2:10
    |
 LL |     foo! bar <
-   |          ^^^ expected open delimiter
+   |          ^^^ expected one of `(`, `[`, or `{`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.rs b/src/test/ui/pattern/usefulness/top-level-alternation.rs
new file mode 100644
index 00000000000..4b47b978930
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/top-level-alternation.rs
@@ -0,0 +1,56 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    while let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+    if let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+
+    match 0u8 {
+        0
+            | 0 => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match Some(0u8) {
+        Some(0)
+            | Some(0) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (_, 0) => {}
+        (0, 0) => {} //~ ERROR unreachable pattern
+        (1, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, 1) | (2, 3) => {}
+        (0, 3) => {}
+        (2, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (_, 0) | (_, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (1, _) => {}
+        _ => {}
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        Some(_) => {} //~ ERROR unreachable pattern
+        None => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        Some(_) => {}
+        None => {}
+        None | Some(_) => {} //~ ERROR unreachable pattern
+    }
+    match 0u8 {
+        1 | 2 => {},
+        1..=2 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+}
diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.stderr b/src/test/ui/pattern/usefulness/top-level-alternation.stderr
new file mode 100644
index 00000000000..7c7c4fc4eba
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/top-level-alternation.stderr
@@ -0,0 +1,68 @@
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:4:23
+   |
+LL |     while let 0..=2 | 1 = 0 {}
+   |                       ^
+   |
+note: lint level defined here
+  --> $DIR/top-level-alternation.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:5:20
+   |
+LL |     if let 0..=2 | 1 = 0 {}
+   |                    ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:9:15
+   |
+LL |             | 0 => {}
+   |               ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:14:15
+   |
+LL |             | Some(0) => {}
+   |               ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:19:9
+   |
+LL |         (0, 0) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:39:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:43:9
+   |
+LL |         Some(_) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:44:9
+   |
+LL |         None => {}
+   |         ^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:49:9
+   |
+LL |         None | Some(_) => {}
+   |         ^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:53:9
+   |
+LL |         1..=2 => {},
+   |         ^^^^^
+
+error: aborting due to 10 previous errors
+
diff --git a/src/test/ui/proc-macro/proc-macro-gates.rs b/src/test/ui/proc-macro/proc-macro-gates.rs
index 0096a84f14c..591c1e039bd 100644
--- a/src/test/ui/proc-macro/proc-macro-gates.rs
+++ b/src/test/ui/proc-macro/proc-macro-gates.rs
@@ -18,7 +18,7 @@ mod _test2_inner {
           //~| ERROR: non-builtin inner attributes are unstable
 }
 
-#[empty_attr = "y"] //~ ERROR: must only be followed by a delimiter token
+#[empty_attr = "y"] //~ ERROR: key-value macro attributes are not supported
 fn _test3() {}
 
 fn attrs() {
diff --git a/src/test/ui/proc-macro/proc-macro-gates.stderr b/src/test/ui/proc-macro/proc-macro-gates.stderr
index 14a4f8c0fbc..e939434243b 100644
--- a/src/test/ui/proc-macro/proc-macro-gates.stderr
+++ b/src/test/ui/proc-macro/proc-macro-gates.stderr
@@ -34,7 +34,7 @@ LL |     #![empty_attr]
    = note: for more information, see https://github.com/rust-lang/rust/issues/54727
    = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
 
-error: custom attribute invocations must be of the form `#[foo]` or `#[foo(..)]`, the macro name must only be followed by a delimiter token
+error: key-value macro attributes are not supported
   --> $DIR/proc-macro-gates.rs:21:1
    |
 LL | #[empty_attr = "y"]
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
index f24ea0505e7..1143bddfe45 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -575,8 +575,17 @@ LL |     if (let 0 = 0)? {}
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/disallowed-positions.rs:46:8
    |
-LL |     if (let 0 = 0)? {}
-   |        ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn nested_within_if_expr() {
+LL | |     if &let 0 = 0 {}
+LL | |
+LL | |
+...  |
+LL | |     if (let 0 = 0)? {}
+   | |        ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+...  |
+LL | |     if let true = let true = true {}
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `()`
    = note: required by `std::ops::Try::from_error`
@@ -754,8 +763,17 @@ LL |     while (let 0 = 0)? {}
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/disallowed-positions.rs:110:11
    |
-LL |     while (let 0 = 0)? {}
-   |           ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn nested_within_while_expr() {
+LL | |     while &let 0 = 0 {}
+LL | |
+LL | |
+...  |
+LL | |     while (let 0 = 0)? {}
+   | |           ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+...  |
+LL | |     while let true = let true = true {}
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `()`
    = note: required by `std::ops::Try::from_error`
@@ -924,8 +942,17 @@ LL |     (let 0 = 0)?;
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/disallowed-positions.rs:183:5
    |
-LL |     (let 0 = 0)?;
-   |     ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn outside_if_and_while_expr() {
+LL | |     &let 0 = 0;
+LL | |
+LL | |     !let 0 = 0;
+...  |
+LL | |     (let 0 = 0)?;
+   | |     ^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+...  |
+LL | |
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `()`
    = note: required by `std::ops::Try::from_error`
diff --git a/src/test/ui/try-on-option-diagnostics.stderr b/src/test/ui/try-on-option-diagnostics.stderr
index 4dd515e1b5a..ce3aca39fb8 100644
--- a/src/test/ui/try-on-option-diagnostics.stderr
+++ b/src/test/ui/try-on-option-diagnostics.stderr
@@ -1,8 +1,13 @@
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option-diagnostics.rs:7:5
    |
-LL |     x?;
-   |     ^^ cannot use the `?` operator in a function that returns `u32`
+LL | / fn a_function() -> u32 {
+LL | |     let x: Option<u32> = None;
+LL | |     x?;
+   | |     ^^ cannot use the `?` operator in a function that returns `u32`
+LL | |     22
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `u32`
    = note: required by `std::ops::Try::from_error`
@@ -10,8 +15,14 @@ LL |     x?;
 error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option-diagnostics.rs:14:9
    |
-LL |         x?;
-   |         ^^ cannot use the `?` operator in a closure that returns `{integer}`
+LL |       let a_closure = || {
+   |  _____________________-
+LL | |         let x: Option<u32> = None;
+LL | |         x?;
+   | |         ^^ cannot use the `?` operator in a closure that returns `{integer}`
+LL | |         22
+LL | |     };
+   | |_____- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `{integer}`
    = note: required by `std::ops::Try::from_error`
diff --git a/src/test/ui/try-on-option.stderr b/src/test/ui/try-on-option.stderr
index db5046f8c15..07615b52a48 100644
--- a/src/test/ui/try-on-option.stderr
+++ b/src/test/ui/try-on-option.stderr
@@ -10,8 +10,13 @@ LL |     x?;
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-on-option.rs:13:5
    |
-LL |     x?;
-   |     ^^ cannot use the `?` operator in a function that returns `u32`
+LL | / fn bar() -> u32 {
+LL | |     let x: Option<u32> = None;
+LL | |     x?;
+   | |     ^^ cannot use the `?` operator in a function that returns `u32`
+LL | |     22
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `u32`
    = note: required by `std::ops::Try::from_error`
diff --git a/src/test/ui/try-operator-on-main.stderr b/src/test/ui/try-operator-on-main.stderr
index d2f1a04837b..d8ba264583e 100644
--- a/src/test/ui/try-operator-on-main.stderr
+++ b/src/test/ui/try-operator-on-main.stderr
@@ -1,8 +1,15 @@
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> $DIR/try-operator-on-main.rs:9:5
    |
-LL |     std::fs::File::open("foo")?;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | / fn main() {
+LL | |     // error for a `Try` type on a non-`Try` fn
+LL | |     std::fs::File::open("foo")?;
+   | |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
+LL | |
+...  |
+LL | |     try_trait_generic::<()>();
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `()`
    = note: required by `std::ops::Try::from_error`