about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock6
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs73
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs4
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs26
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs5
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs4
-rw-r--r--compiler/rustc_type_ir/src/search_graph/stack.rs4
-rw-r--r--library/Cargo.lock15
-rw-r--r--library/std/tests/floats/f32.rs10
-rw-r--r--library/test/Cargo.toml2
-rw-r--r--src/tools/tidy/src/deps.rs1
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs33
-rw-r--r--tests/ui/parser/issues/issue-59418.rs18
-rw-r--r--tests/ui/parser/issues/issue-59418.stderr26
-rw-r--r--tests/ui/parser/tuple-index-suffix-proc-macro.rs32
-rw-r--r--tests/ui/parser/tuple-index-suffix-proc-macro.stderr26
-rw-r--r--tests/ui/parser/tuple-index-suffix.rs79
-rw-r--r--tests/ui/parser/tuple-index-suffix.stderr134
-rw-r--r--tests/ui/suggestions/apitit-unimplemented-method.rs12
-rw-r--r--tests/ui/suggestions/apitit-unimplemented-method.stderr12
-rw-r--r--tests/ui/suggestions/auxiliary/dep.rs4
-rw-r--r--triagebot.toml15
24 files changed, 416 insertions, 129 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c29c3158d4a..24cd5b825b2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -674,7 +674,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
 dependencies = [
  "serde",
  "termcolor",
- "unicode-width 0.2.1",
+ "unicode-width 0.1.14",
 ]
 
 [[package]]
@@ -1458,9 +1458,9 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.23"
+version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
+checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
 dependencies = [
  "unicode-width 0.2.1",
 ]
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index b5c0ca4727c..63d0f400aef 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -85,7 +85,9 @@ use rustc_infer::traits::ObligationCause;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::print::with_types_for_signature;
-use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode};
+use rustc_middle::ty::{
+    self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_session::parse::feature_err;
 use rustc_span::def_id::CRATE_DEF_ID;
@@ -233,8 +235,7 @@ fn missing_items_err(
     };
 
     // Obtain the level of indentation ending in `sugg_sp`.
-    let padding =
-        tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
+    let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new);
     let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) =
         (Vec::new(), Vec::new(), Vec::new());
 
@@ -331,6 +332,7 @@ fn default_body_is_unstable(
 fn bounds_from_generic_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
     predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
+    assoc: ty::AssocItem,
 ) -> (String, String) {
     let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default();
     let mut projections = vec![];
@@ -354,34 +356,50 @@ fn bounds_from_generic_predicates<'tcx>(
     }
 
     let mut where_clauses = vec![];
-    let mut types_str = vec![];
-    for (ty, bounds) in types {
-        if let ty::Param(_) = ty.kind() {
-            let mut bounds_str = vec![];
-            for bound in bounds {
-                let mut projections_str = vec![];
-                for projection in &projections {
-                    let p = projection.skip_binder();
-                    if bound == tcx.parent(p.projection_term.def_id)
-                        && p.projection_term.self_ty() == ty
-                    {
-                        let name = tcx.item_name(p.projection_term.def_id);
-                        projections_str.push(format!("{} = {}", name, p.term));
+    let generics = tcx.generics_of(assoc.def_id);
+    let types_str = generics
+        .own_params
+        .iter()
+        .filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. }))
+        .map(|p| {
+            // we just checked that it's a type, so the unwrap can't fail
+            let ty = tcx.mk_param_from_def(p).as_type().unwrap();
+            if let Some(bounds) = types.get(&ty) {
+                let mut bounds_str = vec![];
+                for bound in bounds.iter().copied() {
+                    let mut projections_str = vec![];
+                    for projection in &projections {
+                        let p = projection.skip_binder();
+                        if bound == tcx.parent(p.projection_term.def_id)
+                            && p.projection_term.self_ty() == ty
+                        {
+                            let name = tcx.item_name(p.projection_term.def_id);
+                            projections_str.push(format!("{} = {}", name, p.term));
+                        }
+                    }
+                    let bound_def_path = tcx.def_path_str(bound);
+                    if projections_str.is_empty() {
+                        where_clauses.push(format!("{}: {}", ty, bound_def_path));
+                    } else {
+                        bounds_str.push(format!(
+                            "{}<{}>",
+                            bound_def_path,
+                            projections_str.join(", ")
+                        ));
                     }
                 }
-                let bound_def_path = tcx.def_path_str(bound);
-                if projections_str.is_empty() {
-                    where_clauses.push(format!("{}: {}", ty, bound_def_path));
+                if bounds_str.is_empty() {
+                    ty.to_string()
                 } else {
-                    bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", ")));
+                    format!("{}: {}", ty, bounds_str.join(" + "))
                 }
-            }
-            if bounds_str.is_empty() {
-                types_str.push(ty.to_string());
             } else {
-                types_str.push(format!("{}: {}", ty, bounds_str.join(" + ")));
+                ty.to_string()
             }
-        } else {
+        })
+        .collect::<Vec<_>>();
+    for (ty, bounds) in types.into_iter() {
+        if !matches!(ty.kind(), ty::Param(_)) {
             // Avoid suggesting the following:
             // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
             where_clauses.extend(
@@ -473,10 +491,10 @@ fn fn_sig_suggestion<'tcx>(
     let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
 
     let safety = sig.safety.prefix_str();
-    let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
+    let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates, assoc);
 
     // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
-    // not be present in the `fn` definition, not will we account for renamed
+    // not be present in the `fn` definition, nor will we account for renamed
     // lifetimes between the `impl` and the `trait`, but this should be good enough to
     // fill in a significant portion of the missing code, and other subsequent
     // suggestions can help the user fix the code.
@@ -512,6 +530,7 @@ fn suggestion_signature<'tcx>(
             let (generics, where_clauses) = bounds_from_generic_predicates(
                 tcx,
                 tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
+                assoc,
             );
             format!("type {}{generics} = /* Type */{where_clauses};", assoc.name())
         }
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 77dd313d9b8..72cd75f6d89 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -479,9 +479,6 @@ parse_invalid_identifier_with_leading_number = identifiers cannot start with a n
 
 parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
     .label = invalid suffix `{$suffix}`
-    .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
-    .tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access
-    .tuple_exception_line_3 = see issue #60210 <https://github.com/rust-lang/rust/issues/60210> for more information
 
 parse_invalid_logical_operator = `{$incorrect}` is not a logical operator
     .note = unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 797d4830c2f..00ca5acd84d 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1017,10 +1017,6 @@ pub(crate) struct InvalidLiteralSuffixOnTupleIndex {
     #[label]
     pub span: Span,
     pub suffix: Symbol,
-    #[help(parse_tuple_exception_line_1)]
-    #[help(parse_tuple_exception_line_2)]
-    #[help(parse_tuple_exception_line_3)]
-    pub exception: bool,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 4c02547357e..81a5d48d94e 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1163,7 +1163,10 @@ impl<'a> Parser<'a> {
                         suffix,
                     }) => {
                         if let Some(suffix) = suffix {
-                            self.expect_no_tuple_index_suffix(current.span, suffix);
+                            self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {
+                                span: current.span,
+                                suffix,
+                            });
                         }
                         match self.break_up_float(symbol, current.span) {
                             // 1e2
@@ -1239,7 +1242,8 @@ impl<'a> Parser<'a> {
         suffix: Option<Symbol>,
     ) -> Box<Expr> {
         if let Some(suffix) = suffix {
-            self.expect_no_tuple_index_suffix(ident_span, suffix);
+            self.dcx()
+                .emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix });
         }
         self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span)))
     }
@@ -2225,24 +2229,6 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) {
-        if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) {
-            // #59553: warn instead of reject out of hand to allow the fix to percolate
-            // through the ecosystem when people fix their macros
-            self.dcx().emit_warn(errors::InvalidLiteralSuffixOnTupleIndex {
-                span,
-                suffix,
-                exception: true,
-            });
-        } else {
-            self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {
-                span,
-                suffix,
-                exception: false,
-            });
-        }
-    }
-
     /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`).
     /// Keep this in sync with `Token::can_begin_literal_maybe_minus`.
     pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, Box<Expr>> {
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 3bbca622975..ed4069dae93 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -1335,7 +1335,10 @@ impl<'a> Parser<'a> {
         if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
         {
             if let Some(suffix) = suffix {
-                self.expect_no_tuple_index_suffix(self.token.span, suffix);
+                self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex {
+                    span: self.token.span,
+                    suffix,
+                });
             }
             self.bump();
             Ok(Ident::new(symbol, self.prev_token.span))
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index dbbc0c217d7..8f8f019510f 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -1262,7 +1262,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             encountered_overflow |= stack_entry.encountered_overflow;
             debug_assert_eq!(stack_entry.input, input);
 
-            // If the current goal is not the root of a cycle, we are done.
+            // If the current goal is not a cycle head, we are done.
             //
             // There are no provisional cache entries which depend on this goal.
             let Some(usages) = stack_entry.usages else {
@@ -1278,7 +1278,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             //
             // Check whether we reached a fixpoint, either because the final result
             // is equal to the provisional result of the previous iteration, or because
-            // this was only the root of either coinductive or inductive cycles, and the
+            // this was only the head of either coinductive or inductive cycles, and the
             // final result is equal to the initial response for that case.
             if self.reached_fixpoint(cx, &stack_entry, usages, result) {
                 self.rebase_provisional_cache_entries(&stack_entry, |_, result| result);
diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs
index 3fd8e2bd16e..8348666be41 100644
--- a/compiler/rustc_type_ir/src/search_graph/stack.rs
+++ b/compiler/rustc_type_ir/src/search_graph/stack.rs
@@ -13,7 +13,7 @@ rustc_index::newtype_index! {
     pub(super) struct StackDepth {}
 }
 
-/// Stack entries of the evaluation stack. Its fields tend to be lazily
+/// Stack entries of the evaluation stack. Its fields tend to be lazily updated
 /// when popping a child goal or completely immutable.
 #[derive_where(Debug; X: Cx)]
 pub(super) struct StackEntry<X: Cx> {
@@ -42,7 +42,7 @@ pub(super) struct StackEntry<X: Cx> {
     /// Whether evaluating this goal encountered overflow. Lazily updated.
     pub encountered_overflow: bool,
 
-    /// Whether and how this goal has been used as the root of a cycle. Lazily updated.
+    /// Whether and how this goal has been used as a cycle head. Lazily updated.
     pub usages: Option<HeadUsages>,
 
     /// We want to be able to ignore head usages if they happen inside of candidates
diff --git a/library/Cargo.lock b/library/Cargo.lock
index e601137e005..e4b3839847b 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -99,13 +99,12 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.23"
+version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
+checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
 dependencies = [
  "rustc-std-workspace-core",
  "rustc-std-workspace-std",
- "unicode-width",
 ]
 
 [[package]]
@@ -362,16 +361,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "unicode-width"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
-dependencies = [
- "rustc-std-workspace-core",
- "rustc-std-workspace-std",
-]
-
-[[package]]
 name = "unwind"
 version = "0.0.0"
 dependencies = [
diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs
index c29d803b25e..3acd0670914 100644
--- a/library/std/tests/floats/f32.rs
+++ b/library/std/tests/floats/f32.rs
@@ -193,13 +193,13 @@ fn test_atanh() {
 #[test]
 fn test_gamma() {
     // precision can differ between platforms
-    assert_approx_eq!(1.0f32.gamma(), 1.0f32);
-    assert_approx_eq!(2.0f32.gamma(), 1.0f32);
-    assert_approx_eq!(3.0f32.gamma(), 2.0f32);
+    assert_approx_eq!(1.0f32.gamma(), 1.0f32, APPROX_DELTA);
+    assert_approx_eq!(2.0f32.gamma(), 1.0f32, APPROX_DELTA);
+    assert_approx_eq!(3.0f32.gamma(), 2.0f32, APPROX_DELTA);
     assert_approx_eq!(4.0f32.gamma(), 6.0f32, APPROX_DELTA);
     assert_approx_eq!(5.0f32.gamma(), 24.0f32, APPROX_DELTA);
-    assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt());
-    assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt());
+    assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt(), APPROX_DELTA);
+    assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt(), APPROX_DELTA);
     assert_eq!(0.0f32.gamma(), f32::INFINITY);
     assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY);
     assert!((-1.0f32).gamma().is_nan());
diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml
index 2a32a7dd76e..fe749847b7c 100644
--- a/library/test/Cargo.toml
+++ b/library/test/Cargo.toml
@@ -6,7 +6,7 @@ version = "0.0.0"
 edition = "2024"
 
 [dependencies]
-getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] }
+getopts = { version = "0.2.24", default-features = false, features = ['rustc-dep-of-std'] }
 std = { path = "../std", public = true }
 core = { path = "../core", public = true }
 
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index fee48bea144..60347b2ea64 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -477,7 +477,6 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "rustc-demangle",
     "rustc-literal-escaper",
     "shlex",
-    "unicode-width",
     "unwinding",
     "wasi",
     "windows-sys",
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index ee06707415f..849dcb9e88f 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2021,7 +2021,6 @@ ui/parser/issues/issue-5806.rs
 ui/parser/issues/issue-58094-missing-right-square-bracket.rs
 ui/parser/issues/issue-58856-1.rs
 ui/parser/issues/issue-58856-2.rs
-ui/parser/issues/issue-59418.rs
 ui/parser/issues/issue-60075.rs
 ui/parser/issues/issue-61858.rs
 ui/parser/issues/issue-62524.rs
diff --git a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs
new file mode 100644
index 00000000000..a5084b55aac
--- /dev/null
+++ b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs
@@ -0,0 +1,33 @@
+#![feature(proc_macro_quote, proc_macro_span)]
+
+extern crate proc_macro;
+
+use proc_macro::{Ident, Literal, Span, TokenStream, TokenTree, quote};
+
+#[proc_macro]
+pub fn bad_tup_indexing(input: TokenStream) -> TokenStream {
+    let tt = input.into_iter().next().unwrap();
+    let TokenTree::Literal(indexing_expr) = tt else {
+        unreachable!();
+    };
+    quote! { (42,).$indexing_expr }
+}
+
+// Expects {IDENT, COMMA, LITERAL}
+#[proc_macro]
+pub fn bad_tup_struct_indexing(input: TokenStream) -> TokenStream {
+    let mut input = input.into_iter();
+
+    let ident = input.next().unwrap();
+    let _comma = input.next().unwrap();
+    let lit = input.next().unwrap();
+
+    let TokenTree::Ident(ident) = ident else {
+        unreachable!("id");
+    };
+    let TokenTree::Literal(indexing_expr) = lit else {
+        unreachable!("lit");
+    };
+
+    quote! { $ident.$indexing_expr }
+}
diff --git a/tests/ui/parser/issues/issue-59418.rs b/tests/ui/parser/issues/issue-59418.rs
deleted file mode 100644
index 0fa191d4a7e..00000000000
--- a/tests/ui/parser/issues/issue-59418.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-struct X(i32,i32,i32);
-
-fn main() {
-    let a = X(1, 2, 3);
-    let b = a.1suffix;
-    //~^ ERROR suffixes on a tuple index are invalid
-    println!("{}", b);
-    let c = (1, 2, 3);
-    let d = c.1suffix;
-    //~^ ERROR suffixes on a tuple index are invalid
-    println!("{}", d);
-    let s = X { 0suffix: 0, 1: 1, 2: 2 };
-    //~^ ERROR suffixes on a tuple index are invalid
-    match s {
-        X { 0suffix: _, .. } => {}
-        //~^ ERROR suffixes on a tuple index are invalid
-    }
-}
diff --git a/tests/ui/parser/issues/issue-59418.stderr b/tests/ui/parser/issues/issue-59418.stderr
deleted file mode 100644
index 347051e9f92..00000000000
--- a/tests/ui/parser/issues/issue-59418.stderr
+++ /dev/null
@@ -1,26 +0,0 @@
-error: suffixes on a tuple index are invalid
-  --> $DIR/issue-59418.rs:5:15
-   |
-LL |     let b = a.1suffix;
-   |               ^^^^^^^ invalid suffix `suffix`
-
-error: suffixes on a tuple index are invalid
-  --> $DIR/issue-59418.rs:9:15
-   |
-LL |     let d = c.1suffix;
-   |               ^^^^^^^ invalid suffix `suffix`
-
-error: suffixes on a tuple index are invalid
-  --> $DIR/issue-59418.rs:12:17
-   |
-LL |     let s = X { 0suffix: 0, 1: 1, 2: 2 };
-   |                 ^^^^^^^ invalid suffix `suffix`
-
-error: suffixes on a tuple index are invalid
-  --> $DIR/issue-59418.rs:15:13
-   |
-LL |         X { 0suffix: _, .. } => {}
-   |             ^^^^^^^ invalid suffix `suffix`
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.rs b/tests/ui/parser/tuple-index-suffix-proc-macro.rs
new file mode 100644
index 00000000000..557c67738d3
--- /dev/null
+++ b/tests/ui/parser/tuple-index-suffix-proc-macro.rs
@@ -0,0 +1,32 @@
+//! See #59418.
+//!
+//! Like `tuple-index-suffix.rs`, but exercises the proc-macro interaction.
+
+//@ proc-macro: tuple-index-suffix-proc-macro-aux.rs
+
+extern crate tuple_index_suffix_proc_macro_aux;
+use tuple_index_suffix_proc_macro_aux as aux;
+
+fn main() {
+    struct TupStruct(i32);
+    let tup_struct = TupStruct(42);
+
+    // Previously, #60186 had carve outs for `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. Now,
+    // they all hard error.
+
+    aux::bad_tup_indexing!(0usize);
+    //~^ ERROR suffixes on a tuple index are invalid
+    aux::bad_tup_struct_indexing!(tup_struct, 0isize);
+    //~^ ERROR suffixes on a tuple index are invalid
+
+    // Not part of the #60186 carve outs.
+
+    aux::bad_tup_indexing!(0u8);
+    //~^ ERROR suffixes on a tuple index are invalid
+    aux::bad_tup_struct_indexing!(tup_struct, 0u64);
+    //~^ ERROR suffixes on a tuple index are invalid
+
+    // NOTE: didn't bother with trying to figure out how to generate `struct P { 0u32: u32 }` using
+    // *only* `proc_macro` without help with `syn`/`quote`, looks like you can't with just
+    // `proc_macro::quote`?
+}
diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr
new file mode 100644
index 00000000000..47d179d3555
--- /dev/null
+++ b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr
@@ -0,0 +1,26 @@
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix-proc-macro.rs:17:28
+   |
+LL |     aux::bad_tup_indexing!(0usize);
+   |                            ^^^^^^ invalid suffix `usize`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix-proc-macro.rs:19:47
+   |
+LL |     aux::bad_tup_struct_indexing!(tup_struct, 0isize);
+   |                                               ^^^^^^ invalid suffix `isize`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix-proc-macro.rs:24:28
+   |
+LL |     aux::bad_tup_indexing!(0u8);
+   |                            ^^^ invalid suffix `u8`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix-proc-macro.rs:26:47
+   |
+LL |     aux::bad_tup_struct_indexing!(tup_struct, 0u64);
+   |                                               ^^^^ invalid suffix `u64`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/parser/tuple-index-suffix.rs b/tests/ui/parser/tuple-index-suffix.rs
new file mode 100644
index 00000000000..c4769500005
--- /dev/null
+++ b/tests/ui/parser/tuple-index-suffix.rs
@@ -0,0 +1,79 @@
+//! Regression test for both the original regression in #59418 where invalid suffixes in indexing
+//! positions were accidentally accepted, and also for the removal of the temporary carve out that
+//! mitigated ecosystem impact following trying to reject #59418 (this was implemented as a FCW
+//! tracked in #60210).
+//!
+//! Check that we hard error on invalid suffixes in tuple indexing subexpressions and struct numeral
+//! field names.
+
+struct X(i32,i32,i32);
+
+fn main() {
+    let tup_struct = X(1, 2, 3);
+    let invalid_tup_struct_suffix = tup_struct.0suffix;
+    //~^ ERROR suffixes on a tuple index are invalid
+    let previous_carve_out_tup_struct_suffix = tup_struct.0i32;
+    //~^ ERROR suffixes on a tuple index are invalid
+
+    let tup = (1, 2, 3);
+    let invalid_tup_suffix = tup.0suffix;
+    //~^ ERROR suffixes on a tuple index are invalid
+    let previous_carve_out_tup_suffix = tup.0u32;
+    //~^ ERROR suffixes on a tuple index are invalid
+
+    numeral_struct_field_name_suffix_invalid();
+    numeral_struct_field_name_suffix_previous_carve_out();
+}
+
+// Previously, there were very limited carve outs as a ecosystem impact mitigation implemented in
+// #60186. *Only* `{i,u}{32,usize}` suffixes were temporarily accepted. Now, they all hard error.
+fn previous_carve_outs() {
+    // Previously temporarily accepted by a pseudo-FCW (#60210), now hard error.
+
+    let previous_carve_out_i32 = (42,).0i32;     //~ ERROR suffixes on a tuple index are invalid
+    let previous_carve_out_i32 = (42,).0u32;     //~ ERROR suffixes on a tuple index are invalid
+    let previous_carve_out_isize = (42,).0isize; //~ ERROR suffixes on a tuple index are invalid
+    let previous_carve_out_usize = (42,).0usize; //~ ERROR suffixes on a tuple index are invalid
+
+    // Not part of the carve outs!
+    let error_i8 = (42,).0i8;      //~ ERROR suffixes on a tuple index are invalid
+    let error_u8 = (42,).0u8;      //~ ERROR suffixes on a tuple index are invalid
+    let error_i16 = (42,).0i16;    //~ ERROR suffixes on a tuple index are invalid
+    let error_u16 = (42,).0u16;    //~ ERROR suffixes on a tuple index are invalid
+    let error_i64 = (42,).0i64;    //~ ERROR suffixes on a tuple index are invalid
+    let error_u64 = (42,).0u64;    //~ ERROR suffixes on a tuple index are invalid
+    let error_i128 = (42,).0i128;  //~ ERROR suffixes on a tuple index are invalid
+    let error_u128 = (42,).0u128;  //~ ERROR suffixes on a tuple index are invalid
+}
+
+fn numeral_struct_field_name_suffix_invalid() {
+    let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 };
+    //~^ ERROR suffixes on a tuple index are invalid
+    match invalid_struct_name {
+        X { 0suffix: _, .. } => {}
+        //~^ ERROR suffixes on a tuple index are invalid
+    }
+}
+
+fn numeral_struct_field_name_suffix_previous_carve_out() {
+    let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 };
+    //~^ ERROR suffixes on a tuple index are invalid
+    match carve_out_struct_name {
+        X { 0u32: _, .. } => {}
+        //~^ ERROR suffixes on a tuple index are invalid
+    }
+}
+
+// Unfortunately, it turns out `std::mem::offset_of!` uses the same expect suffix code path.
+fn offset_of_suffix() {
+    #[repr(C)]
+    pub struct Struct<T>(u8, T);
+
+    // Previous pseudo-FCW carve outs
+    assert_eq!(std::mem::offset_of!(Struct<u32>, 0usize), 0);
+    //~^ ERROR suffixes on a tuple index are invalid
+
+    // Not part of carve outs
+    assert_eq!(std::mem::offset_of!(Struct<u32>, 0u8), 0);
+    //~^ ERROR suffixes on a tuple index are invalid
+}
diff --git a/tests/ui/parser/tuple-index-suffix.stderr b/tests/ui/parser/tuple-index-suffix.stderr
new file mode 100644
index 00000000000..6d96c6d3cbf
--- /dev/null
+++ b/tests/ui/parser/tuple-index-suffix.stderr
@@ -0,0 +1,134 @@
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:13:48
+   |
+LL |     let invalid_tup_struct_suffix = tup_struct.0suffix;
+   |                                                ^^^^^^^ invalid suffix `suffix`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:15:59
+   |
+LL |     let previous_carve_out_tup_struct_suffix = tup_struct.0i32;
+   |                                                           ^^^^ invalid suffix `i32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:19:34
+   |
+LL |     let invalid_tup_suffix = tup.0suffix;
+   |                                  ^^^^^^^ invalid suffix `suffix`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:21:45
+   |
+LL |     let previous_carve_out_tup_suffix = tup.0u32;
+   |                                             ^^^^ invalid suffix `u32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:33:40
+   |
+LL |     let previous_carve_out_i32 = (42,).0i32;
+   |                                        ^^^^ invalid suffix `i32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:34:40
+   |
+LL |     let previous_carve_out_i32 = (42,).0u32;
+   |                                        ^^^^ invalid suffix `u32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:35:42
+   |
+LL |     let previous_carve_out_isize = (42,).0isize;
+   |                                          ^^^^^^ invalid suffix `isize`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:36:42
+   |
+LL |     let previous_carve_out_usize = (42,).0usize;
+   |                                          ^^^^^^ invalid suffix `usize`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:39:26
+   |
+LL |     let error_i8 = (42,).0i8;
+   |                          ^^^ invalid suffix `i8`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:40:26
+   |
+LL |     let error_u8 = (42,).0u8;
+   |                          ^^^ invalid suffix `u8`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:41:27
+   |
+LL |     let error_i16 = (42,).0i16;
+   |                           ^^^^ invalid suffix `i16`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:42:27
+   |
+LL |     let error_u16 = (42,).0u16;
+   |                           ^^^^ invalid suffix `u16`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:43:27
+   |
+LL |     let error_i64 = (42,).0i64;
+   |                           ^^^^ invalid suffix `i64`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:44:27
+   |
+LL |     let error_u64 = (42,).0u64;
+   |                           ^^^^ invalid suffix `u64`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:45:28
+   |
+LL |     let error_i128 = (42,).0i128;
+   |                            ^^^^^ invalid suffix `i128`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:46:28
+   |
+LL |     let error_u128 = (42,).0u128;
+   |                            ^^^^^ invalid suffix `u128`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:50:35
+   |
+LL |     let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 };
+   |                                   ^^^^^^^ invalid suffix `suffix`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:53:13
+   |
+LL |         X { 0suffix: _, .. } => {}
+   |             ^^^^^^^ invalid suffix `suffix`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:59:37
+   |
+LL |     let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 };
+   |                                     ^^^^ invalid suffix `u32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:62:13
+   |
+LL |         X { 0u32: _, .. } => {}
+   |             ^^^^ invalid suffix `u32`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:73:50
+   |
+LL |     assert_eq!(std::mem::offset_of!(Struct<u32>, 0usize), 0);
+   |                                                  ^^^^^^ invalid suffix `usize`
+
+error: suffixes on a tuple index are invalid
+  --> $DIR/tuple-index-suffix.rs:77:50
+   |
+LL |     assert_eq!(std::mem::offset_of!(Struct<u32>, 0u8), 0);
+   |                                                  ^^^ invalid suffix `u8`
+
+error: aborting due to 22 previous errors
+
diff --git a/tests/ui/suggestions/apitit-unimplemented-method.rs b/tests/ui/suggestions/apitit-unimplemented-method.rs
new file mode 100644
index 00000000000..b182e1939b3
--- /dev/null
+++ b/tests/ui/suggestions/apitit-unimplemented-method.rs
@@ -0,0 +1,12 @@
+//@ aux-build:dep.rs
+
+extern crate dep;
+use dep::*;
+
+struct Local;
+impl Trait for Local {}
+//~^ ERROR not all trait items implemented
+//~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }`
+//~| HELP implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`
+
+fn main() {}
diff --git a/tests/ui/suggestions/apitit-unimplemented-method.stderr b/tests/ui/suggestions/apitit-unimplemented-method.stderr
new file mode 100644
index 00000000000..b309a64e958
--- /dev/null
+++ b/tests/ui/suggestions/apitit-unimplemented-method.stderr
@@ -0,0 +1,12 @@
+error[E0046]: not all trait items implemented, missing: `foo`, `bar`
+  --> $DIR/apitit-unimplemented-method.rs:7:1
+   |
+LL | impl Trait for Local {}
+   | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation
+   |
+   = help: implement the missing item: `fn foo(_: impl Sized) { todo!() }`
+   = help: implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/tests/ui/suggestions/auxiliary/dep.rs b/tests/ui/suggestions/auxiliary/dep.rs
new file mode 100644
index 00000000000..ac0de418313
--- /dev/null
+++ b/tests/ui/suggestions/auxiliary/dep.rs
@@ -0,0 +1,4 @@
+pub trait Trait {
+    fn foo(_: impl Sized);
+    fn bar<T>(_: impl Sized);
+}
diff --git a/triagebot.toml b/triagebot.toml
index bf9bfee4306..2d58c616bc2 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1308,21 +1308,32 @@ cc = ["@m-ou-se"]
 [mentions."compiler/rustc_ast_lowering/src/format.rs"]
 cc = ["@m-ou-se"]
 
+# Content-based mentions
+
 [mentions."#[miri::intrinsic_fallback_is_spec]"]
 type = "content"
 message = """
-`#[miri::intrinsic_fallback_is_spec]` must only be used if the function actively checks for all UB cases,
+⚠️ `#[miri::intrinsic_fallback_is_spec]` must only be used if the function actively checks for all UB cases,
 and explores the possible non-determinism of the intrinsic.
 """
 cc = ["@rust-lang/miri"]
+
 [mentions."#[rustc_allow_const_fn_unstable"]
 type = "content"
 message = """
-`#[rustc_allow_const_fn_unstable]` needs careful audit to avoid accidentally exposing unstable
+⚠️ `#[rustc_allow_const_fn_unstable]` needs careful audit to avoid accidentally exposing unstable
 implementation details on stable.
 """
 cc = ["@rust-lang/wg-const-eval"]
 
+[mentions."#[rustc_intrinsic_const_stable_indirect]"]
+type = "content"
+message = """
+⚠️ `#[rustc_intrinsic_const_stable_indirect]` controls whether intrinsics can be exposed to stable const
+code; adding it needs t-lang approval.
+"""
+cc = ["@rust-lang/wg-const-eval"]
+
 
 # ------------------------------------------------------------------------------
 # PR assignments