diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 72dd44a4b4d98..35eca23a11625 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -50,11 +50,12 @@ pub enum Delimiter {
     Brace,
     /// `[ ... ]`
     Bracket,
-    /// `Ø ... Ø`
+    /// `/*«*/ ... /*»*/`
     /// An invisible delimiter, that may, for example, appear around tokens coming from a
     /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
     /// `$var * 3` where `$var` is `1 + 2`.
-    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+    /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+    /// Therefore, they might not survive a roundtrip of a token stream through a string.
     Invisible,
 }
 
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index e79f4f0a0950d..c02cdc295610f 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -590,15 +590,29 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.nbsp();
                 }
                 self.word("{");
-                if !tts.is_empty() {
+                let empty = tts.is_empty();
+                if !empty {
                     self.space();
                 }
                 self.ibox(0);
                 self.print_tts(tts, convert_dollar_crate);
                 self.end();
-                let empty = tts.is_empty();
                 self.bclose(span, empty);
             }
+            Some(Delimiter::Invisible) => {
+                self.word("/*«*/");
+                let empty = tts.is_empty();
+                if !empty {
+                    self.space();
+                }
+                self.ibox(0);
+                self.print_tts(tts, convert_dollar_crate);
+                self.end();
+                if !empty {
+                    self.space();
+                }
+                self.word("/*»*/");
+            }
             Some(delim) => {
                 let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
                 self.word(token_str);
@@ -772,9 +786,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             token::CloseDelim(Delimiter::Bracket) => "]".into(),
             token::OpenDelim(Delimiter::Brace) => "{".into(),
             token::CloseDelim(Delimiter::Brace) => "}".into(),
-            token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
-                "".into()
-            }
+            token::OpenDelim(Delimiter::Invisible) => "/*«*/".into(),
+            token::CloseDelim(Delimiter::Invisible) => "/*»*/".into(),
             token::Pound => "#".into(),
             token::Dollar => "$".into(),
             token::Question => "?".into(),
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index f2d833b320249..a8a5ac2f9d95d 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -84,14 +84,18 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
     }
 
     #[inline]
-    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+    pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) {
         match self {
-            Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
-            Immediate::Scalar(..) => {
-                bug!("Got a scalar where a scalar pair was expected")
-            }
+            Immediate::ScalarPair(val1, val2) => (val1, val2),
+            Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"),
         }
     }
+
+    #[inline]
+    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+        let (val1, val2) = self.to_scalar_or_uninit_pair();
+        Ok((val1.check_init()?, val2.check_init()?))
+    }
 }
 
 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
@@ -248,9 +252,12 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
     /// Returns `None` if the layout does not permit loading this as a value.
-    fn try_read_immediate_from_mplace(
+    ///
+    /// This is an internal function; call `read_immediate` instead.
+    fn read_immediate_from_mplace_raw(
         &self,
         mplace: &MPlaceTy<'tcx, M::PointerTag>,
+        force: bool,
     ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
         if mplace.layout.is_unsized() {
             // Don't touch unsized
@@ -271,42 +278,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // case where some of the bytes are initialized and others are not. So, we need an extra
         // check that walks over the type of `mplace` to make sure it is truly correct to treat this
         // like a `Scalar` (or `ScalarPair`).
-        match mplace.layout.abi {
-            Abi::Scalar(abi::Scalar::Initialized { .. }) => {
-                let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
-                Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
-            }
+        let scalar_layout = match mplace.layout.abi {
+            // `if` does not work nested inside patterns, making this a bit awkward to express.
+            Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => Some(s),
+            Abi::Scalar(s) if force => Some(s.primitive()),
+            _ => None,
+        };
+        if let Some(_) = scalar_layout {
+            let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
+            return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
+        }
+        let scalar_pair_layout = match mplace.layout.abi {
             Abi::ScalarPair(
                 abi::Scalar::Initialized { value: a, .. },
                 abi::Scalar::Initialized { value: b, .. },
-            ) => {
-                // We checked `ptr_align` above, so all fields will have the alignment they need.
-                // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
-                // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
-                let (a_size, b_size) = (a.size(self), b.size(self));
-                let b_offset = a_size.align_to(b.align(self).abi);
-                assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
-                let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
-                let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
-                Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout }))
-            }
-            _ => Ok(None),
+            ) => Some((a, b)),
+            Abi::ScalarPair(a, b) if force => Some((a.primitive(), b.primitive())),
+            _ => None,
+        };
+        if let Some((a, b)) = scalar_pair_layout {
+            // We checked `ptr_align` above, so all fields will have the alignment they need.
+            // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
+            // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
+            let (a_size, b_size) = (a.size(self), b.size(self));
+            let b_offset = a_size.align_to(b.align(self).abi);
+            assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
+            let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
+            let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
+            return Ok(Some(ImmTy {
+                imm: Immediate::ScalarPair(a_val, b_val),
+                layout: mplace.layout,
+            }));
         }
+        // Neither a scalar nor scalar pair.
+        return Ok(None);
     }
 
-    /// Try returning an immediate for the operand.
-    /// If the layout does not permit loading this as an immediate, return where in memory
-    /// we can find the data.
+    /// Try returning an immediate for the operand. If the layout does not permit loading this as an
+    /// immediate, return where in memory we can find the data.
     /// Note that for a given layout, this operation will either always fail or always
     /// succeed!  Whether it succeeds depends on whether the layout can be represented
     /// in an `Immediate`, not on which data is stored there currently.
-    pub fn try_read_immediate(
+    ///
+    /// If `force` is `true`, then even scalars with fields that can be ununit will be
+    /// read. This means the load is lossy and should not be written back!
+    /// This flag exists only for validity checking.
+    ///
+    /// This is an internal function that should not usually be used; call `read_immediate` instead.
+    pub fn read_immediate_raw(
         &self,
         src: &OpTy<'tcx, M::PointerTag>,
+        force: bool,
     ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
         Ok(match src.try_as_mplace() {
             Ok(ref mplace) => {
-                if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
+                if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? {
                     Ok(val)
                 } else {
                     Err(*mplace)
@@ -322,7 +348,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         op: &OpTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
-        if let Ok(imm) = self.try_read_immediate(op)? {
+        if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? {
             Ok(imm)
         } else {
             span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty);
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 380eb5263618b..df6e05bb13cde 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -720,7 +720,7 @@ where
         }
         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
 
-        // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`,
+        // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
         // but not factored as a separate function.
         let mplace = match dest.place {
             Place::Local { frame, local } => {
@@ -879,7 +879,7 @@ where
         }
 
         // Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
-        let src = match self.try_read_immediate(src)? {
+        let src = match self.read_immediate_raw(src, /*force*/ false)? {
             Ok(src_val) => {
                 assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
                 // Yay, we got a value that we can write directly.
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 71d29be97d5ec..92e3ac04dc418 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -20,8 +20,8 @@ use rustc_target::abi::{Abi, Scalar as ScalarAbi, Size, VariantIdx, Variants, Wr
 use std::hash::Hash;
 
 use super::{
-    alloc_range, CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine,
-    MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
+    alloc_range, CheckInAllocMsg, GlobalAlloc, Immediate, InterpCx, InterpResult, MPlaceTy,
+    Machine, MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
 };
 
 macro_rules! throw_validation_failure {
@@ -487,6 +487,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         ))
     }
 
+    fn read_immediate_forced(
+        &self,
+        op: &OpTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+        Ok(*try_validation!(
+            self.ecx.read_immediate_raw(op, /*force*/ true),
+            self.path,
+            err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
+        ).unwrap())
+    }
+
     /// Check if this is a value of primitive type, and if yes check the validity of the value
     /// at that type.  Return `true` if the type is indeed primitive.
     fn try_visit_primitive(
@@ -626,18 +637,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
 
     fn visit_scalar(
         &mut self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        scalar: ScalarMaybeUninit<M::PointerTag>,
         scalar_layout: ScalarAbi,
     ) -> InterpResult<'tcx> {
         // We check `is_full_range` in a slightly complicated way because *if* we are checking
         // number validity, then we want to ensure that `Scalar::Initialized` is indeed initialized,
         // i.e. that we go over the `check_init` below.
+        let size = scalar_layout.size(self.ecx);
         let is_full_range = match scalar_layout {
             ScalarAbi::Initialized { valid_range, .. } => {
                 if M::enforce_number_validity(self.ecx) {
                     false // not "full" since uninit is not accepted
                 } else {
-                    valid_range.is_full_for(op.layout.size)
+                    valid_range.is_full_for(size)
                 }
             }
             ScalarAbi::Union { .. } => true,
@@ -646,21 +658,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
             // Nothing to check
             return Ok(());
         }
-        // We have something to check.
+        // We have something to check: it must at least be initialized.
         let valid_range = scalar_layout.valid_range(self.ecx);
         let WrappingRange { start, end } = valid_range;
-        let max_value = op.layout.size.unsigned_int_max();
+        let max_value = size.unsigned_int_max();
         assert!(end <= max_value);
-        // Determine the allowed range
-        let value = self.read_scalar(op)?;
         let value = try_validation!(
-            value.check_init(),
+            scalar.check_init(),
             self.path,
-            err_ub!(InvalidUninitBytes(None)) => { "{:x}", value }
+            err_ub!(InvalidUninitBytes(None)) => { "{:x}", scalar }
                 expected { "something {}", wrapping_range_format(valid_range, max_value) },
         );
         let bits = match value.try_to_int() {
-            Ok(int) => int.assert_bits(op.layout.size),
+            Ok(int) => int.assert_bits(size),
             Err(_) => {
                 // So this is a pointer then, and casting to an int failed.
                 // Can only happen during CTFE.
@@ -678,7 +688,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                     } else {
                         return Ok(());
                     }
-                } else if scalar_layout.valid_range(self.ecx).is_full_for(op.layout.size) {
+                } else if scalar_layout.valid_range(self.ecx).is_full_for(size) {
                     // Easy. (This is reachable if `enforce_number_validity` is set.)
                     return Ok(());
                 } else {
@@ -817,13 +827,23 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 );
             }
             Abi::Scalar(scalar_layout) => {
-                self.visit_scalar(op, scalar_layout)?;
+                let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
+                self.visit_scalar(scalar, scalar_layout)?;
+            }
+            Abi::ScalarPair(a_layout, b_layout) => {
+                // We would validate these things as we descend into the fields,
+                // but that can miss bugs in layout computation. Layout computation
+                // is subtle due to enums having ScalarPair layout, where one field
+                // is the discriminant.
+                if cfg!(debug_assertions) {
+                    let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
+                    self.visit_scalar(a, a_layout)?;
+                    self.visit_scalar(b, b_layout)?;
+                }
             }
-            Abi::ScalarPair { .. } | Abi::Vector { .. } => {
-                // These have fields that we already visited above, so we already checked
-                // all their scalar-level restrictions.
-                // There is also no equivalent to `rustc_layout_scalar_valid_range_start`
-                // that would make skipping them here an issue.
+            Abi::Vector { .. } => {
+                // No checks here, we assume layout computation gets this right.
+                // (This is harder to check since Miri does not represent these as `Immediate`.)
             }
             Abi::Aggregate { .. } => {
                 // Nothing to do.
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 691f4fb0e5425..f7535d338da40 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -415,7 +415,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
         // Try to read the local as an immediate so that if it is representable as a scalar, we can
         // handle it as such, but otherwise, just return the value as is.
-        Some(match self.ecx.try_read_immediate(&op) {
+        Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
             Ok(Ok(imm)) => imm.into(),
             _ => op,
         })
@@ -709,8 +709,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             return;
         }
 
-        // FIXME> figure out what to do when try_read_immediate fails
-        let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
+        // FIXME> figure out what to do when read_immediate_raw fails
+        let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value, /*force*/ false));
 
         if let Some(Ok(imm)) = imm {
             match *imm {
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 4945c10c9aaa9..aa898cfd3ba5e 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -412,7 +412,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
 
         // Try to read the local as an immediate so that if it is representable as a scalar, we can
         // handle it as such, but otherwise, just return the value as is.
-        Some(match self.ecx.try_read_immediate(&op) {
+        Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
             Ok(Ok(imm)) => imm.into(),
             _ => op,
         })
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 1bdf53cf84fed..df6733ac45f9b 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -486,6 +486,9 @@ struct DiagnosticMetadata<'ast> {
     current_where_predicate: Option<&'ast WherePredicate>,
 
     current_type_path: Option<&'ast Ty>,
+
+    /// The current impl items (used to suggest).
+    current_impl_items: Option<&'ast [P<AssocItem>]>,
 }
 
 struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -1637,7 +1640,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 items: ref impl_items,
                 ..
             }) => {
+                self.diagnostic_metadata.current_impl_items = Some(impl_items);
                 self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items);
+                self.diagnostic_metadata.current_impl_items = None;
             }
 
             ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 3076cc1131700..4f07d0076f140 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -6,7 +6,7 @@ use crate::path_names_to_string;
 use crate::{Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
 
-use rustc_ast::visit::FnKind;
+use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{
     self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
     NodeId, Path, Ty, TyKind,
@@ -144,15 +144,22 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
 
         // Make the base error.
+        struct BaseError<'a> {
+            msg: String,
+            fallback_label: String,
+            span: Span,
+            could_be_expr: bool,
+            suggestion: Option<(Span, &'a str, String)>,
+        }
         let mut expected = source.descr_expected();
         let path_str = Segment::names_to_string(path);
         let item_str = path.last().unwrap().ident;
-        let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
-            (
-                format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
-                format!("not a {}", expected),
+        let base_error = if let Some(res) = res {
+            BaseError {
+                msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
+                fallback_label: format!("not a {expected}"),
                 span,
-                match res {
+                could_be_expr: match res {
                     Res::Def(DefKind::Fn, _) => {
                         // Verify whether this is a fn call or an Fn used as a type.
                         self.r
@@ -171,22 +178,49 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     | Res::Local(_) => true,
                     _ => false,
                 },
-            )
+                suggestion: None,
+            }
         } else {
             let item_span = path.last().unwrap().ident.span;
-            let (mod_prefix, mod_str) = if path.len() == 1 {
-                (String::new(), "this scope".to_string())
+            let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
+                debug!(?self.diagnostic_metadata.current_impl_items);
+                debug!(?self.diagnostic_metadata.current_function);
+                let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items
+                    && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
+                    && self.current_trait_ref.is_none()
+                    && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+                    && let Some(item) = items.iter().find(|i| {
+                        if let AssocItemKind::Fn(fn_) = &i.kind
+                            && !fn_.sig.decl.has_self()
+                            && i.ident.name == item_str.name
+                        {
+                            debug!(?item_str.name);
+                            debug!(?fn_.sig.decl.inputs);
+                            return true
+                        }
+                        false
+                    })
+                {
+                    Some((
+                        item_span,
+                        "consider using the associated function",
+                        format!("Self::{}", item.ident)
+                    ))
+                } else {
+                    None
+                };
+                (String::new(), "this scope".to_string(), suggestion)
             } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
                 if self.r.session.edition() > Edition::Edition2015 {
                     // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
                     // which overrides all other expectations of item type
                     expected = "crate";
-                    (String::new(), "the list of imported crates".to_string())
+                    (String::new(), "the list of imported crates".to_string(), None)
                 } else {
-                    (String::new(), "the crate root".to_string())
+                    (String::new(), "the crate root".to_string(), None)
                 }
             } else if path.len() == 2 && path[0].ident.name == kw::Crate {
-                (String::new(), "the crate root".to_string())
+                (String::new(), "the crate root".to_string(), None)
             } else {
                 let mod_path = &path[..path.len() - 1];
                 let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
@@ -194,22 +228,28 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     _ => None,
                 }
                 .map_or_else(String::new, |res| format!("{} ", res.descr()));
-                (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
+                (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
             };
-            (
-                format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
-                if path_str == "async" && expected.starts_with("struct") {
+            BaseError {
+                msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
+                fallback_label: if path_str == "async" && expected.starts_with("struct") {
                     "`async` blocks are only allowed in Rust 2018 or later".to_string()
                 } else {
-                    format!("not found in {}", mod_str)
+                    format!("not found in {mod_str}")
                 },
-                item_span,
-                false,
-            )
+                span: item_span,
+                could_be_expr: false,
+                suggestion,
+            }
         };
 
         let code = source.error_code(res.is_some());
-        let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
+        let mut err =
+            self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
+
+        if let Some(sugg) = base_error.suggestion {
+            err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
+        }
 
         if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
             err.multipart_suggestion(
@@ -269,7 +309,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             }
         }
 
-        self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err);
+        self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err);
 
         // Emit special messages for unresolved `Self` and `self`.
         if is_self_type(path, ns) {
@@ -471,7 +511,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 source,
                 res,
                 &path_str,
-                &fallback_label,
+                &base_error.fallback_label,
             ) {
                 // We do this to avoid losing a secondary span when we override the main error span.
                 self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
@@ -479,8 +519,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             }
         }
 
-        let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none();
-        if !self.type_ascription_suggestion(&mut err, base_span) {
+        let is_macro =
+            base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
+        if !self.type_ascription_suggestion(&mut err, base_error.span) {
             let mut fallback = false;
             if let (
                 PathSource::Trait(AliasPossibility::Maybe),
@@ -493,7 +534,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     let spans: Vec<Span> = bounds
                         .iter()
                         .map(|bound| bound.span())
-                        .filter(|&sp| sp != base_span)
+                        .filter(|&sp| sp != base_error.span)
                         .collect();
 
                     let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap();
@@ -515,7 +556,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                         multi_span.push_span_label(sp, msg);
                     }
                     multi_span.push_span_label(
-                        base_span,
+                        base_error.span,
                         "expected this type to be a trait...".to_string(),
                     );
                     err.span_help(
@@ -525,14 +566,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     );
                     if bounds.iter().all(|bound| match bound {
                         ast::GenericBound::Outlives(_) => true,
-                        ast::GenericBound::Trait(tr, _) => tr.span == base_span,
+                        ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
                     }) {
                         let mut sugg = vec![];
-                        if base_span != start_span {
-                            sugg.push((start_span.until(base_span), String::new()));
+                        if base_error.span != start_span {
+                            sugg.push((start_span.until(base_error.span), String::new()));
                         }
-                        if base_span != end_span {
-                            sugg.push((base_span.shrink_to_hi().to(end_span), String::new()));
+                        if base_error.span != end_span {
+                            sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
                         }
 
                         err.multipart_suggestion(
@@ -550,7 +591,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 fallback = true;
                 match self.diagnostic_metadata.current_let_binding {
                     Some((pat_sp, Some(ty_sp), None))
-                        if ty_sp.contains(base_span) && could_be_expr =>
+                        if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
                     {
                         err.span_suggestion_short(
                             pat_sp.between(ty_sp),
@@ -568,7 +609,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             }
             if fallback {
                 // Fallback label.
-                err.span_label(base_span, fallback_label);
+                err.span_label(base_error.span, base_error.fallback_label);
             }
         }
         if let Some(err_code) = &err.code {
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 19e68f0b14f4f..f57986a985cde 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -44,6 +44,59 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
     };
     let sp = tcx.sess.source_map().guess_head_span(item.span);
     let tr = impl_.of_trait.as_ref().unwrap();
+
+    // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
+    // and #84660 where it would otherwise allow unsoundness.
+    if trait_ref.has_opaque_types() {
+        trace!("{:#?}", item);
+        // First we find the opaque type in question.
+        for ty in trait_ref.substs {
+            for ty in ty.walk() {
+                let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
+                let ty::Opaque(def_id, _) = *ty.kind() else { continue };
+                trace!(?def_id);
+
+                // Then we search for mentions of the opaque type's type alias in the HIR
+                struct SpanFinder<'tcx> {
+                    sp: Span,
+                    def_id: DefId,
+                    tcx: TyCtxt<'tcx>,
+                }
+                impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
+                    #[instrument(level = "trace", skip(self, _id))]
+                    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+                        // You can't mention an opaque type directly, so we look for type aliases
+                        if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
+                            // And check if that type alias's type contains the opaque type we're looking for
+                            for arg in self.tcx.type_of(def_id).walk() {
+                                if let GenericArgKind::Type(ty) = arg.unpack() {
+                                    if let ty::Opaque(def_id, _) = *ty.kind() {
+                                        if def_id == self.def_id {
+                                            // Finally we update the span to the mention of the type alias
+                                            self.sp = path.span;
+                                            return;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        hir::intravisit::walk_path(self, path)
+                    }
+                }
+
+                let mut visitor = SpanFinder { sp, def_id, tcx };
+                hir::intravisit::walk_item(&mut visitor, item);
+                let reported = tcx
+                    .sess
+                    .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
+                    .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
+                    .emit();
+                return Err(reported);
+            }
+        }
+        span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
+    }
+
     match traits::orphan_check(tcx, item.def_id.to_def_id()) {
         Ok(()) => {}
         Err(err) => emit_orphan_check_error(
@@ -143,58 +196,6 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
         }
     }
 
-    // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
-    // and #84660 where it would otherwise allow unsoundness.
-    if trait_ref.has_opaque_types() {
-        trace!("{:#?}", item);
-        // First we find the opaque type in question.
-        for ty in trait_ref.substs {
-            for ty in ty.walk() {
-                let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
-                let ty::Opaque(def_id, _) = *ty.kind() else { continue };
-                trace!(?def_id);
-
-                // Then we search for mentions of the opaque type's type alias in the HIR
-                struct SpanFinder<'tcx> {
-                    sp: Span,
-                    def_id: DefId,
-                    tcx: TyCtxt<'tcx>,
-                }
-                impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
-                    #[instrument(level = "trace", skip(self, _id))]
-                    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
-                        // You can't mention an opaque type directly, so we look for type aliases
-                        if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
-                            // And check if that type alias's type contains the opaque type we're looking for
-                            for arg in self.tcx.type_of(def_id).walk() {
-                                if let GenericArgKind::Type(ty) = arg.unpack() {
-                                    if let ty::Opaque(def_id, _) = *ty.kind() {
-                                        if def_id == self.def_id {
-                                            // Finally we update the span to the mention of the type alias
-                                            self.sp = path.span;
-                                            return;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        hir::intravisit::walk_path(self, path)
-                    }
-                }
-
-                let mut visitor = SpanFinder { sp, def_id, tcx };
-                hir::intravisit::walk_item(&mut visitor, item);
-                let reported = tcx
-                    .sess
-                    .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
-                    .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
-                    .emit();
-                return Err(reported);
-            }
-        }
-        span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
-    }
-
     Ok(())
 }
 
diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs
index 909c99adab5d2..6f764a952c022 100644
--- a/compiler/rustc_typeck/src/constrained_generic_params.rs
+++ b/compiler/rustc_typeck/src/constrained_generic_params.rs
@@ -59,7 +59,7 @@ struct ParameterCollector {
 impl<'tcx> TypeVisitor<'tcx> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
-            ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => {
+            ty::Projection(..) if !self.include_nonconstraining => {
                 // projections are not injective
                 return ControlFlow::CONTINUE;
             }
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 4bcc78ae0f4b2..5295745647365 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -374,19 +374,26 @@ impl<T> Rc<T> {
         }
     }
 
-    /// Constructs a new `Rc<T>` using a closure `data_fn` that has access to a
-    /// weak reference to the constructing `Rc<T>`.
+    /// Constructs a new `Rc<T>` while giving you a `Weak<T>` to the allocation,
+    /// to allow you to construct a `T` which holds a weak pointer to itself.
     ///
     /// Generally, a structure circularly referencing itself, either directly or
-    /// indirectly, should not hold a strong reference to prevent a memory leak.
-    /// In `data_fn`, initialization of `T` can make use of the weak reference
-    /// by cloning and storing it inside `T` for use at a later time.
+    /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+    /// Using this function, you get access to the weak pointer during the
+    /// initialization of `T`, before the `Rc<T>` is created, such that you can
+    /// clone and store it inside the `T`.
+    ///
+    /// `new_cyclic` first allocates the managed allocation for the `Rc<T>`,
+    /// then calls your closure, giving it a `Weak<T>` to this allocation,
+    /// and only afterwards completes the construction of the `Rc<T>` by placing
+    /// the `T` returned from your closure into the allocation.
     ///
     /// Since the new `Rc<T>` is not fully-constructed until `Rc<T>::new_cyclic`
-    /// returns, calling [`upgrade`] on the weak reference inside `data_fn` will
+    /// returns, calling [`upgrade`] on the weak reference inside your closure will
     /// fail and result in a `None` value.
     ///
     /// # Panics
+    ///
     /// If `data_fn` panics, the panic is propagated to the caller, and the
     /// temporary [`Weak<T>`] is dropped normally.
     ///
@@ -403,7 +410,12 @@ impl<T> Rc<T> {
     /// impl Gadget {
     ///     /// Construct a reference counted Gadget.
     ///     fn new() -> Rc<Self> {
-    ///         Rc::new_cyclic(|me| Gadget { me: me.clone() })
+    ///         // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+    ///         // `Rc` we're constructing.
+    ///         Rc::new_cyclic(|me| {
+    ///             // Create the actual struct here.
+    ///             Gadget { me: me.clone() }
+    ///         })
     ///     }
     ///
     ///     /// Return a reference counted pointer to Self.
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 1e2caddcacb19..2bd8f418ee90c 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -351,23 +351,31 @@ impl<T> Arc<T> {
         unsafe { Self::from_inner(Box::leak(x).into()) }
     }
 
-    /// Constructs a new `Arc<T>` using a closure `data_fn` that has access to
-    /// a weak reference to the constructing `Arc<T>`.
+    /// Constructs a new `Arc<T>` while giving you a `Weak<T>` to the allocation,
+    /// to allow you to construct a `T` which holds a weak pointer to itself.
     ///
     /// Generally, a structure circularly referencing itself, either directly or
-    /// indirectly, should not hold a strong reference to prevent a memory leak.
-    /// In `data_fn`, initialization of `T` can make use of the weak reference
-    /// by cloning and storing it inside `T` for use at a later time.
+    /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+    /// Using this function, you get access to the weak pointer during the
+    /// initialization of `T`, before the `Arc<T>` is created, such that you can
+    /// clone and store it inside the `T`.
     ///
-    /// Since the new `Arc<T>` is not fully-constructed until
-    /// `Arc<T>::new_cyclic` returns, calling [`upgrade`] on the weak
-    /// reference inside `data_fn` will fail and result in a `None` value.
+    /// `new_cyclic` first allocates the managed allocation for the `Arc<T>`,
+    /// then calls your closure, giving it a `Weak<T>` to this allocation,
+    /// and only afterwards completes the construction of the `Arc<T>` by placing
+    /// the `T` returned from your closure into the allocation.
+    ///
+    /// Since the new `Arc<T>` is not fully-constructed until `Arc<T>::new_cyclic`
+    /// returns, calling [`upgrade`] on the weak reference inside your closure will
+    /// fail and result in a `None` value.
     ///
     /// # Panics
+    ///
     /// If `data_fn` panics, the panic is propagated to the caller, and the
     /// temporary [`Weak<T>`] is dropped normally.
     ///
     /// # Example
+    ///
     /// ```
     /// # #![allow(dead_code)]
     /// use std::sync::{Arc, Weak};
@@ -379,7 +387,12 @@ impl<T> Arc<T> {
     /// impl Gadget {
     ///     /// Construct a reference counted Gadget.
     ///     fn new() -> Arc<Self> {
-    ///         Arc::new_cyclic(|me| Gadget { me: me.clone() })
+    ///         // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+    ///         // `Arc` we're constructing.
+    ///         Arc::new_cyclic(|me| {
+    ///             // Create the actual struct here.
+    ///             Gadget { me: me.clone() }
+    ///         })
     ///     }
     ///
     ///     /// Return a reference counted pointer to Self.
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 1bf447347408d..b5c7982a5a871 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -2015,7 +2015,12 @@ macro_rules! int_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the division results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2050,7 +2055,12 @@ macro_rules! int_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the division results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2088,7 +2098,12 @@ macro_rules! int_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the operation results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2157,7 +2172,6 @@ macro_rules! int_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        #[rustc_inherit_overflow_checks]
         pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
             // This would otherwise fail when calculating `r` when self == T::MIN.
             if rhs == -1 {
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index ce52e4773ce1f..048d6bafcdecd 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -2020,7 +2020,7 @@ macro_rules! uint_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0.
+        /// This function will panic if `rhs` is zero.
         ///
         /// # Examples
         ///
@@ -2034,7 +2034,6 @@ macro_rules! uint_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_inherit_overflow_checks]
         pub const fn div_floor(self, rhs: Self) -> Self {
             self / rhs
         }
@@ -2043,7 +2042,12 @@ macro_rules! uint_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2073,7 +2077,12 @@ macro_rules! uint_impl {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the operation results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2097,7 +2106,7 @@ macro_rules! uint_impl {
         }
 
         /// Calculates the smallest value greater than or equal to `self` that
-        /// is a multiple of `rhs`. Returns `None` is `rhs` is zero or the
+        /// is a multiple of `rhs`. Returns `None` if `rhs` is zero or the
         /// operation would result in overflow.
         ///
         /// # Examples
@@ -2115,7 +2124,6 @@ macro_rules! uint_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        #[rustc_inherit_overflow_checks]
         pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
             match try_opt!(self.checked_rem(rhs)) {
                 0 => Some(self),
diff --git a/library/core/tests/num/i128.rs b/library/core/tests/num/i128.rs
index 72c0b225991f6..1ddd20f33d0b1 100644
--- a/library/core/tests/num/i128.rs
+++ b/library/core/tests/num/i128.rs
@@ -1 +1 @@
-int_module!(i128, i128);
+int_module!(i128);
diff --git a/library/core/tests/num/i16.rs b/library/core/tests/num/i16.rs
index f5544b914b73d..c7aa9fff964ed 100644
--- a/library/core/tests/num/i16.rs
+++ b/library/core/tests/num/i16.rs
@@ -1 +1 @@
-int_module!(i16, i16);
+int_module!(i16);
diff --git a/library/core/tests/num/i32.rs b/library/core/tests/num/i32.rs
index 4acc760ffac99..efd5b1596a80d 100644
--- a/library/core/tests/num/i32.rs
+++ b/library/core/tests/num/i32.rs
@@ -1,4 +1,4 @@
-int_module!(i32, i32);
+int_module!(i32);
 
 #[test]
 fn test_arith_operation() {
diff --git a/library/core/tests/num/i64.rs b/library/core/tests/num/i64.rs
index fa4d2ab6638d7..93d23c10adf7e 100644
--- a/library/core/tests/num/i64.rs
+++ b/library/core/tests/num/i64.rs
@@ -1 +1 @@
-int_module!(i64, i64);
+int_module!(i64);
diff --git a/library/core/tests/num/i8.rs b/library/core/tests/num/i8.rs
index ccec6915fe090..887d4f17d25ff 100644
--- a/library/core/tests/num/i8.rs
+++ b/library/core/tests/num/i8.rs
@@ -1 +1 @@
-int_module!(i8, i8);
+int_module!(i8);
diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs
index d2d655ea2c750..8b84a78e6be08 100644
--- a/library/core/tests/num/int_macros.rs
+++ b/library/core/tests/num/int_macros.rs
@@ -1,9 +1,9 @@
 macro_rules! int_module {
-    ($T:ident, $T_i:ident) => {
+    ($T:ident) => {
         #[cfg(test)]
         mod tests {
             use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
-            use core::$T_i::*;
+            use core::$T::*;
 
             use crate::num;
 
diff --git a/library/core/tests/num/u128.rs b/library/core/tests/num/u128.rs
index 716d1836f2c0e..a7b0f9effefb9 100644
--- a/library/core/tests/num/u128.rs
+++ b/library/core/tests/num/u128.rs
@@ -1 +1 @@
-uint_module!(u128, u128);
+uint_module!(u128);
diff --git a/library/core/tests/num/u16.rs b/library/core/tests/num/u16.rs
index 435b914224c5a..010596a34a56c 100644
--- a/library/core/tests/num/u16.rs
+++ b/library/core/tests/num/u16.rs
@@ -1 +1 @@
-uint_module!(u16, u16);
+uint_module!(u16);
diff --git a/library/core/tests/num/u32.rs b/library/core/tests/num/u32.rs
index 71dc005dea370..687d3bbaa907f 100644
--- a/library/core/tests/num/u32.rs
+++ b/library/core/tests/num/u32.rs
@@ -1 +1 @@
-uint_module!(u32, u32);
+uint_module!(u32);
diff --git a/library/core/tests/num/u64.rs b/library/core/tests/num/u64.rs
index b498ebc52042e..ee55071e94996 100644
--- a/library/core/tests/num/u64.rs
+++ b/library/core/tests/num/u64.rs
@@ -1 +1 @@
-uint_module!(u64, u64);
+uint_module!(u64);
diff --git a/library/core/tests/num/u8.rs b/library/core/tests/num/u8.rs
index 68e938be704ac..12b038ce0f75c 100644
--- a/library/core/tests/num/u8.rs
+++ b/library/core/tests/num/u8.rs
@@ -1 +1 @@
-uint_module!(u8, u8);
+uint_module!(u8);
diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs
index 49f8f1f13fad4..93ae620c23302 100644
--- a/library/core/tests/num/uint_macros.rs
+++ b/library/core/tests/num/uint_macros.rs
@@ -1,9 +1,9 @@
 macro_rules! uint_module {
-    ($T:ident, $T_i:ident) => {
+    ($T:ident) => {
         #[cfg(test)]
         mod tests {
             use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
-            use core::$T_i::*;
+            use core::$T::*;
             use std::str::FromStr;
 
             use crate::num;
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index f1c5eaad868e9..6f7c6305afc14 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -703,11 +703,12 @@ pub enum Delimiter {
     /// `[ ... ]`
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     Bracket,
-    /// `Ø ... Ø`
+    /// `/*«*/ ... /*»*/`
     /// An invisible delimiter, that may, for example, appear around tokens coming from a
     /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
     /// `$var * 3` where `$var` is `1 + 2`.
-    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+    /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+    /// Therefore, they might not survive a roundtrip of a token stream through a string.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     None,
 }
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 1dfd9c762c46e..667f89c07e212 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -7,6 +7,8 @@
 /* global onEach, onEachLazy, removeClass */
 /* global switchTheme, useSystemTheme */
 
+"use strict";
+
 if (!String.prototype.startsWith) {
     String.prototype.startsWith = function(searchString, position) {
         position = position || 0;
@@ -292,15 +294,12 @@ function loadCss(cssFileName) {
 }
 
 (function() {
-    "use strict";
-
     function loadScript(url) {
         const script = document.createElement('script');
         script.src = url;
         document.head.append(script);
     }
 
-
     getSettingsButton().onclick = event => {
         event.preventDefault();
         loadScript(window.settingsJS);
diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js
index 491b3950ae6eb..70fcef522129e 100644
--- a/src/librustdoc/html/static/js/scrape-examples.js
+++ b/src/librustdoc/html/static/js/scrape-examples.js
@@ -4,6 +4,8 @@
 /* eslint prefer-arrow-callback: "error" */
 /* global addClass, hasClass, removeClass, onEachLazy */
 
+"use strict";
+
 (function () {
     // Number of lines shown when code viewer is not expanded
     const MAX_LINES = 10;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 60ad431ba7a99..54ce2508c468d 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5,7 +5,9 @@
 /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
 /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
 
-(function () {
+"use strict";
+
+(function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
 const itemTypes = [
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 43b24245ab2e8..119d4b07665a4 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -7,6 +7,8 @@
 /* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
 /* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
 
+"use strict";
+
 (function () {
     const isSettingsPage = window.location.pathname.endsWith("/settings.html");
 
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index 290bf40a8f577..f788d41d2ded4 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -9,7 +9,10 @@
 // Local js definitions:
 /* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */
 /* global updateLocalStorage */
-(function () {
+
+"use strict";
+
+(function() {
 
 function getCurrentFilePath() {
     const parts = window.location.pathname.split("/");
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 913fc278eb387..052731e99aed2 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -3,6 +3,8 @@
 /* eslint prefer-const: "error" */
 /* eslint prefer-arrow-callback: "error" */
 
+"use strict";
+
 const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
diff --git a/src/test/ui/proc-macro/auxiliary/expand-expr.rs b/src/test/ui/proc-macro/auxiliary/expand-expr.rs
index 2bc34f3c6bfc0..a2e30e2e93b5a 100644
--- a/src/test/ui/proc-macro/auxiliary/expand-expr.rs
+++ b/src/test/ui/proc-macro/auxiliary/expand-expr.rs
@@ -12,6 +12,15 @@ use std::str::FromStr;
 
 #[proc_macro]
 pub fn expand_expr_is(input: TokenStream) -> TokenStream {
+    expand_expr_is_inner(input, false)
+}
+
+#[proc_macro]
+pub fn expand_expr_is_trim(input: TokenStream) -> TokenStream {
+    expand_expr_is_inner(input, true)
+}
+
+fn expand_expr_is_inner(input: TokenStream, trim_invisible: bool) -> TokenStream {
     let mut iter = input.into_iter();
     let mut expected_tts = Vec::new();
     loop {
@@ -22,14 +31,18 @@ pub fn expand_expr_is(input: TokenStream) -> TokenStream {
         }
     }
 
-    let expected = expected_tts.into_iter().collect::<TokenStream>();
-    let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
-    assert!(
-        expected.to_string() == expanded.to_string(),
-        "assert failed\nexpected: `{}`\nexpanded: `{}`",
-        expected.to_string(),
-        expanded.to_string()
-    );
+    // If requested, trim the "invisible" delimiters at the start and end.
+    let expected = expected_tts.into_iter().collect::<TokenStream>().to_string();
+    let expected = if trim_invisible {
+        let len1 = "/*«*/ ".len();
+        let len2 = " /*»*/".len();
+        &expected[len1..expected.len() - len2]
+    } else {
+        &expected[..]
+    };
+    let expanded = iter.collect::<TokenStream>().expand_expr().unwrap().to_string();
+
+    assert_eq!(expected, expanded);
 
     TokenStream::new()
 }
diff --git a/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout
index 4de8746a1b460..3d0e7eaff00d8 100644
--- a/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout
+++ b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): self
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ self /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
@@ -13,8 +14,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
 ]
 PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30,
 std::option::Option, pub(in some::path) , [a b c], -30
-PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30,
-std :: option :: Option, pub(in some :: path), [a b c], - 30
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/, /*«*/ { "a" } /*»*/, /*«*/ let a = 1 /*»*/, /*«*/
+String /*»*/, my_name, /*«*/ 'a /*»*/, /*«*/ my_val = 30 /*»*/, /*«*/
+std :: option :: Option /*»*/, /*«*/ pub(in some :: path) /*»*/, [a b c],
+/*«*/ - 30 /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
@@ -295,6 +298,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): (a, b)
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ (a, b) /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/capture-unglued-token.stdout b/src/test/ui/proc-macro/capture-unglued-token.stdout
index 7e6b540332c79..5fe6ff72b4544 100644
--- a/src/test/ui/proc-macro/capture-unglued-token.stdout
+++ b/src/test/ui/proc-macro/capture-unglued-token.stdout
@@ -1,5 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): Vec<u8>
-PRINT-BANG RE-COLLECTED (DISPLAY): Vec < u8 >
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ Vec < u8 > /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/expand-expr.rs b/src/test/ui/proc-macro/expand-expr.rs
index d1146d9703062..edcb30f892cdf 100644
--- a/src/test/ui/proc-macro/expand-expr.rs
+++ b/src/test/ui/proc-macro/expand-expr.rs
@@ -2,9 +2,9 @@
 
 extern crate expand_expr;
 
-use expand_expr::{
-    check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is, recursive_expand,
-};
+use expand_expr::{check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is};
+use expand_expr::{expand_expr_is_trim, recursive_expand};
+
 
 // Check builtin macros can be expanded.
 
@@ -47,21 +47,21 @@ macro_rules! echo_expr {
 
 macro_rules! simple_lit {
     ($l:literal) => {
-        expand_expr_is!($l, $l);
-        expand_expr_is!($l, echo_lit!($l));
-        expand_expr_is!($l, echo_expr!($l));
-        expand_expr_is!($l, echo_tts!($l));
-        expand_expr_is!($l, echo_pm!($l));
+        expand_expr_is_trim!($l, $l);
+        expand_expr_is_trim!($l, echo_lit!($l));
+        expand_expr_is_trim!($l, echo_expr!($l));
+        expand_expr_is_trim!($l, echo_tts!($l));
+        expand_expr_is_trim!($l, echo_pm!($l));
         const _: () = {
             macro_rules! mac {
                 () => {
                     $l
                 };
             }
-            expand_expr_is!($l, mac!());
-            expand_expr_is!($l, echo_expr!(mac!()));
-            expand_expr_is!($l, echo_tts!(mac!()));
-            expand_expr_is!($l, echo_pm!(mac!()));
+            expand_expr_is_trim!($l, mac!());
+            expand_expr_is_trim!($l, echo_expr!(mac!()));
+            expand_expr_is_trim!($l, echo_tts!(mac!()));
+            expand_expr_is_trim!($l, echo_pm!(mac!()));
         };
     };
 }
diff --git a/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout b/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout
index 686d53e887660..04b516fd25424 100644
--- a/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout
+++ b/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout
@@ -1,5 +1,6 @@
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0 ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0 ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E
+{ V = { let _ = /*«*/ #[allow(warnings)] #[allow(warnings)] 0 /*»*/ ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -123,7 +124,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -203,6 +204,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ {} /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -281,7 +283,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -359,7 +361,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 + 1 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -450,7 +452,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH + 1 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
diff --git a/src/test/ui/proc-macro/issue-75734-pp-paren.stdout b/src/test/ui/proc-macro/issue-75734-pp-paren.stdout
index 0fda6654ff370..55818969c7178 100644
--- a/src/test/ui/proc-macro/issue-75734-pp-paren.stdout
+++ b/src/test/ui/proc-macro/issue-75734-pp-paren.stdout
@@ -96,6 +96,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): 1 + 1 * 2
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/ * 2
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout
index 60a400a5deabf..6cf8043c34f81 100644
--- a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout
+++ b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout
@@ -1,7 +1,7 @@
 PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar {
     #![doc = r" Foo"]
 } }
-PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] mod bar { #! [doc = r" Foo"] } }
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] /*«*/ mod bar { #! [doc = r" Foo"] } /*»*/ }
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "foo",
diff --git a/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout
index 4b7ed874307d8..adbd653ead4b7 100644
--- a/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout
+++ b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ ; /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout
index a3d24dd26fe97..b912e426d5d99 100644
--- a/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout
+++ b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout
@@ -1,4 +1,6 @@
 PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 0 + 1 + 2 /*»*/ + 3
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): /*«*/ /*«*/ /*«*/ 0 /*»*/ + 1 /*»*/ + 2 /*»*/ + 3
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/nodelim-groups.stdout b/src/test/ui/proc-macro/nodelim-groups.stdout
index 6b410f0bfb7e3..0d2f33b41750d 100644
--- a/src/test/ui/proc-macro/nodelim-groups.stdout
+++ b/src/test/ui/proc-macro/nodelim-groups.stdout
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): "hi" 1 + (25) + 1 (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ 1 + (25) + 1 /*»*/ (1 + 1)
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Literal {
         kind: Str,
@@ -71,6 +72,9 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ "hello".len() + "world".len() /*»*/ (1 + 1)
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): "hi" /*«*/ /*«*/ "hello".len() /*»*/ + /*«*/ "world".len() /*»*/ /*»*/
+(1 + 1)
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Literal {
         kind: Str,
diff --git a/src/test/ui/proc-macro/nonterminal-expansion.stdout b/src/test/ui/proc-macro/nonterminal-expansion.stdout
index 4d884348f2ca4..32981e7011d97 100644
--- a/src/test/ui/proc-macro/nonterminal-expansion.stdout
+++ b/src/test/ui/proc-macro/nonterminal-expansion.stdout
@@ -1,5 +1,5 @@
 PRINT-ATTR_ARGS INPUT (DISPLAY): a, line!(), b
-PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, line! (), b
+PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, /*«*/ line! () /*»*/, b
 PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
     Ident {
         ident: "a",
diff --git a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout
index c08e5308138c9..ba18ca75d7fe4 100644
--- a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout
+++ b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout
@@ -1,5 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): struct S;
-PRINT-BANG RE-COLLECTED (DISPLAY): struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ struct S ; /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
diff --git a/src/test/ui/proc-macro/parent-source-spans.rs b/src/test/ui/proc-macro/parent-source-spans.rs
index 354657db4db38..71e5065a87a88 100644
--- a/src/test/ui/proc-macro/parent-source-spans.rs
+++ b/src/test/ui/proc-macro/parent-source-spans.rs
@@ -8,16 +8,16 @@ use parent_source_spans::parent_source_spans;
 
 macro one($a:expr, $b:expr) {
     two!($a, $b);
-    //~^ ERROR first parent: "hello"
-    //~| ERROR second parent: "world"
+    //~^ ERROR first parent: /*«*/ "hello" /*»*/
+    //~| ERROR second parent: /*«*/ "world" /*»*/
 }
 
 macro two($a:expr, $b:expr) {
     three!($a, $b);
-    //~^ ERROR first final: "hello"
-    //~| ERROR second final: "world"
-    //~| ERROR first final: "yay"
-    //~| ERROR second final: "rust"
+    //~^ ERROR first final: /*«*/ "hello" /*»*/
+    //~| ERROR second final: /*«*/ "world" /*»*/
+    //~| ERROR first final: /*«*/ "yay" /*»*/
+    //~| ERROR second final: /*«*/ "rust" /*»*/
 }
 
 // forwarding tokens directly doesn't create a new source chain
@@ -34,16 +34,16 @@ macro four($($tokens:tt)*) {
 
 fn main() {
     one!("hello", "world");
-    //~^ ERROR first grandparent: "hello"
-    //~| ERROR second grandparent: "world"
-    //~| ERROR first source: "hello"
-    //~| ERROR second source: "world"
+    //~^ ERROR first grandparent: /*«*/ "hello" /*»*/
+    //~| ERROR second grandparent: /*«*/ "world" /*»*/
+    //~| ERROR first source: /*«*/ "hello" /*»*/
+    //~| ERROR second source: /*«*/ "world" /*»*/
 
     two!("yay", "rust");
-    //~^ ERROR first parent: "yay"
-    //~| ERROR second parent: "rust"
-    //~| ERROR first source: "yay"
-    //~| ERROR second source: "rust"
+    //~^ ERROR first parent: /*«*/ "yay" /*»*/
+    //~| ERROR second parent: /*«*/ "rust" /*»*/
+    //~| ERROR first source: /*«*/ "yay" /*»*/
+    //~| ERROR second source: /*«*/ "rust" /*»*/
 
     three!("hip", "hop");
     //~^ ERROR first final: "hip"
diff --git a/src/test/ui/proc-macro/parent-source-spans.stderr b/src/test/ui/proc-macro/parent-source-spans.stderr
index 4548269b50793..e42218ea70117 100644
--- a/src/test/ui/proc-macro/parent-source-spans.stderr
+++ b/src/test/ui/proc-macro/parent-source-spans.stderr
@@ -1,4 +1,4 @@
-error: first final: "hello"
+error: first final: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:16:12
    |
 LL |     three!($a, $b);
@@ -9,7 +9,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second final: "world"
+error: second final: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:16:16
    |
 LL |     three!($a, $b);
@@ -20,7 +20,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first parent: "hello"
+error: first parent: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:10:5
    |
 LL |     two!($a, $b);
@@ -31,7 +31,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second parent: "world"
+error: second parent: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:10:5
    |
 LL |     two!($a, $b);
@@ -42,31 +42,31 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first grandparent: "hello"
+error: first grandparent: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: second grandparent: "world"
+error: second grandparent: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: first source: "hello"
+error: first source: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: second source: "world"
+error: second source: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: first final: "yay"
+error: first final: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:16:12
    |
 LL |     three!($a, $b);
@@ -77,7 +77,7 @@ LL |     two!("yay", "rust");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second final: "rust"
+error: second final: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:16:16
    |
 LL |     three!($a, $b);
@@ -88,25 +88,25 @@ LL |     two!("yay", "rust");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first parent: "yay"
+error: first parent: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: second parent: "rust"
+error: second parent: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: first source: "yay"
+error: first source: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: second source: "rust"
+error: second source: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr
index d187267388577..b8d528efc1590 100644
--- a/src/test/ui/resolve/issue-2356.stderr
+++ b/src/test/ui/resolve/issue-2356.stderr
@@ -48,6 +48,11 @@ error[E0425]: cannot find function `static_method` in this scope
    |
 LL |         static_method();
    |         ^^^^^^^^^^^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::static_method();
+   |         ~~~~~~~~~~~~~~~~~~~
 
 error[E0425]: cannot find function `purr` in this scope
   --> $DIR/issue-2356.rs:54:9
@@ -85,6 +90,11 @@ error[E0425]: cannot find function `grow_older` in this scope
    |
 LL |     grow_older();
    |     ^^^^^^^^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |     Self::grow_older();
+   |     ~~~~~~~~~~~~~~~~
 
 error[E0425]: cannot find function `shave` in this scope
   --> $DIR/issue-2356.rs:74:5
diff --git a/src/test/ui/suggestions/assoc_fn_without_self.rs b/src/test/ui/suggestions/assoc_fn_without_self.rs
new file mode 100644
index 0000000000000..778d9847773f3
--- /dev/null
+++ b/src/test/ui/suggestions/assoc_fn_without_self.rs
@@ -0,0 +1,20 @@
+fn main() {}
+
+struct S;
+
+impl S {
+    fn foo() {}
+
+    fn bar(&self) {}
+
+    fn baz(a: u8, b: u8) {}
+
+    fn b() {
+        fn c() {
+            foo(); //~ ERROR cannot find function `foo` in this scope
+        }
+        foo(); //~ ERROR cannot find function `foo` in this scope
+        bar(); //~ ERROR cannot find function `bar` in this scope
+        baz(2, 3); //~ ERROR cannot find function `baz` in this scope
+    }
+}
diff --git a/src/test/ui/suggestions/assoc_fn_without_self.stderr b/src/test/ui/suggestions/assoc_fn_without_self.stderr
new file mode 100644
index 0000000000000..4a0e62e73093b
--- /dev/null
+++ b/src/test/ui/suggestions/assoc_fn_without_self.stderr
@@ -0,0 +1,37 @@
+error[E0425]: cannot find function `foo` in this scope
+  --> $DIR/assoc_fn_without_self.rs:14:13
+   |
+LL |             foo();
+   |             ^^^ not found in this scope
+
+error[E0425]: cannot find function `foo` in this scope
+  --> $DIR/assoc_fn_without_self.rs:16:9
+   |
+LL |         foo();
+   |         ^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::foo();
+   |         ~~~~~~~~~
+
+error[E0425]: cannot find function `bar` in this scope
+  --> $DIR/assoc_fn_without_self.rs:17:9
+   |
+LL |         bar();
+   |         ^^^ not found in this scope
+
+error[E0425]: cannot find function `baz` in this scope
+  --> $DIR/assoc_fn_without_self.rs:18:9
+   |
+LL |         baz(2, 3);
+   |         ^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::baz(2, 3);
+   |         ~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/type-alias-impl-trait/coherence.rs b/src/test/ui/type-alias-impl-trait/coherence.rs
index 1c0f83d6c12da..98ac215ad6cc5 100644
--- a/src/test/ui/type-alias-impl-trait/coherence.rs
+++ b/src/test/ui/type-alias-impl-trait/coherence.rs
@@ -12,6 +12,6 @@ fn use_alias<T>(val: T) -> AliasOfForeignType<T> {
 }
 
 impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
-//~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates
+//~^ ERROR cannot implement trait on type alias impl trait
 
 fn main() {}
diff --git a/src/test/ui/type-alias-impl-trait/coherence.stderr b/src/test/ui/type-alias-impl-trait/coherence.stderr
index 6ede0fa14ba70..3ce25d94f6e12 100644
--- a/src/test/ui/type-alias-impl-trait/coherence.stderr
+++ b/src/test/ui/type-alias-impl-trait/coherence.stderr
@@ -1,9 +1,14 @@
-error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
-  --> $DIR/coherence.rs:14:6
+error: cannot implement trait on type alias impl trait
+  --> $DIR/coherence.rs:14:41
    |
 LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
-   |      ^ unconstrained type parameter
+   |                                         ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: type alias impl trait defined here
+  --> $DIR/coherence.rs:9:30
+   |
+LL | type AliasOfForeignType<T> = impl LocalTrait;
+   |                              ^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0207`.