about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-10-28 20:59:36 +0000
committerbors <bors@rust-lang.org>2019-10-28 20:59:36 +0000
commitcac68218105f1ba8b6a2a0b21b791951ff02f75d (patch)
tree6900e4f7b28de2ececaa397b27b438c1b251a52c
parentb497e18995d6b6992f97512c6b86b5cb3f2f34f5 (diff)
parent30431a33ead3cef1988266e3cb99d35607f7e3bb (diff)
downloadrust-cac68218105f1ba8b6a2a0b21b791951ff02f75d.tar.gz
rust-cac68218105f1ba8b6a2a0b21b791951ff02f75d.zip
Auto merge of #65907 - Centril:rollup-9i8ev23, r=Centril
Rollup of 9 pull requests

Successful merges:

 - #65563 (Add long error explanation for E0587)
 - #65640 (Use heuristics to recover parsing of missing `;`)
 - #65643 (Correct handling of type flags with `ConstValue::Placeholder`)
 - #65825 (rustc: use IndexVec<DefIndex, T> instead of Vec<T>.)
 - #65858 (suggest `const_in_array_repeat_expression` flag)
 - #65877 (doc: introduce `once` in `iter::chain` document)
 - #65887 (doc: mention `get(_mut)` in Vec)
 - #65891 (self-profiling: Record something more useful for crate metadata generation event.)
 - #65893 (Output previous stable  error messaging when using stable build.)

Failed merges:

r? @ghost
-rw-r--r--src/liballoc/vec.rs6
-rw-r--r--src/libcore/iter/traits/iterator.rs21
-rw-r--r--src/librustc/hir/lowering/expr.rs19
-rw-r--r--src/librustc/hir/map/collector.rs8
-rw-r--r--src/librustc/hir/map/definitions.rs18
-rw-r--r--src/librustc/hir/map/mod.rs14
-rw-r--r--src/librustc/traits/error_reporting.rs11
-rw-r--r--src/librustc/traits/mod.rs5
-rw-r--r--src/librustc/traits/structural_impls.rs2
-rw-r--r--src/librustc/ty/context.rs1
-rw-r--r--src/librustc/ty/flags.rs16
-rw-r--r--src/librustc_codegen_ssa/base.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs10
-rw-r--r--src/librustc_mir/transform/promote_consts.rs25
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs21
-rw-r--r--src/librustc_typeck/error_codes.rs20
-rw-r--r--src/libsyntax/parse/parser/diagnostics.rs150
-rw-r--r--src/libsyntax/parse/parser/item.rs24
-rw-r--r--src/libsyntax/parse/parser/stmt.rs6
-rw-r--r--src/libsyntax/parse/token.rs51
-rw-r--r--src/test/ui/async-await/no-async-const.rs2
-rw-r--r--src/test/ui/async-await/no-async-const.stderr2
-rw-r--r--src/test/ui/async-await/no-unsafe-async.rs4
-rw-r--r--src/test/ui/async-await/no-unsafe-async.stderr4
-rw-r--r--src/test/ui/can-begin-expr-check.rs2
-rw-r--r--src/test/ui/can-begin-expr-check.stderr2
-rw-r--r--src/test/ui/conflicting-repr-hints.stderr3
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs7
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr16
-rw-r--r--src/test/ui/issues/issue-43196.stderr2
-rw-r--r--src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr2
-rw-r--r--src/test/ui/macros/issue-54441.stderr4
-rw-r--r--src/test/ui/parser/default.stderr2
-rw-r--r--src/test/ui/parser/duplicate-visibility.stderr2
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.rs2
-rw-r--r--src/test/ui/parser/extern-expected-fn-or-brace.stderr2
-rw-r--r--src/test/ui/parser/impl-parsing.stderr2
-rw-r--r--src/test/ui/parser/issue-15980.rs2
-rw-r--r--src/test/ui/parser/issue-15980.stderr2
-rw-r--r--src/test/ui/parser/issue-19398.rs2
-rw-r--r--src/test/ui/parser/issue-19398.stderr4
-rw-r--r--src/test/ui/parser/issue-3036.rs4
-rw-r--r--src/test/ui/parser/issue-3036.stderr8
-rw-r--r--src/test/ui/parser/macro/trait-non-item-macros.stderr2
-rw-r--r--src/test/ui/parser/raw/raw-literal-keywords.rs2
-rw-r--r--src/test/ui/parser/raw/raw-literal-keywords.stderr2
-rw-r--r--src/test/ui/parser/recover-for-loop-parens-around-head.rs2
-rw-r--r--src/test/ui/parser/recover-for-loop-parens-around-head.stderr2
-rw-r--r--src/test/ui/parser/recover-missing-semi.rs4
-rw-r--r--src/test/ui/parser/recover-missing-semi.stderr20
-rw-r--r--src/test/ui/parser/removed-syntax-static-fn.stderr2
-rw-r--r--src/test/ui/parser/removed-syntax-uniq-mut-ty.rs3
-rw-r--r--src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr2
-rw-r--r--src/test/ui/parser/underscore_item_not_const.rs2
-rw-r--r--src/test/ui/parser/underscore_item_not_const.stderr2
55 files changed, 342 insertions, 215 deletions
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index 5e733fa43d7..5b53a6a2899 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -154,8 +154,8 @@ use crate::raw_vec::RawVec;
 /// println!("{}", v[6]); // it will panic!
 /// ```
 ///
-/// In conclusion: always check if the index you want to get really exists
-/// before doing it.
+/// Use [`get`] and [`get_mut`] if you want to check whether the index is in
+/// the `Vec`.
 ///
 /// # Slicing
 ///
@@ -277,6 +277,8 @@ use crate::raw_vec::RawVec;
 /// The order has changed in the past and may change again.
 ///
 /// [`vec!`]: ../../std/macro.vec.html
+/// [`get`]: ../../std/vec/struct.Vec.html#method.get
+/// [`get_mut`]: ../../std/vec/struct.Vec.html#method.get_mut
 /// [`Index`]: ../../std/ops/trait.Index.html
 /// [`String`]: ../../std/string/struct.String.html
 /// [`&str`]: ../../std/primitive.str.html
diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs
index a272035150a..7ffc8b3729c 100644
--- a/src/libcore/iter/traits/iterator.rs
+++ b/src/libcore/iter/traits/iterator.rs
@@ -384,6 +384,9 @@ pub trait Iterator {
     ///
     /// In other words, it links two iterators together, in a chain. 🔗
     ///
+    /// [`once`] is commonly used to adapt a single value into a chain of
+    /// other kinds of iteration.
+    ///
     /// # Examples
     ///
     /// Basic usage:
@@ -408,9 +411,6 @@ pub trait Iterator {
     /// [`Iterator`] itself. For example, slices (`&[T]`) implement
     /// [`IntoIterator`], and so can be passed to `chain()` directly:
     ///
-    /// [`IntoIterator`]: trait.IntoIterator.html
-    /// [`Iterator`]: trait.Iterator.html
-    ///
     /// ```
     /// let s1 = &[1, 2, 3];
     /// let s2 = &[4, 5, 6];
@@ -425,6 +425,21 @@ pub trait Iterator {
     /// assert_eq!(iter.next(), Some(&6));
     /// assert_eq!(iter.next(), None);
     /// ```
+    ///
+    /// If you work with Windows API, you may wish to convert [`OsStr`] to `Vec<u16>`:
+    ///
+    /// ```
+    /// #[cfg(windows)]
+    /// fn os_str_to_utf16(s: &std::ffi::OsStr) -> Vec<u16> {
+    ///     use std::os::windows::ffi::OsStrExt;
+    ///     s.encode_wide().chain(std::iter::once(0)).collect()
+    /// }
+    /// ```
+    ///
+    /// [`once`]: fn.once.html
+    /// [`Iterator`]: trait.Iterator.html
+    /// [`IntoIterator`]: trait.IntoIterator.html
+    /// [`OsStr`]: ../../std/ffi/struct.OsStr.html
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> where
diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs
index d5fcc0ef6ed..73db762a64b 100644
--- a/src/librustc/hir/lowering/expr.rs
+++ b/src/librustc/hir/lowering/expr.rs
@@ -235,11 +235,20 @@ impl LoweringContext<'_> {
     /// ```
     fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind {
         // If we got here, the `let` expression is not allowed.
-        self.sess
-            .struct_span_err(span, "`let` expressions are not supported here")
-            .note("only supported directly in conditions of `if`- and `while`-expressions")
-            .note("as well as when nested within `&&` and parenthesis in those conditions")
-            .emit();
+
+        if self.sess.opts.unstable_features.is_nightly_build() {
+            self.sess
+                .struct_span_err(span, "`let` expressions are not supported here")
+                .note("only supported directly in conditions of `if`- and `while`-expressions")
+                .note("as well as when nested within `&&` and parenthesis in those conditions")
+                .emit();
+        }
+        else {
+            self.sess
+                .struct_span_err(span, "expected expression, found statement (`let`)")
+                .note("variable declaration using `let` is a statement")
+                .emit();
+        }
 
         // For better recovery, we emit:
         // ```
diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs
index 307dbe7dab0..b0fa844c818 100644
--- a/src/librustc/hir/map/collector.rs
+++ b/src/librustc/hir/map/collector.rs
@@ -149,7 +149,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
         let mut collector = NodeCollector {
             krate,
             source_map: sess.source_map(),
-            map: vec![None; definitions.def_index_count()],
+            map: IndexVec::from_elem_n(IndexVec::new(), definitions.def_index_count()),
             parent_node: hir::CRATE_HIR_ID,
             current_signature_dep_index: root_mod_sig_dep_index,
             current_full_dep_index: root_mod_full_dep_index,
@@ -227,12 +227,8 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
 
     fn insert_entry(&mut self, id: HirId, entry: Entry<'hir>) {
         debug!("hir_map: {:?} => {:?}", id, entry);
-        let local_map = &mut self.map[id.owner.index()];
+        let local_map = &mut self.map[id.owner];
         let i = id.local_id.as_u32() as usize;
-        if local_map.is_none() {
-            *local_map = Some(IndexVec::with_capacity(i + 1));
-        }
-        let local_map = local_map.as_mut().unwrap();
         let len = local_map.len();
         if i >= len {
             local_map.extend(repeat(None).take(i - len + 1));
diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs
index 4e163314f6b..be8d82173e4 100644
--- a/src/librustc/hir/map/definitions.rs
+++ b/src/librustc/hir/map/definitions.rs
@@ -27,8 +27,8 @@ use syntax_pos::{Span, DUMMY_SP};
 /// There is one `DefPathTable` for each crate.
 #[derive(Clone, Default, RustcDecodable, RustcEncodable)]
 pub struct DefPathTable {
-    index_to_key: Vec<DefKey>,
-    def_path_hashes: Vec<DefPathHash>,
+    index_to_key: IndexVec<DefIndex, DefKey>,
+    def_path_hashes: IndexVec<DefIndex, DefPathHash>,
 }
 
 impl DefPathTable {
@@ -53,14 +53,14 @@ impl DefPathTable {
 
     #[inline(always)]
     pub fn def_key(&self, index: DefIndex) -> DefKey {
-        self.index_to_key[index.index()]
+        self.index_to_key[index]
     }
 
     #[inline(always)]
     pub fn def_path_hash(&self, index: DefIndex) -> DefPathHash {
-        let ret = self.def_path_hashes[index.index()];
-        debug!("def_path_hash({:?}) = {:?}", index, ret);
-        return ret
+        let hash = self.def_path_hashes[index];
+        debug!("def_path_hash({:?}) = {:?}", index, hash);
+        hash
     }
 
     pub fn add_def_path_hashes_to(&self,
@@ -92,7 +92,7 @@ impl DefPathTable {
 pub struct Definitions {
     table: DefPathTable,
     node_to_def_index: NodeMap<DefIndex>,
-    def_index_to_node: Vec<ast::NodeId>,
+    def_index_to_node: IndexVec<DefIndex, ast::NodeId>,
     pub(super) node_to_hir_id: IndexVec<ast::NodeId, hir::HirId>,
     /// If `ExpnId` is an ID of some macro expansion,
     /// then `DefId` is the normal module (`mod`) in which the expanded macro was defined.
@@ -375,7 +375,7 @@ impl Definitions {
     #[inline]
     pub fn as_local_node_id(&self, def_id: DefId) -> Option<ast::NodeId> {
         if def_id.krate == LOCAL_CRATE {
-            let node_id = self.def_index_to_node[def_id.index.index()];
+            let node_id = self.def_index_to_node[def_id.index];
             if node_id != ast::DUMMY_NODE_ID {
                 return Some(node_id);
             }
@@ -404,7 +404,7 @@ impl Definitions {
 
     #[inline]
     pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> hir::HirId {
-        let node_id = self.def_index_to_node[def_index.index()];
+        let node_id = self.def_index_to_node[def_index];
         self.node_to_hir_id[node_id]
     }
 
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index cd36944253d..acadd77cc36 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -156,9 +156,9 @@ impl Forest {
 
 /// This type is effectively a `HashMap<HirId, Entry<'hir>>`,
 /// but it is implemented as 2 layers of arrays.
-/// - first we have `A = Vec<Option<B>>` mapping a `DefIndex`'s index to an inner value
+/// - first we have `A = IndexVec<DefIndex, B>` mapping `DefIndex`s to an inner value
 /// - which is `B = IndexVec<ItemLocalId, Option<Entry<'hir>>` which gives you the `Entry`.
-pub(super) type HirEntryMap<'hir> = Vec<Option<IndexVec<ItemLocalId, Option<Entry<'hir>>>>>;
+pub(super) type HirEntryMap<'hir> = IndexVec<DefIndex, IndexVec<ItemLocalId, Option<Entry<'hir>>>>;
 
 /// Represents a mapping from `NodeId`s to AST elements and their parent `NodeId`s.
 #[derive(Clone)]
@@ -222,8 +222,8 @@ impl<'map> Iterator for ParentHirIterator<'map> {
 impl<'hir> Map<'hir> {
     #[inline]
     fn lookup(&self, id: HirId) -> Option<&Entry<'hir>> {
-        let local_map = self.map.get(id.owner.index())?;
-        local_map.as_ref()?.get(id.local_id)?.as_ref()
+        let local_map = self.map.get(id.owner)?;
+        local_map.get(id.local_id)?.as_ref()
     }
 
     /// Registers a read in the dependency graph of the AST node with
@@ -1031,14 +1031,12 @@ impl<'hir> Map<'hir> {
         // see the comment on `HirEntryMap`.
         // Iterate over all the indices and return a reference to
         // local maps and their index given that they exist.
-        self.map.iter().enumerate().filter_map(|(i, local_map)| {
-            local_map.as_ref().map(|m| (i, m))
-        }).flat_map(move |(array_index, local_map)| {
+        self.map.iter_enumerated().flat_map(move |(owner, local_map)| {
             // Iterate over each valid entry in the local map.
             local_map.iter_enumerated().filter_map(move |(i, entry)| entry.map(move |_| {
                 // Reconstruct the `HirId` based on the 3 indices we used to find it.
                 HirId {
-                    owner: DefIndex::from(array_index),
+                    owner,
                     local_id: i,
                 }
             }))
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index b2e5624f476..b55c0f29a70 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -2112,9 +2112,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 err.note(&format!("required by cast to type `{}`",
                                   self.ty_to_string(target)));
             }
-            ObligationCauseCode::RepeatVec => {
+            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expression) => {
                 err.note("the `Copy` trait is required because the \
                           repeated element will be copied");
+                if suggest_const_in_array_repeat_expression {
+                    err.note("this array initializer can be evaluated at compile-time, for more \
+                              information, see issue \
+                              https://github.com/rust-lang/rust/issues/49147");
+                    if tcx.sess.opts.unstable_features.is_nightly_build() {
+                        err.help("add `#![feature(const_in_array_repeat_expression)]` to the \
+                                  crate attributes to enable");
+                    }
+                }
             }
             ObligationCauseCode::VariableType(_) => {
                 err.note("all local variables must have a statically known size");
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 04c1b4d927a..b8275299562 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -206,8 +206,9 @@ pub enum ObligationCauseCode<'tcx> {
     SizedReturnType,
     /// Yield type must be Sized
     SizedYieldType,
-    /// [T,..n] --> T must be Copy
-    RepeatVec,
+    /// [T,..n] --> T must be Copy. If `true`, suggest `const_in_array_repeat_expression` feature
+    /// flag.
+    RepeatVec(bool),
 
     /// Types of fields (other than the last, except for packed structs) in a struct must be sized.
     FieldSized { adt_kind: AdtKind, last: bool },
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index d9b796d063e..109e884f8bd 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -494,7 +494,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
             super::SizedArgumentType => Some(super::SizedArgumentType),
             super::SizedReturnType => Some(super::SizedReturnType),
             super::SizedYieldType => Some(super::SizedYieldType),
-            super::RepeatVec => Some(super::RepeatVec),
+            super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)),
             super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }),
             super::ConstSized => Some(super::ConstSized),
             super::ConstPatternStructural => Some(super::ConstPatternStructural),
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 0f7d5d9a25e..bdf9b2d7f3f 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1408,6 +1408,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn encode_metadata(self)-> EncodedMetadata {
+        let _prof_timer = self.prof.generic_activity("generate_crate_metadata");
         self.cstore.encode_metadata(self)
     }
 
diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs
index cb1fb4f685d..d4b7f37b120 100644
--- a/src/librustc/ty/flags.rs
+++ b/src/librustc/ty/flags.rs
@@ -114,6 +114,7 @@ impl FlagComputation {
             }
 
             &ty::Placeholder(..) => {
+                self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES);
                 self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER);
             }
 
@@ -123,8 +124,7 @@ impl FlagComputation {
                 match infer {
                     ty::FreshTy(_) |
                     ty::FreshIntTy(_) |
-                    ty::FreshFloatTy(_) => {
-                    }
+                    ty::FreshFloatTy(_) => {}
 
                     ty::TyVar(_) |
                     ty::IntVar(_) |
@@ -245,14 +245,16 @@ impl FlagComputation {
             }
             ConstValue::Bound(debruijn, _) => self.add_binder(debruijn),
             ConstValue::Param(_) => {
-                self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES | TypeFlags::HAS_PARAMS);
+                self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES);
+                self.add_flags(TypeFlags::HAS_PARAMS);
             }
             ConstValue::Placeholder(_) => {
-                self.add_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_CT_PLACEHOLDER);
+                self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES);
+                self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER);
             }
-            ConstValue::Scalar(_) => { }
-            ConstValue::Slice { data: _, start: _, end: _ } => { }
-            ConstValue::ByRef { alloc: _, offset: _ } => { }
+            ConstValue::Scalar(_) => {}
+            ConstValue::Slice { .. } => {}
+            ConstValue::ByRef { .. } => {}
         }
     }
 
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs
index bf687f84635..ee4ec7fb41e 100644
--- a/src/librustc_codegen_ssa/base.rs
+++ b/src/librustc_codegen_ssa/base.rs
@@ -574,8 +574,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
 
     if need_metadata_module {
         // Codegen the encoded metadata.
-        let _prof_timer = tcx.prof.generic_activity("codegen_crate_metadata");
-
         let metadata_cgu_name = cgu_name_builder.build_cgu_name(LOCAL_CRATE,
                                                                 &["crate"],
                                                                 Some("metadata")).as_str()
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index b5560fe6751..9f2f174553f 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -16,6 +16,7 @@ use crate::borrow_check::nll::type_check::free_region_relations::{
 };
 use crate::borrow_check::nll::universal_regions::{DefiningTy, UniversalRegions};
 use crate::borrow_check::nll::ToRegionVid;
+use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
 use crate::dataflow::move_paths::MoveData;
 use crate::dataflow::FlowAtLocation;
 use crate::dataflow::MaybeInitializedPlaces;
@@ -1983,12 +1984,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     let span = body.source_info(location).span;
                     let ty = operand.ty(body, tcx);
                     if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
+                        // To determine if `const_in_array_repeat_expression` feature gate should
+                        // be mentioned, need to check if the rvalue is promotable.
+                        let should_suggest =
+                            should_suggest_const_in_array_repeat_expressions_attribute(
+                                tcx, self.mir_def_id, body, operand);
+                        debug!("check_rvalue: should_suggest={:?}", should_suggest);
+
                         self.infcx.report_selection_error(
                             &traits::Obligation::new(
                                 ObligationCause::new(
                                     span,
                                     self.tcx().hir().def_index_to_hir_id(self.mir_def_id.index),
-                                    traits::ObligationCauseCode::RepeatVec,
+                                    traits::ObligationCauseCode::RepeatVec(should_suggest),
                                 ),
                                 self.param_env,
                                 ty::Predicate::Trait(ty::Binder::bind(ty::TraitPredicate {
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 3af08090853..8def717f158 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -1110,3 +1110,28 @@ pub fn promote_candidates<'tcx>(
 
     promotions
 }
+
+/// This function returns `true` if the `const_in_array_repeat_expression` feature attribute should
+/// be suggested. This function is probably quite expensive, it shouldn't be run in the happy path.
+/// Feature attribute should be suggested if `operand` can be promoted and the feature is not
+/// enabled.
+crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_def_id: DefId,
+    body: &Body<'tcx>,
+    operand: &Operand<'tcx>,
+) -> bool {
+    let mut rpo = traversal::reverse_postorder(body);
+    let (temps, _) = collect_temps_and_candidates(tcx, body, &mut rpo);
+    let validator = Validator {
+        item: Item::new(tcx, mir_def_id, body),
+        temps: &temps,
+        explicit: false,
+    };
+
+    let should_promote = validator.validate_operand(operand).is_ok();
+    let feature_flag = tcx.features().const_in_array_repeat_expressions;
+    debug!("should_suggest_const_in_array_repeat_expressions_flag: mir_def_id={:?} \
+            should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
+    should_promote && !feature_flag
+}
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 2f77cd5ddf7..3702dd9ad49 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -878,13 +878,11 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 }
             },
             ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => {
-                let candidate = Candidate::Repeat(location);
-                let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
-                                     IsNotPromotable::in_operand(self, operand);
-                debug!("assign: self.def_id={:?} operand={:?}", self.def_id, operand);
-                if !not_promotable && self.tcx.features().const_in_array_repeat_expressions {
-                    debug!("assign: candidate={:?}", candidate);
-                    self.promotion_candidates.push(candidate);
+                debug!("assign: self.cx.mode={:?} self.def_id={:?} location={:?} operand={:?}",
+                       self.cx.mode, self.def_id, location, operand);
+                if self.should_promote_repeat_expression(operand) &&
+                        self.tcx.features().const_in_array_repeat_expressions {
+                    self.promotion_candidates.push(Candidate::Repeat(location));
                 }
             },
             _ => {},
@@ -1149,6 +1147,15 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
 
         candidates
     }
+
+    /// Returns `true` if the operand of a repeat expression is promotable.
+    fn should_promote_repeat_expression(&self, operand: &Operand<'tcx>) -> bool {
+        let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
+                             IsNotPromotable::in_operand(self, operand);
+        debug!("should_promote_repeat_expression: operand={:?} not_promotable={:?}",
+               operand, not_promotable);
+        !not_promotable
+    }
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs
index ae67f54ac12..248b687c31c 100644
--- a/src/librustc_typeck/error_codes.rs
+++ b/src/librustc_typeck/error_codes.rs
@@ -3891,6 +3891,25 @@ details.
 [issue #33685]: https://github.com/rust-lang/rust/issues/33685
 "##,
 
+E0587: r##"
+A type has both `packed` and `align` representation hints.
+
+Erroneous code example:
+
+```compile_fail,E0587
+#[repr(packed, align(8))] // error!
+struct Umbrella(i32);
+```
+
+You cannot use `packed` and `align` hints on a same type. If you want to pack a
+type to a given size, you should provide a size to packed:
+
+```
+#[repr(packed)] // ok!
+struct Umbrella(i32);
+```
+"##,
+
 E0588: r##"
 A type with `packed` representation hint has a field with `align`
 representation hint.
@@ -5098,7 +5117,6 @@ struct B<const X: A>; // ok!
 //  E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
 //  E0564, // only named lifetimes are allowed in `impl Trait`,
            // but `{}` was found in the type `{}`
-    E0587, // type has conflicting packed and align representation hints
 //  E0611, // merged into E0616
 //  E0612, // merged into E0609
 //  E0613, // Removed (merged with E0609)
diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs
index 06982c789db..ab2b4519cb7 100644
--- a/src/libsyntax/parse/parser/diagnostics.rs
+++ b/src/libsyntax/parse/parser/diagnostics.rs
@@ -6,7 +6,7 @@ use crate::ast::{
     self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
     Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind,
 };
-use crate::parse::token::{self, TokenKind};
+use crate::parse::token::{self, TokenKind, token_can_begin_expr};
 use crate::print::pprust;
 use crate::ptr::P;
 use crate::symbol::{kw, sym};
@@ -274,23 +274,23 @@ impl<'a> Parser<'a> {
         expected.sort_by_cached_key(|x| x.to_string());
         expected.dedup();
         let expect = tokens_to_string(&expected[..]);
-        let actual = self.this_token_to_string();
+        let actual = self.this_token_descr();
         let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 {
             let short_expect = if expected.len() > 6 {
                 format!("{} possible tokens", expected.len())
             } else {
                 expect.clone()
             };
-            (format!("expected one of {}, found `{}`", expect, actual),
+            (format!("expected one of {}, found {}", expect, actual),
                 (self.sess.source_map().next_point(self.prev_span),
                 format!("expected one of {} here", short_expect)))
         } else if expected.is_empty() {
-            (format!("unexpected token: `{}`", actual),
+            (format!("unexpected token: {}", actual),
                 (self.prev_span, "unexpected token after this".to_string()))
         } else {
-            (format!("expected {}, found `{}`", expect, actual),
+            (format!("expected {}, found {}", expect, actual),
                 (self.sess.source_map().next_point(self.prev_span),
-                format!("expected {} here", expect)))
+                format!("expected {}", expect)))
         };
         self.last_unexpected_token_span = Some(self.token.span);
         let mut err = self.fatal(&msg_exp);
@@ -326,58 +326,28 @@ impl<'a> Parser<'a> {
             }
         }
 
-        let is_semi_suggestable = expected.iter().any(|t| match t {
-            TokenType::Token(token::Semi) => true, // We expect a `;` here.
-            _ => false,
-        }) && ( // A `;` would be expected before the current keyword.
-            self.token.is_keyword(kw::Break) ||
-            self.token.is_keyword(kw::Continue) ||
-            self.token.is_keyword(kw::For) ||
-            self.token.is_keyword(kw::If) ||
-            self.token.is_keyword(kw::Let) ||
-            self.token.is_keyword(kw::Loop) ||
-            self.token.is_keyword(kw::Match) ||
-            self.token.is_keyword(kw::Return) ||
-            self.token.is_keyword(kw::While)
-        );
         let sm = self.sess.source_map();
-        match (sm.lookup_line(self.token.span.lo()), sm.lookup_line(sp.lo())) {
-            (Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => {
-                // The spans are in different lines, expected `;` and found `let` or `return`.
-                // High likelihood that it is only a missing `;`.
-                err.span_suggestion_short(
-                    label_sp,
-                    "a semicolon may be missing here",
-                    ";".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
-                err.emit();
-                return Ok(true);
-            }
-            (Ok(ref a), Ok(ref b)) if a.line == b.line => {
-                // When the spans are in the same line, it means that the only content between
-                // them is whitespace, point at the found token in that case:
-                //
-                // X |     () => { syntax error };
-                //   |                    ^^^^^ expected one of 8 possible tokens here
-                //
-                // instead of having:
-                //
-                // X |     () => { syntax error };
-                //   |                   -^^^^^ unexpected token
-                //   |                   |
-                //   |                   expected one of 8 possible tokens here
-                err.span_label(self.token.span, label_exp);
-            }
-            _ if self.prev_span == syntax_pos::DUMMY_SP => {
-                // Account for macro context where the previous span might not be
-                // available to avoid incorrect output (#54841).
-                err.span_label(self.token.span, "unexpected token");
-            }
-            _ => {
-                err.span_label(sp, label_exp);
-                err.span_label(self.token.span, "unexpected token");
-            }
+        if self.prev_span == DUMMY_SP {
+            // Account for macro context where the previous span might not be
+            // available to avoid incorrect output (#54841).
+            err.span_label(self.token.span, label_exp);
+        } else if !sm.is_multiline(self.token.span.shrink_to_hi().until(sp.shrink_to_lo())) {
+            // When the spans are in the same line, it means that the only content between
+            // them is whitespace, point at the found token in that case:
+            //
+            // X |     () => { syntax error };
+            //   |                    ^^^^^ expected one of 8 possible tokens here
+            //
+            // instead of having:
+            //
+            // X |     () => { syntax error };
+            //   |                   -^^^^^ unexpected token
+            //   |                   |
+            //   |                   expected one of 8 possible tokens here
+            err.span_label(self.token.span, label_exp);
+        } else {
+            err.span_label(sp, label_exp);
+            err.span_label(self.token.span, "unexpected token");
         }
         self.maybe_annotate_with_ascription(&mut err, false);
         Err(err)
@@ -902,20 +872,64 @@ impl<'a> Parser<'a> {
             }
         }
         let sm = self.sess.source_map();
-        match (sm.lookup_line(prev_sp.lo()), sm.lookup_line(sp.lo())) {
-            (Ok(ref a), Ok(ref b)) if a.line == b.line => {
-                // When the spans are in the same line, it means that the only content
-                // between them is whitespace, point only at the found token.
-                err.span_label(sp, label_exp);
-            }
-            _ => {
-                err.span_label(prev_sp, label_exp);
-                err.span_label(sp, "unexpected token");
-            }
+        if !sm.is_multiline(prev_sp.until(sp)) {
+            // When the spans are in the same line, it means that the only content
+            // between them is whitespace, point only at the found token.
+            err.span_label(sp, label_exp);
+        } else {
+            err.span_label(prev_sp, label_exp);
+            err.span_label(sp, "unexpected token");
         }
         Err(err)
     }
 
+    pub(super) fn expect_semi(&mut self) -> PResult<'a, ()> {
+        if self.eat(&token::Semi) {
+            return Ok(());
+        }
+        let sm = self.sess.source_map();
+        let msg = format!("expected `;`, found `{}`", self.this_token_descr());
+        let appl = Applicability::MachineApplicable;
+        if self.token.span == DUMMY_SP || self.prev_span == DUMMY_SP {
+            // Likely inside a macro, can't provide meaninful suggestions.
+            return self.expect(&token::Semi).map(|_| ());
+        } else if !sm.is_multiline(self.prev_span.until(self.token.span)) {
+            // The current token is in the same line as the prior token, not recoverable.
+        } else if self.look_ahead(1, |t| t == &token::CloseDelim(token::Brace)
+            || token_can_begin_expr(t) && t.kind != token::Colon
+        ) && [token::Comma, token::Colon].contains(&self.token.kind) {
+            // Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
+            // either `,` or `:`, and the next token could either start a new statement or is a
+            // block close. For example:
+            //
+            //   let x = 32:
+            //   let y = 42;
+            self.bump();
+            let sp = self.prev_span;
+            self.struct_span_err(sp, &msg)
+                .span_suggestion(sp, "change this to `;`", ";".to_string(), appl)
+                .emit();
+            return Ok(())
+        } else if self.look_ahead(0, |t| t == &token::CloseDelim(token::Brace) || (
+                token_can_begin_expr(t)
+                && t != &token::Semi
+                && t != &token::Pound // Avoid triggering with too many trailing `#` in raw string.
+        )) {
+            // Missing semicolon typo. This is triggered if the next token could either start a
+            // new statement or is a block close. For example:
+            //
+            //   let x = 32
+            //   let y = 42;
+            let sp = self.prev_span.shrink_to_hi();
+            self.struct_span_err(sp, &msg)
+                .span_label(self.token.span, "unexpected token")
+                .span_suggestion_short(sp, "add `;` here", ";".to_string(), appl)
+                .emit();
+            return Ok(())
+        }
+        self.expect(&token::Semi).map(|_| ()) // Error unconditionally
+    }
+
     pub(super) fn parse_semi_or_incorrect_foreign_fn_body(
         &mut self,
         ident: &Ident,
@@ -943,7 +957,7 @@ impl<'a> Parser<'a> {
                 Err(mut err) => {
                     err.cancel();
                     mem::replace(self, parser_snapshot);
-                    self.expect(&token::Semi)?;
+                    self.expect_semi()?;
                 }
             }
         } else {
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index 506a1a2a27a..fe125336190 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -98,7 +98,7 @@ impl<'a> Parser<'a> {
         if self.eat_keyword(kw::Use) {
             // USE ITEM
             let item_ = ItemKind::Use(P(self.parse_use_tree()?));
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
 
             let span = lo.to(self.prev_span);
             let item = self.mk_item(span, Ident::invalid(), item_, vis, attrs);
@@ -526,7 +526,7 @@ impl<'a> Parser<'a> {
             // eat a matched-delimiter token tree:
             let (delim, tts) = self.expect_delimited_token_tree()?;
             if delim != MacDelimiter::Brace {
-                self.expect(&token::Semi)?;
+                self.expect_semi()?;
             }
 
             Ok(Some(Mac {
@@ -776,7 +776,7 @@ impl<'a> Parser<'a> {
         let typ = self.parse_ty()?;
         self.expect(&token::Eq)?;
         let expr = self.parse_expr()?;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((name, ImplItemKind::Const(typ, expr), Generics::default()))
     }
 
@@ -813,7 +813,7 @@ impl<'a> Parser<'a> {
 
             let bounds = self.parse_generic_bounds(None)?;
             tps.where_clause = self.parse_where_clause()?;
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
 
             let whole_span = lo.to(self.prev_span);
             if is_auto == IsAuto::Yes {
@@ -927,7 +927,7 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((ident, TraitItemKind::Const(ty, default), Generics::default()))
     }
 
@@ -951,7 +951,7 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
 
         Ok((ident, TraitItemKind::Type(bounds, default), generics))
     }
@@ -1054,7 +1054,7 @@ impl<'a> Parser<'a> {
         } else {
             (orig_name, None)
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
 
         let span = lo.to(self.prev_span);
         Ok(self.mk_item(span, item_name, ItemKind::ExternCrate(orig_name), visibility, attrs))
@@ -1217,7 +1217,7 @@ impl<'a> Parser<'a> {
         self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
         let hi = self.token.span;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok(ForeignItem {
             ident,
             attrs,
@@ -1235,7 +1235,7 @@ impl<'a> Parser<'a> {
 
         let ident = self.parse_ident()?;
         let hi = self.token.span;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok(ast::ForeignItem {
             ident,
             attrs,
@@ -1282,7 +1282,7 @@ impl<'a> Parser<'a> {
 
         self.expect(&token::Eq)?;
         let e = self.parse_expr()?;
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         let item = match m {
             Some(m) => ItemKind::Static(ty, m, e),
             None => ItemKind::Const(ty, e),
@@ -1344,7 +1344,7 @@ impl<'a> Parser<'a> {
             let ty = self.parse_ty()?;
             AliasKind::Weak(ty)
         };
-        self.expect(&token::Semi)?;
+        self.expect_semi()?;
         Ok((ident, alias, tps))
     }
 
@@ -1468,7 +1468,7 @@ impl<'a> Parser<'a> {
         } else if self.token == token::OpenDelim(token::Paren) {
             let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
             generics.where_clause = self.parse_where_clause()?;
-            self.expect(&token::Semi)?;
+            self.expect_semi()?;
             body
         } else {
             let token_str = self.this_token_descr();
diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs
index ea7e4c05ea1..4f51fefe66f 100644
--- a/src/libsyntax/parse/parser/stmt.rs
+++ b/src/libsyntax/parse/parser/stmt.rs
@@ -432,6 +432,7 @@ impl<'a> Parser<'a> {
             None => return Ok(None),
         };
 
+        let mut eat_semi = true;
         match stmt.kind {
             StmtKind::Expr(ref expr) if self.token != token::Eof => {
                 // expression without semicolon
@@ -453,13 +454,14 @@ impl<'a> Parser<'a> {
                 if macro_legacy_warnings && self.token != token::Semi {
                     self.warn_missing_semicolon();
                 } else {
-                    self.expect_one_of(&[], &[token::Semi])?;
+                    self.expect_semi()?;
+                    eat_semi = false;
                 }
             }
             _ => {}
         }
 
-        if self.eat(&token::Semi) {
+        if eat_semi && self.eat(&token::Semi) {
             stmt = stmt.add_trailing_semicolon();
         }
         stmt.span = stmt.span.to(self.prev_span);
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 4a8b25c6107..03e77b199cc 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -143,34 +143,35 @@ impl Lit {
 
 pub(crate) fn ident_can_begin_expr(name: ast::Name, span: Span, is_raw: bool) -> bool {
     let ident_token = Token::new(Ident(name, is_raw), span);
+    token_can_begin_expr(&ident_token)
+}
 
+pub(crate) fn token_can_begin_expr(ident_token: &Token) -> bool {
     !ident_token.is_reserved_ident() ||
     ident_token.is_path_segment_keyword() ||
-    [
-        kw::Async,
-
-        // FIXME: remove when `await!(..)` syntax is removed
-        // https://github.com/rust-lang/rust/issues/60610
-        kw::Await,
-
-        kw::Do,
-        kw::Box,
-        kw::Break,
-        kw::Continue,
-        kw::False,
-        kw::For,
-        kw::If,
-        kw::Let,
-        kw::Loop,
-        kw::Match,
-        kw::Move,
-        kw::Return,
-        kw::True,
-        kw::Unsafe,
-        kw::While,
-        kw::Yield,
-        kw::Static,
-    ].contains(&name)
+    match ident_token.kind {
+        TokenKind::Ident(ident, _) => [
+            kw::Async,
+            kw::Do,
+            kw::Box,
+            kw::Break,
+            kw::Continue,
+            kw::False,
+            kw::For,
+            kw::If,
+            kw::Let,
+            kw::Loop,
+            kw::Match,
+            kw::Move,
+            kw::Return,
+            kw::True,
+            kw::Unsafe,
+            kw::While,
+            kw::Yield,
+            kw::Static,
+        ].contains(&ident),
+        _=> false,
+    }
 }
 
 fn ident_can_begin_type(name: ast::Name, span: Span, is_raw: bool) -> bool {
diff --git a/src/test/ui/async-await/no-async-const.rs b/src/test/ui/async-await/no-async-const.rs
index 7a6eb498b2e..44f02d1a7b1 100644
--- a/src/test/ui/async-await/no-async-const.rs
+++ b/src/test/ui/async-await/no-async-const.rs
@@ -3,4 +3,4 @@
 // compile-flags: --crate-type lib
 
 pub async const fn x() {}
-//~^ ERROR expected one of `fn` or `unsafe`, found `const`
+//~^ ERROR expected one of `fn` or `unsafe`, found keyword `const`
diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr
index edbdfb56522..f89d1810ba4 100644
--- a/src/test/ui/async-await/no-async-const.stderr
+++ b/src/test/ui/async-await/no-async-const.stderr
@@ -1,4 +1,4 @@
-error: expected one of `fn` or `unsafe`, found `const`
+error: expected one of `fn` or `unsafe`, found keyword `const`
   --> $DIR/no-async-const.rs:5:11
    |
 LL | pub async const fn x() {}
diff --git a/src/test/ui/async-await/no-unsafe-async.rs b/src/test/ui/async-await/no-unsafe-async.rs
index 81e0cd799ad..1ac1bdffda9 100644
--- a/src/test/ui/async-await/no-unsafe-async.rs
+++ b/src/test/ui/async-await/no-unsafe-async.rs
@@ -4,8 +4,8 @@ struct S;
 
 impl S {
     #[cfg(FALSE)]
-    unsafe async fn g() {} //~ ERROR expected one of `extern` or `fn`, found `async`
+    unsafe async fn g() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async`
 }
 
 #[cfg(FALSE)]
-unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found `async`
+unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found keyword `async`
diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr
index c339c7c3bf5..79d9f1befd6 100644
--- a/src/test/ui/async-await/no-unsafe-async.stderr
+++ b/src/test/ui/async-await/no-unsafe-async.stderr
@@ -1,10 +1,10 @@
-error: expected one of `extern` or `fn`, found `async`
+error: expected one of `extern` or `fn`, found keyword `async`
   --> $DIR/no-unsafe-async.rs:7:12
    |
 LL |     unsafe async fn g() {}
    |            ^^^^^ expected one of `extern` or `fn` here
 
-error: expected one of `extern`, `fn`, or `{`, found `async`
+error: expected one of `extern`, `fn`, or `{`, found keyword `async`
   --> $DIR/no-unsafe-async.rs:11:8
    |
 LL | unsafe async fn f() {}
diff --git a/src/test/ui/can-begin-expr-check.rs b/src/test/ui/can-begin-expr-check.rs
index 35aed067c69..8974d9f48c1 100644
--- a/src/test/ui/can-begin-expr-check.rs
+++ b/src/test/ui/can-begin-expr-check.rs
@@ -16,5 +16,5 @@ pub fn main() {
         return break as ();
     }
 
-    return enum; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `enum`
+    return enum; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
 }
diff --git a/src/test/ui/can-begin-expr-check.stderr b/src/test/ui/can-begin-expr-check.stderr
index 676c2cb661e..0e03e9915fc 100644
--- a/src/test/ui/can-begin-expr-check.stderr
+++ b/src/test/ui/can-begin-expr-check.stderr
@@ -1,4 +1,4 @@
-error: expected one of `.`, `;`, `?`, `}`, or an operator, found `enum`
+error: expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `enum`
   --> $DIR/can-begin-expr-check.rs:19:12
    |
 LL |     return enum;
diff --git a/src/test/ui/conflicting-repr-hints.stderr b/src/test/ui/conflicting-repr-hints.stderr
index 832f5c3ac2b..414c15f93bc 100644
--- a/src/test/ui/conflicting-repr-hints.stderr
+++ b/src/test/ui/conflicting-repr-hints.stderr
@@ -66,4 +66,5 @@ LL | | }
 
 error: aborting due to 8 previous errors
 
-For more information about this error, try `rustc --explain E0566`.
+Some errors have detailed explanations: E0566, E0587.
+For more information about an error, try `rustc --explain E0566`.
diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
index be195271c10..c3c554d7d27 100644
--- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
+++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
@@ -3,9 +3,16 @@
 
 struct Bar;
 
+// This function would compile with the feature gate, and tests that it is suggested.
 fn foo() {
     let arr: [Option<String>; 2] = [None::<String>; 2];
     //~^ ERROR the trait bound `std::option::Option<std::string::String>: std::marker::Copy` is not satisfied [E0277]
 }
 
+// This function would not compile with the feature gate, and tests that it is not suggested.
+fn bar() {
+    let arr: [Option<String>; 2] = [Some("foo".to_string()); 2];
+    //~^ ERROR the trait bound `std::option::Option<std::string::String>: std::marker::Copy` is not satisfied [E0277]
+}
+
 fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr
index eed69a0c28d..cd9242de88f 100644
--- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr
+++ b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `std::option::Option<std::string::String>: std::marker::Copy` is not satisfied
-  --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:7:36
+  --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:8:36
    |
 LL |     let arr: [Option<String>; 2] = [None::<String>; 2];
    |                                    ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option<std::string::String>`
@@ -7,7 +7,19 @@ LL |     let arr: [Option<String>; 2] = [None::<String>; 2];
    = help: the following implementations were found:
              <std::option::Option<T> as std::marker::Copy>
    = note: the `Copy` trait is required because the repeated element will be copied
+   = note: this array initializer can be evaluated at compile-time, for more information, see issue https://github.com/rust-lang/rust/issues/49147
+   = help: add `#![feature(const_in_array_repeat_expression)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error[E0277]: the trait bound `std::option::Option<std::string::String>: std::marker::Copy` is not satisfied
+  --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:14:36
+   |
+LL |     let arr: [Option<String>; 2] = [Some("foo".to_string()); 2];
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option<std::string::String>`
+   |
+   = help: the following implementations were found:
+             <std::option::Option<T> as std::marker::Copy>
+   = note: the `Copy` trait is required because the repeated element will be copied
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/issues/issue-43196.stderr b/src/test/ui/issues/issue-43196.stderr
index 32efe23c72b..4f7ed5cc6fd 100644
--- a/src/test/ui/issues/issue-43196.stderr
+++ b/src/test/ui/issues/issue-43196.stderr
@@ -2,7 +2,7 @@ error: expected `|`, found `}`
   --> $DIR/issue-43196.rs:3:1
    |
 LL |     |
-   |      - expected `|` here
+   |      - expected `|`
 LL | }
    | ^ unexpected token
 
diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
index 447c76a5bbc..48c2f556f1d 100644
--- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
+++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-type.stderr
@@ -2,7 +2,7 @@ error: expected `fn`, found `::`
   --> $DIR/keyword-extern-as-identifier-type.rs:1:16
    |
 LL | type A = extern::foo::bar;
-   |                ^^ expected `fn` here
+   |                ^^ expected `fn`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/macros/issue-54441.stderr b/src/test/ui/macros/issue-54441.stderr
index af0ee3ae8ec..287d579c76d 100644
--- a/src/test/ui/macros/issue-54441.stderr
+++ b/src/test/ui/macros/issue-54441.stderr
@@ -1,8 +1,8 @@
-error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found `let`
+error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found keyword `let`
   --> $DIR/issue-54441.rs:3:9
    |
 LL |         let
-   |         ^^^ unexpected token
+   |         ^^^ expected one of `crate`, `fn`, `pub`, `static`, or `type` here
 ...
 LL |     m!();
    |     ----- in this macro invocation
diff --git a/src/test/ui/parser/default.stderr b/src/test/ui/parser/default.stderr
index e199045134e..8843fd303ec 100644
--- a/src/test/ui/parser/default.stderr
+++ b/src/test/ui/parser/default.stderr
@@ -1,4 +1,4 @@
-error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found `pub`
+error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, found keyword `pub`
   --> $DIR/default.rs:22:13
    |
 LL |     default pub fn foo<T: Default>() -> T { T::default() }
diff --git a/src/test/ui/parser/duplicate-visibility.stderr b/src/test/ui/parser/duplicate-visibility.stderr
index 880b536cd18..675adb88d20 100644
--- a/src/test/ui/parser/duplicate-visibility.stderr
+++ b/src/test/ui/parser/duplicate-visibility.stderr
@@ -1,4 +1,4 @@
-error: expected one of `(`, `fn`, `static`, or `type`, found `pub`
+error: expected one of `(`, `fn`, `static`, or `type`, found keyword `pub`
   --> $DIR/duplicate-visibility.rs:3:9
    |
 LL |     pub pub fn foo();
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.rs b/src/test/ui/parser/extern-expected-fn-or-brace.rs
index dd46b87fa42..907de0d8f91 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.rs
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.rs
@@ -1,4 +1,4 @@
 // Verifies that the expected token errors for `extern crate` are
 // raised
 
-extern "C" mod foo; //~ERROR expected one of `fn` or `{`, found `mod`
+extern "C" mod foo; //~ERROR expected one of `fn` or `{`, found keyword `mod`
diff --git a/src/test/ui/parser/extern-expected-fn-or-brace.stderr b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
index 0fb99355341..691f4cddff2 100644
--- a/src/test/ui/parser/extern-expected-fn-or-brace.stderr
+++ b/src/test/ui/parser/extern-expected-fn-or-brace.stderr
@@ -1,4 +1,4 @@
-error: expected one of `fn` or `{`, found `mod`
+error: expected one of `fn` or `{`, found keyword `mod`
   --> $DIR/extern-expected-fn-or-brace.rs:4:12
    |
 LL | extern "C" mod foo;
diff --git a/src/test/ui/parser/impl-parsing.stderr b/src/test/ui/parser/impl-parsing.stderr
index 935e93963e1..e929fa53620 100644
--- a/src/test/ui/parser/impl-parsing.stderr
+++ b/src/test/ui/parser/impl-parsing.stderr
@@ -26,7 +26,7 @@ error: expected `impl`, found `FAIL`
   --> $DIR/impl-parsing.rs:11:16
    |
 LL | default unsafe FAIL
-   |                ^^^^ expected `impl` here
+   |                ^^^^ expected `impl`
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/parser/issue-15980.rs b/src/test/ui/parser/issue-15980.rs
index ba874fb4476..beb94c8042d 100644
--- a/src/test/ui/parser/issue-15980.rs
+++ b/src/test/ui/parser/issue-15980.rs
@@ -11,7 +11,7 @@ fn main(){
         }
         //~^ NOTE expected one of `.`, `=>`, `?`, or an operator here
         _ => {}
-        //~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found `_`
+        //~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
         //~| NOTE unexpected token
     }
 }
diff --git a/src/test/ui/parser/issue-15980.stderr b/src/test/ui/parser/issue-15980.stderr
index 47c275110b4..26f75d45fa2 100644
--- a/src/test/ui/parser/issue-15980.stderr
+++ b/src/test/ui/parser/issue-15980.stderr
@@ -12,7 +12,7 @@ help: you can escape reserved keywords to use them as identifiers
 LL |             r#return
    |
 
-error: expected one of `.`, `=>`, `?`, or an operator, found `_`
+error: expected one of `.`, `=>`, `?`, or an operator, found reserved identifier `_`
   --> $DIR/issue-15980.rs:13:9
    |
 LL |         }
diff --git a/src/test/ui/parser/issue-19398.rs b/src/test/ui/parser/issue-19398.rs
index 822f4a6fde2..90221039b41 100644
--- a/src/test/ui/parser/issue-19398.rs
+++ b/src/test/ui/parser/issue-19398.rs
@@ -1,5 +1,5 @@
 trait T {
-    extern "Rust" unsafe fn foo(); //~ ERROR expected `fn`, found `unsafe`
+    extern "Rust" unsafe fn foo(); //~ ERROR expected `fn`, found keyword `unsafe`
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/issue-19398.stderr b/src/test/ui/parser/issue-19398.stderr
index d5f1f972d55..41ec4f3ced4 100644
--- a/src/test/ui/parser/issue-19398.stderr
+++ b/src/test/ui/parser/issue-19398.stderr
@@ -1,8 +1,8 @@
-error: expected `fn`, found `unsafe`
+error: expected `fn`, found keyword `unsafe`
   --> $DIR/issue-19398.rs:2:19
    |
 LL |     extern "Rust" unsafe fn foo();
-   |                   ^^^^^^ expected `fn` here
+   |                   ^^^^^^ expected `fn`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/issue-3036.rs b/src/test/ui/parser/issue-3036.rs
index 00b241b9054..6a8b67fefa7 100644
--- a/src/test/ui/parser/issue-3036.rs
+++ b/src/test/ui/parser/issue-3036.rs
@@ -2,5 +2,5 @@
 
 fn main()
 {
-    let x = 3
-} //~ ERROR: expected one of `.`, `;`, `?`, or an operator, found `}`
+    let x = 3 //~ ERROR: expected `;`
+}
diff --git a/src/test/ui/parser/issue-3036.stderr b/src/test/ui/parser/issue-3036.stderr
index 18947b8fa40..b6557163d45 100644
--- a/src/test/ui/parser/issue-3036.stderr
+++ b/src/test/ui/parser/issue-3036.stderr
@@ -1,10 +1,10 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `}`
-  --> $DIR/issue-3036.rs:6:1
+error: expected `;`, found ``}``
+  --> $DIR/issue-3036.rs:5:14
    |
 LL |     let x = 3
-   |              - expected one of `.`, `;`, `?`, or an operator here
+   |              ^ help: add `;` here
 LL | }
-   | ^ unexpected token
+   | - unexpected token
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/macro/trait-non-item-macros.stderr b/src/test/ui/parser/macro/trait-non-item-macros.stderr
index 76670e0bde0..a953e23a710 100644
--- a/src/test/ui/parser/macro/trait-non-item-macros.stderr
+++ b/src/test/ui/parser/macro/trait-non-item-macros.stderr
@@ -2,7 +2,7 @@ error: expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe`, fo
   --> $DIR/trait-non-item-macros.rs:2:19
    |
 LL |     ($a:expr) => ($a)
-   |                   ^^ unexpected token
+   |                   ^^ expected one of `async`, `const`, `extern`, `fn`, `type`, or `unsafe` here
 ...
 LL |     bah!(2);
    |     -------- in this macro invocation
diff --git a/src/test/ui/parser/raw/raw-literal-keywords.rs b/src/test/ui/parser/raw/raw-literal-keywords.rs
index 6b055fbb117..bf9cbcdab2e 100644
--- a/src/test/ui/parser/raw/raw-literal-keywords.rs
+++ b/src/test/ui/parser/raw/raw-literal-keywords.rs
@@ -1,5 +1,5 @@
 fn test_if() {
-    r#if true { } //~ ERROR found `true`
+    r#if true { } //~ ERROR found keyword `true`
 }
 
 fn test_struct() {
diff --git a/src/test/ui/parser/raw/raw-literal-keywords.stderr b/src/test/ui/parser/raw/raw-literal-keywords.stderr
index f39e29cfaa8..4cea605be6f 100644
--- a/src/test/ui/parser/raw/raw-literal-keywords.stderr
+++ b/src/test/ui/parser/raw/raw-literal-keywords.stderr
@@ -1,4 +1,4 @@
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `true`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found keyword `true`
   --> $DIR/raw-literal-keywords.rs:2:10
    |
 LL |     r#if true { }
diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.rs b/src/test/ui/parser/recover-for-loop-parens-around-head.rs
index c6be2c90667..779e1646344 100644
--- a/src/test/ui/parser/recover-for-loop-parens-around-head.rs
+++ b/src/test/ui/parser/recover-for-loop-parens-around-head.rs
@@ -8,7 +8,7 @@ fn main() {
     let vec = vec![1, 2, 3];
 
     for ( elem in vec ) {
-        //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found `in`
+        //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in`
         //~| ERROR unexpected closing `)`
         const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
     }
diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
index 1b5b6cca092..1a1f395ee21 100644
--- a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
+++ b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr
@@ -1,4 +1,4 @@
-error: expected one of `)`, `,`, `@`, or `|`, found `in`
+error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
   --> $DIR/recover-for-loop-parens-around-head.rs:10:16
    |
 LL |     for ( elem in vec ) {
diff --git a/src/test/ui/parser/recover-missing-semi.rs b/src/test/ui/parser/recover-missing-semi.rs
index 1893dc716be..f47d5e6805f 100644
--- a/src/test/ui/parser/recover-missing-semi.rs
+++ b/src/test/ui/parser/recover-missing-semi.rs
@@ -1,13 +1,13 @@
 fn main() {
     let _: usize = ()
     //~^ ERROR mismatched types
+    //~| ERROR expected `;`
     let _ = 3;
-    //~^ ERROR expected one of
 }
 
 fn foo() -> usize {
     let _: usize = ()
     //~^ ERROR mismatched types
+    //~| ERROR expected `;`
     return 3;
-    //~^ ERROR expected one of
 }
diff --git a/src/test/ui/parser/recover-missing-semi.stderr b/src/test/ui/parser/recover-missing-semi.stderr
index 99339e4dd50..c40918ee2bd 100644
--- a/src/test/ui/parser/recover-missing-semi.stderr
+++ b/src/test/ui/parser/recover-missing-semi.stderr
@@ -1,20 +1,20 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `let`
-  --> $DIR/recover-missing-semi.rs:4:5
+error: expected `;`, found `keyword `let``
+  --> $DIR/recover-missing-semi.rs:2:22
    |
 LL |     let _: usize = ()
-   |                      - help: a semicolon may be missing here
-LL |
+   |                      ^ help: add `;` here
+...
 LL |     let _ = 3;
-   |     ^^^
+   |     --- unexpected token
 
-error: expected one of `.`, `;`, `?`, or an operator, found `return`
-  --> $DIR/recover-missing-semi.rs:11:5
+error: expected `;`, found `keyword `return``
+  --> $DIR/recover-missing-semi.rs:9:22
    |
 LL |     let _: usize = ()
-   |                      - help: a semicolon may be missing here
-LL |
+   |                      ^ help: add `;` here
+...
 LL |     return 3;
-   |     ^^^^^^
+   |     ------ unexpected token
 
 error[E0308]: mismatched types
   --> $DIR/recover-missing-semi.rs:2:20
diff --git a/src/test/ui/parser/removed-syntax-static-fn.stderr b/src/test/ui/parser/removed-syntax-static-fn.stderr
index 21cb71df657..af148e69711 100644
--- a/src/test/ui/parser/removed-syntax-static-fn.stderr
+++ b/src/test/ui/parser/removed-syntax-static-fn.stderr
@@ -1,4 +1,4 @@
-error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `static`
+error: expected one of `async`, `const`, `crate`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found keyword `static`
   --> $DIR/removed-syntax-static-fn.rs:4:5
    |
 LL | impl S {
diff --git a/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs b/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
index 79d51f5595d..f9a9d071a3d 100644
--- a/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
+++ b/src/test/ui/parser/removed-syntax-uniq-mut-ty.rs
@@ -1 +1,2 @@
-type mut_box = Box<mut isize>; //~ ERROR expected one of `>`, const, lifetime, or type, found `mut`
+type mut_box = Box<mut isize>;
+//~^ ERROR expected one of `>`, const, lifetime, or type, found keyword `mut`
diff --git a/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr b/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
index b6c5749c031..9c47e3db67d 100644
--- a/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
+++ b/src/test/ui/parser/removed-syntax-uniq-mut-ty.stderr
@@ -1,4 +1,4 @@
-error: expected one of `>`, const, lifetime, or type, found `mut`
+error: expected one of `>`, const, lifetime, or type, found keyword `mut`
   --> $DIR/removed-syntax-uniq-mut-ty.rs:1:20
    |
 LL | type mut_box = Box<mut isize>;
diff --git a/src/test/ui/parser/underscore_item_not_const.rs b/src/test/ui/parser/underscore_item_not_const.rs
index 375bdc3a463..7b0d128f06f 100644
--- a/src/test/ui/parser/underscore_item_not_const.rs
+++ b/src/test/ui/parser/underscore_item_not_const.rs
@@ -25,6 +25,6 @@ use _ as g; //~ ERROR expected identifier, found reserved identifier `_`
 trait _ {} //~ ERROR expected identifier, found reserved identifier `_`
 trait _ = Copy; //~ ERROR expected identifier, found reserved identifier `_`
 macro_rules! _ { () => {} } //~ ERROR expected identifier, found reserved identifier `_`
-union _ { f: u8 } //~ ERROR expected one of `!` or `::`, found `_`
+union _ { f: u8 } //~ ERROR expected one of `!` or `::`, found reserved identifier `_`
 
 fn main() {}
diff --git a/src/test/ui/parser/underscore_item_not_const.stderr b/src/test/ui/parser/underscore_item_not_const.stderr
index deb4a012e32..8814aa35271 100644
--- a/src/test/ui/parser/underscore_item_not_const.stderr
+++ b/src/test/ui/parser/underscore_item_not_const.stderr
@@ -82,7 +82,7 @@ error: expected identifier, found reserved identifier `_`
 LL | macro_rules! _ { () => {} }
    |              ^ expected identifier, found reserved identifier
 
-error: expected one of `!` or `::`, found `_`
+error: expected one of `!` or `::`, found reserved identifier `_`
   --> $DIR/underscore_item_not_const.rs:28:7
    |
 LL | union _ { f: u8 }