about summary refs log tree commit diff
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-25 06:41:56 +0000
committerbors <bors@rust-lang.org>2024-09-25 06:41:56 +0000
commit34aff74fb06e78fb9cd1a66acafb6d150638de35 (patch)
tree14533de260c2f1834994e58af1d210a6884625a9 /compiler/rustc_errors/src
parent938c7b1162a38dca257c7004ef7ecf86397a8634 (diff)
parent8be19465ec14880160d294f2909202451a547740 (diff)
downloadrust-34aff74fb06e78fb9cd1a66acafb6d150638de35.tar.gz
rust-34aff74fb06e78fb9cd1a66acafb6d150638de35.zip
Auto merge of #18183 - lnicola:sync-from-rust, r=lnicola
internal: Sync from downstream
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs6
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs73
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs6
-rw-r--r--compiler/rustc_errors/src/emitter.rs10
-rw-r--r--compiler/rustc_errors/src/json.rs20
-rw-r--r--compiler/rustc_errors/src/json/tests.rs162
-rw-r--r--compiler/rustc_errors/src/lib.rs149
-rw-r--r--compiler/rustc_errors/src/lock.rs4
-rw-r--r--compiler/rustc_errors/src/markdown/parse.rs2
-rw-r--r--compiler/rustc_errors/src/tests.rs4
10 files changed, 178 insertions, 258 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index d71ae9d210d..f468fbf4497 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -8,12 +8,12 @@
 use annotate_snippets::{Renderer, Snippet};
 use rustc_data_structures::sync::Lrc;
 use rustc_error_messages::FluentArgs;
-use rustc_span::source_map::SourceMap;
 use rustc_span::SourceFile;
+use rustc_span::source_map::SourceMap;
 
 use crate::emitter::FileWithAnnotatedLines;
 use crate::snippet::Line;
-use crate::translation::{to_fluent_args, Translate};
+use crate::translation::{Translate, to_fluent_args};
 use crate::{
     CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
     Level, MultiSpan, Style, Subdiag,
@@ -48,7 +48,7 @@ impl Emitter for AnnotateSnippetEmitter {
     fn emit_diagnostic(&mut self, mut diag: DiagInner) {
         let fluent_args = to_fluent_args(diag.args.iter());
 
-        let mut suggestions = diag.suggestions.unwrap_or(vec![]);
+        let mut suggestions = diag.suggestions.unwrap_tag();
         self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
 
         self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 3303e4ee752..4352de3ad25 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -7,25 +7,21 @@ use std::panic;
 use std::thread::panicking;
 
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_error_messages::{fluent_value_from_str_list_sep_by_and, FluentValue};
-use rustc_lint_defs::{Applicability, LintExpectationId};
+use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
+use rustc_lint_defs::Applicability;
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Symbol;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Span};
 use tracing::debug;
 
 use crate::snippet::Style;
 use crate::{
     CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level,
     MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
+    Suggestions,
 };
 
-/// Error type for `DiagInner`'s `suggestions` field, indicating that
-/// `.disable_suggestions()` was called on the `DiagInner`.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
-pub struct SuggestionsDisabled;
-
 /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
 /// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic
 /// emission.
@@ -296,7 +292,7 @@ pub struct DiagInner {
     pub code: Option<ErrCode>,
     pub span: MultiSpan,
     pub children: Vec<Subdiag>,
-    pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
+    pub suggestions: Suggestions,
     pub args: DiagArgMap,
 
     /// This is not used for highlighting or rendering any error message. Rather, it can be used
@@ -325,7 +321,7 @@ impl DiagInner {
             code: None,
             span: MultiSpan::new(),
             children: vec![],
-            suggestions: Ok(vec![]),
+            suggestions: Suggestions::Enabled(vec![]),
             args: Default::default(),
             sort_span: DUMMY_SP,
             is_lint: None,
@@ -354,31 +350,6 @@ impl DiagInner {
         }
     }
 
-    pub(crate) fn update_unstable_expectation_id(
-        &mut self,
-        unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>,
-    ) {
-        if let Level::Expect(expectation_id) | Level::ForceWarning(Some(expectation_id)) =
-            &mut self.level
-        {
-            if expectation_id.is_stable() {
-                return;
-            }
-
-            // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index.
-            // The lint index inside the attribute is manually transferred here.
-            let lint_index = expectation_id.get_lint_index();
-            expectation_id.set_lint_index(None);
-            let mut stable_id = unstable_to_stable
-                .get(expectation_id)
-                .expect("each unstable `LintExpectationId` must have a matching stable id")
-                .normalize();
-
-            stable_id.set_lint_index(lint_index);
-            *expectation_id = stable_id;
-        }
-    }
-
     /// Indicates whether this diagnostic should show up in cargo's future breakage report.
     pub(crate) fn has_future_breakage(&self) -> bool {
         matches!(self.is_lint, Some(IsLint { has_future_breakage: true, .. }))
@@ -434,7 +405,7 @@ impl DiagInner {
         &Option<ErrCode>,
         &MultiSpan,
         &[Subdiag],
-        &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
+        &Suggestions,
         Vec<(&DiagArgName, &DiagArgValue)>,
         &Option<IsLint>,
     ) {
@@ -484,7 +455,7 @@ pub struct Subdiag {
 /// - The `EmissionGuarantee`, which determines the type returned from `emit`.
 ///
 /// Each constructed `Diag` must be consumed by a function such as `emit`,
-/// `cancel`, `delay_as_bug`, or `into_diag`. A panic occurrs if a `Diag`
+/// `cancel`, `delay_as_bug`, or `into_diag`. A panic occurs if a `Diag`
 /// is dropped without being consumed by one of these functions.
 ///
 /// If there is some state in a downstream crate you would like to access in
@@ -706,10 +677,10 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
             " ".repeat(expected_padding),
             expected_label
         ))];
-        msg.extend(expected.0.into_iter());
+        msg.extend(expected.0);
         msg.push(StringPart::normal(format!("`{expected_extra}\n")));
         msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label)));
-        msg.extend(found.0.into_iter());
+        msg.extend(found.0);
         msg.push(StringPart::normal(format!("`{found_extra}")));
 
         // For now, just attach these as notes.
@@ -848,16 +819,32 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self
     }
 
-    /// Disallow attaching suggestions this diagnostic.
+    /// Disallow attaching suggestions to this diagnostic.
     /// Any suggestions attached e.g. with the `span_suggestion_*` methods
     /// (before and after the call to `disable_suggestions`) will be ignored.
     #[rustc_lint_diagnostics]
     pub fn disable_suggestions(&mut self) -> &mut Self {
-        self.suggestions = Err(SuggestionsDisabled);
+        self.suggestions = Suggestions::Disabled;
+        self
+    }
+
+    /// Prevent new suggestions from being added to this diagnostic.
+    ///
+    /// Suggestions added before the call to `.seal_suggestions()` will be preserved
+    /// and new suggestions will be ignored.
+    #[rustc_lint_diagnostics]
+    pub fn seal_suggestions(&mut self) -> &mut Self {
+        if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
+            let suggestions_slice = std::mem::take(suggestions).into_boxed_slice();
+            self.suggestions = Suggestions::Sealed(suggestions_slice);
+        }
         self
     }
 
-    /// Helper for pushing to `self.suggestions`, if available (not disable).
+    /// Helper for pushing to `self.suggestions`.
+    ///
+    /// A new suggestion is added if suggestions are enabled for this diagnostic.
+    /// Otherwise, they are ignored.
     #[rustc_lint_diagnostics]
     fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
         for subst in &suggestion.substitutions {
@@ -871,7 +858,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
             }
         }
 
-        if let Ok(suggestions) = &mut self.suggestions {
+        if let Suggestions::Enabled(suggestions) = &mut self.suggestions {
             suggestions.push(suggestion);
         }
     }
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 9e3bc3e60b1..19529f06ef1 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -7,9 +7,9 @@ use std::process::ExitStatus;
 
 use rustc_ast_pretty::pprust;
 use rustc_macros::Subdiagnostic;
+use rustc_span::Span;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
-use rustc_span::Span;
 use rustc_target::abi::TargetDataLayoutErrors;
 use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
 use rustc_type_ir::{ClosureKind, FloatTy};
@@ -17,8 +17,8 @@ use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::diagnostic::DiagLocation;
 use crate::{
-    fluent_generated as fluent, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee,
-    ErrCode, IntoDiagArg, Level, SubdiagMessageOp, Subdiagnostic,
+    Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level,
+    SubdiagMessageOp, Subdiagnostic, fluent_generated as fluent,
 };
 
 pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display);
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 2bc29dabd18..1dc52c4d0a4 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -8,7 +8,7 @@
 //! The output types are defined in `rustc_session::config::ErrorOutputType`.
 
 use std::borrow::Cow;
-use std::cmp::{max, min, Reverse};
+use std::cmp::{Reverse, max, min};
 use std::error::Report;
 use std::io::prelude::*;
 use std::io::{self, IsTerminal};
@@ -22,7 +22,7 @@ use rustc_error_messages::{FluentArgs, SpanLabel};
 use rustc_lint_defs::pluralize;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
-use rustc_span::{char_width, FileLines, FileName, SourceFile, Span};
+use rustc_span::{FileLines, FileName, SourceFile, Span, char_width};
 use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
 use tracing::{debug, instrument, trace, warn};
 
@@ -31,7 +31,7 @@ use crate::snippet::{
     Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
 };
 use crate::styled_buffer::StyledBuffer;
-use crate::translation::{to_fluent_args, Translate};
+use crate::translation::{Translate, to_fluent_args};
 use crate::{
     CodeSuggestion, DiagCtxt, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle,
     Level, MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl,
@@ -498,7 +498,7 @@ impl Emitter for HumanEmitter {
     fn emit_diagnostic(&mut self, mut diag: DiagInner) {
         let fluent_args = to_fluent_args(diag.args.iter());
 
-        let mut suggestions = diag.suggestions.unwrap_or(vec![]);
+        let mut suggestions = diag.suggestions.unwrap_tag();
         self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
 
         self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
@@ -2300,7 +2300,7 @@ impl HumanEmitter {
                     // For example, for the following:
                     //   |
                     // 2 -     .await
-                    // 2 +     (note the left over whitepsace)
+                    // 2 +     (note the left over whitespace)
                     //   |
                     // We really want
                     //   |
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 32e59f9ab03..1972a522e39 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -19,21 +19,22 @@ use derive_setters::Setters;
 use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
 use rustc_error_messages::FluentArgs;
 use rustc_lint_defs::Applicability;
+use rustc_span::Span;
 use rustc_span::hygiene::ExpnData;
 use rustc_span::source_map::SourceMap;
-use rustc_span::Span;
 use serde::Serialize;
 use termcolor::{ColorSpec, WriteColor};
 
 use crate::diagnostic::IsLint;
 use crate::emitter::{
-    should_show_source_code, ColorConfig, Destination, Emitter, HumanEmitter,
-    HumanReadableErrorType,
+    ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType,
+    should_show_source_code,
 };
 use crate::registry::Registry;
-use crate::translation::{to_fluent_args, Translate};
+use crate::translation::{Translate, to_fluent_args};
 use crate::{
-    CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, TerminalUrl,
+    CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
+    TerminalUrl,
 };
 
 #[cfg(test)]
@@ -292,7 +293,7 @@ impl Diagnostic {
     /// Converts from `rustc_errors::DiagInner` to `Diagnostic`.
     fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic {
         let args = to_fluent_args(diag.args.iter());
-        let sugg = diag.suggestions.iter().flatten().map(|sugg| {
+        let sugg_to_diag = |sugg: &CodeSuggestion| {
             let translated_message =
                 je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
             Diagnostic {
@@ -303,7 +304,12 @@ impl Diagnostic {
                 children: vec![],
                 rendered: None,
             }
-        });
+        };
+        let sugg = match &diag.suggestions {
+            Suggestions::Enabled(suggestions) => suggestions.iter().map(sugg_to_diag),
+            Suggestions::Sealed(suggestions) => suggestions.iter().map(sugg_to_diag),
+            Suggestions::Disabled => [].iter().map(sugg_to_diag),
+        };
 
         // generate regular command line output and store it in the json
 
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 6af376d7afd..0de555b83d3 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -1,7 +1,7 @@
 use std::str;
 
-use rustc_span::source_map::FilePathMapping;
 use rustc_span::BytePos;
+use rustc_span::source_map::FilePathMapping;
 use serde::Deserialize;
 
 use super::*;
@@ -69,128 +69,96 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
 
 #[test]
 fn empty() {
-    test_positions(
-        " ",
-        (0, 1),
-        SpanTestData {
-            byte_start: 0,
-            byte_end: 1,
-            line_start: 1,
-            column_start: 1,
-            line_end: 1,
-            column_end: 2,
-        },
-    )
+    test_positions(" ", (0, 1), SpanTestData {
+        byte_start: 0,
+        byte_end: 1,
+        line_start: 1,
+        column_start: 1,
+        line_end: 1,
+        column_end: 2,
+    })
 }
 
 #[test]
 fn bom() {
-    test_positions(
-        "\u{feff} ",
-        (0, 1),
-        SpanTestData {
-            byte_start: 3,
-            byte_end: 4,
-            line_start: 1,
-            column_start: 1,
-            line_end: 1,
-            column_end: 2,
-        },
-    )
+    test_positions("\u{feff} ", (0, 1), SpanTestData {
+        byte_start: 3,
+        byte_end: 4,
+        line_start: 1,
+        column_start: 1,
+        line_end: 1,
+        column_end: 2,
+    })
 }
 
 #[test]
 fn lf_newlines() {
-    test_positions(
-        "\nmod foo;\nmod bar;\n",
-        (5, 12),
-        SpanTestData {
-            byte_start: 5,
-            byte_end: 12,
-            line_start: 2,
-            column_start: 5,
-            line_end: 3,
-            column_end: 3,
-        },
-    )
+    test_positions("\nmod foo;\nmod bar;\n", (5, 12), SpanTestData {
+        byte_start: 5,
+        byte_end: 12,
+        line_start: 2,
+        column_start: 5,
+        line_end: 3,
+        column_end: 3,
+    })
 }
 
 #[test]
 fn crlf_newlines() {
-    test_positions(
-        "\r\nmod foo;\r\nmod bar;\r\n",
-        (5, 12),
-        SpanTestData {
-            byte_start: 6,
-            byte_end: 14,
-            line_start: 2,
-            column_start: 5,
-            line_end: 3,
-            column_end: 3,
-        },
-    )
+    test_positions("\r\nmod foo;\r\nmod bar;\r\n", (5, 12), SpanTestData {
+        byte_start: 6,
+        byte_end: 14,
+        line_start: 2,
+        column_start: 5,
+        line_end: 3,
+        column_end: 3,
+    })
 }
 
 #[test]
 fn crlf_newlines_with_bom() {
-    test_positions(
-        "\u{feff}\r\nmod foo;\r\nmod bar;\r\n",
-        (5, 12),
-        SpanTestData {
-            byte_start: 9,
-            byte_end: 17,
-            line_start: 2,
-            column_start: 5,
-            line_end: 3,
-            column_end: 3,
-        },
-    )
+    test_positions("\u{feff}\r\nmod foo;\r\nmod bar;\r\n", (5, 12), SpanTestData {
+        byte_start: 9,
+        byte_end: 17,
+        line_start: 2,
+        column_start: 5,
+        line_end: 3,
+        column_end: 3,
+    })
 }
 
 #[test]
 fn span_before_crlf() {
-    test_positions(
-        "foo\r\nbar",
-        (2, 3),
-        SpanTestData {
-            byte_start: 2,
-            byte_end: 3,
-            line_start: 1,
-            column_start: 3,
-            line_end: 1,
-            column_end: 4,
-        },
-    )
+    test_positions("foo\r\nbar", (2, 3), SpanTestData {
+        byte_start: 2,
+        byte_end: 3,
+        line_start: 1,
+        column_start: 3,
+        line_end: 1,
+        column_end: 4,
+    })
 }
 
 #[test]
 fn span_on_crlf() {
-    test_positions(
-        "foo\r\nbar",
-        (3, 4),
-        SpanTestData {
-            byte_start: 3,
-            byte_end: 5,
-            line_start: 1,
-            column_start: 4,
-            line_end: 2,
-            column_end: 1,
-        },
-    )
+    test_positions("foo\r\nbar", (3, 4), SpanTestData {
+        byte_start: 3,
+        byte_end: 5,
+        line_start: 1,
+        column_start: 4,
+        line_end: 2,
+        column_end: 1,
+    })
 }
 
 #[test]
 fn span_after_crlf() {
-    test_positions(
-        "foo\r\nbar",
-        (4, 5),
-        SpanTestData {
-            byte_start: 5,
-            byte_end: 6,
-            line_start: 2,
-            column_start: 1,
-            line_end: 2,
-            column_end: 2,
-        },
-    )
+    test_positions("foo\r\nbar", (4, 5), SpanTestData {
+        byte_start: 5,
+        byte_end: 6,
+        line_start: 2,
+        column_start: 1,
+        line_end: 2,
+        column_end: 2,
+    })
 }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 99ee8fb17d7..39acacfbe59 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -42,6 +42,7 @@ use std::ops::DerefMut;
 use std::path::{Path, PathBuf};
 use std::{fmt, panic};
 
+use Level::*;
 pub use codes::*;
 pub use diagnostic::{
     BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString,
@@ -53,29 +54,28 @@ pub use diagnostic_impls::{
     IndicateAnonymousLifetime, SingleLabelManySpans,
 };
 pub use emitter::ColorConfig;
-use emitter::{is_case_difference, is_different, DynEmitter, Emitter};
+use emitter::{DynEmitter, Emitter, is_case_difference, is_different};
 use registry::Registry;
+use rustc_data_structures::AtomicRef;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
 use rustc_data_structures::sync::{Lock, Lrc};
-use rustc_data_structures::AtomicRef;
 pub use rustc_error_messages::{
-    fallback_fluent_bundle, fluent_bundle, DiagMessage, FluentBundle, LanguageIdentifier,
-    LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage,
+    DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel,
+    SubdiagMessage, fallback_fluent_bundle, fluent_bundle,
 };
 use rustc_lint_defs::LintExpectationId;
-pub use rustc_lint_defs::{pluralize, Applicability};
+pub use rustc_lint_defs::{Applicability, pluralize};
 use rustc_macros::{Decodable, Encodable};
+pub use rustc_span::ErrorGuaranteed;
 pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
 use rustc_span::source_map::SourceMap;
-pub use rustc_span::ErrorGuaranteed;
-use rustc_span::{Loc, Span, DUMMY_SP};
+use rustc_span::{DUMMY_SP, Loc, Span};
 pub use snippet::Style;
 // Used by external projects such as `rust-gpu`.
 // See https://github.com/rust-lang/rust/pull/115393.
 pub use termcolor::{Color, ColorSpec, WriteColor};
 use tracing::debug;
-use Level::*;
 
 pub mod annotate_snippet_emitter_writer;
 pub mod codes;
@@ -126,6 +126,41 @@ impl SuggestionStyle {
     }
 }
 
+/// Represents the help messages seen on a diagnostic.
+#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
+pub enum Suggestions {
+    /// Indicates that new suggestions can be added or removed from this diagnostic.
+    ///
+    /// `DiagInner`'s new_* methods initialize the `suggestions` field with
+    /// this variant. Also, this is the default variant for `Suggestions`.
+    Enabled(Vec<CodeSuggestion>),
+    /// Indicates that suggestions cannot be added or removed from this diagnostic.
+    ///
+    /// Gets toggled when `.seal_suggestions()` is called on the `DiagInner`.
+    Sealed(Box<[CodeSuggestion]>),
+    /// Indicates that no suggestion is available for this diagnostic.
+    ///
+    /// Gets toggled when `.disable_suggestions()` is called on the `DiagInner`.
+    Disabled,
+}
+
+impl Suggestions {
+    /// Returns the underlying list of suggestions.
+    pub fn unwrap_tag(self) -> Vec<CodeSuggestion> {
+        match self {
+            Suggestions::Enabled(suggestions) => suggestions,
+            Suggestions::Sealed(suggestions) => suggestions.into_vec(),
+            Suggestions::Disabled => Vec::new(),
+        }
+    }
+}
+
+impl Default for Suggestions {
+    fn default() -> Self {
+        Self::Enabled(vec![])
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
 pub struct CodeSuggestion {
     /// Each substitute can have multiple variants due to multiple
@@ -497,28 +532,18 @@ struct DiagCtxtInner {
 
     future_breakage_diagnostics: Vec<DiagInner>,
 
-    /// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is
-    /// dropped. However, it can have values if the compilation is stopped early
-    /// or is only partially executed. To avoid ICEs, like in rust#94953 we only
-    /// check if [`Self::unstable_expect_diagnostics`] is empty, if the expectation ids
-    /// have been converted.
-    check_unstable_expect_diagnostics: bool,
-
-    /// Expected [`DiagInner`][struct@diagnostic::DiagInner]s store a [`LintExpectationId`] as part
-    /// of the lint level. [`LintExpectationId`]s created early during the compilation
-    /// (before `HirId`s have been defined) are not stable and can therefore not be
-    /// stored on disk. This buffer stores these diagnostics until the ID has been
-    /// replaced by a stable [`LintExpectationId`]. The [`DiagInner`][struct@diagnostic::DiagInner]s
-    /// are submitted for storage and added to the list of fulfilled expectations.
-    unstable_expect_diagnostics: Vec<DiagInner>,
-
     /// expected diagnostic will have the level `Expect` which additionally
     /// carries the [`LintExpectationId`] of the expectation that can be
     /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s
     /// that have been marked as fulfilled this way.
     ///
+    /// Emitting expectations after having stolen this field can happen. In particular, an
+    /// `#[expect(warnings)]` can easily make the `UNFULFILLED_LINT_EXPECTATIONS` lint expect
+    /// itself. To avoid needless complexity in this corner case, we tolerate failing to track
+    /// those expectations.
+    ///
     /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html
-    fulfilled_expectations: FxHashSet<LintExpectationId>,
+    fulfilled_expectations: FxIndexSet<LintExpectationId>,
 
     /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be
     /// stored along side the main panic backtrace.
@@ -546,6 +571,8 @@ pub enum StashKey {
     /// Query cycle detected, stashing in favor of a better error.
     Cycle,
     UndeterminedMacroResolution,
+    /// Used by `Parser::maybe_recover_trailing_expr`
+    ExprInPat,
 }
 
 fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {
@@ -605,13 +632,6 @@ impl Drop for DiagCtxtInner {
                 );
             }
         }
-
-        if self.check_unstable_expect_diagnostics {
-            assert!(
-                self.unstable_expect_diagnostics.is_empty(),
-                "all diagnostics with unstable expectations should have been converted",
-            );
-        }
     }
 }
 
@@ -740,8 +760,6 @@ impl DiagCtxt {
             emitted_diagnostics,
             stashed_diagnostics,
             future_breakage_diagnostics,
-            check_unstable_expect_diagnostics,
-            unstable_expect_diagnostics,
             fulfilled_expectations,
             ice_file: _,
         } = inner.deref_mut();
@@ -761,8 +779,6 @@ impl DiagCtxt {
         *emitted_diagnostics = Default::default();
         *stashed_diagnostics = Default::default();
         *future_breakage_diagnostics = Default::default();
-        *check_unstable_expect_diagnostics = false;
-        *unstable_expect_diagnostics = Default::default();
         *fulfilled_expectations = Default::default();
     }
 
@@ -817,7 +833,7 @@ impl<'a> DiagCtxtHandle<'a> {
                 );
             }
             // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs`
-            // can be used to create a backtrace at the stashing site insted of whenever the
+            // can be used to create a backtrace at the stashing site instead of whenever the
             // diagnostic context is dropped and thus delayed bugs are emitted.
             Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
             DelayedBug => {
@@ -1094,44 +1110,10 @@ impl<'a> DiagCtxtHandle<'a> {
         inner.emitter.emit_unused_externs(lint_level, unused_externs)
     }
 
-    pub fn update_unstable_expectation_id(
-        &self,
-        unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>,
-    ) {
-        let mut inner = self.inner.borrow_mut();
-        let diags = std::mem::take(&mut inner.unstable_expect_diagnostics);
-        inner.check_unstable_expect_diagnostics = true;
-
-        if !diags.is_empty() {
-            inner.suppressed_expected_diag = true;
-            for mut diag in diags.into_iter() {
-                diag.update_unstable_expectation_id(unstable_to_stable);
-
-                // Here the diagnostic is given back to `emit_diagnostic` where it was first
-                // intercepted. Now it should be processed as usual, since the unstable expectation
-                // id is now stable.
-                inner.emit_diagnostic(diag, self.tainted_with_errors);
-            }
-        }
-
-        inner
-            .stashed_diagnostics
-            .values_mut()
-            .for_each(|(diag, _guar)| diag.update_unstable_expectation_id(unstable_to_stable));
-        inner
-            .future_breakage_diagnostics
-            .iter_mut()
-            .for_each(|diag| diag.update_unstable_expectation_id(unstable_to_stable));
-    }
-
     /// This methods steals all [`LintExpectationId`]s that are stored inside
     /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled.
     #[must_use]
-    pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet<LintExpectationId> {
-        assert!(
-            self.inner.borrow().unstable_expect_diagnostics.is_empty(),
-            "`DiagCtxtInner::unstable_expect_diagnostics` should be empty at this point",
-        );
+    pub fn steal_fulfilled_expectation_ids(&self) -> FxIndexSet<LintExpectationId> {
         std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations)
     }
 
@@ -1440,8 +1422,6 @@ impl DiagCtxtInner {
             emitted_diagnostics: Default::default(),
             stashed_diagnostics: Default::default(),
             future_breakage_diagnostics: Vec::new(),
-            check_unstable_expect_diagnostics: false,
-            unstable_expect_diagnostics: Vec::new(),
             fulfilled_expectations: Default::default(),
             ice_file: None,
         }
@@ -1471,24 +1451,6 @@ impl DiagCtxtInner {
         mut diagnostic: DiagInner,
         taint: Option<&Cell<Option<ErrorGuaranteed>>>,
     ) -> Option<ErrorGuaranteed> {
-        match diagnostic.level {
-            Expect(expect_id) | ForceWarning(Some(expect_id)) => {
-                // The `LintExpectationId` can be stable or unstable depending on when it was
-                // created. Diagnostics created before the definition of `HirId`s are unstable and
-                // can not yet be stored. Instead, they are buffered until the `LintExpectationId`
-                // is replaced by a stable one by the `LintLevelsBuilder`.
-                if let LintExpectationId::Unstable { .. } = expect_id {
-                    // We don't call TRACK_DIAGNOSTIC because we wait for the
-                    // unstable ID to be updated, whereupon the diagnostic will be
-                    // passed into this method again.
-                    self.unstable_expect_diagnostics.push(diagnostic);
-                    return None;
-                }
-                // Continue through to the `Expect`/`ForceWarning` case below.
-            }
-            _ => {}
-        }
-
         if diagnostic.has_future_breakage() {
             // Future breakages aren't emitted if they're `Level::Allow` or
             // `Level::Expect`, but they still need to be constructed and
@@ -1564,10 +1526,7 @@ impl DiagCtxtInner {
                 return None;
             }
             Expect(expect_id) | ForceWarning(Some(expect_id)) => {
-                if let LintExpectationId::Unstable { .. } = expect_id {
-                    unreachable!(); // this case was handled at the top of this function
-                }
-                self.fulfilled_expectations.insert(expect_id.normalize());
+                self.fulfilled_expectations.insert(expect_id);
                 if let Expect(_) = diagnostic.level {
                     // Nothing emitted here for expected lints.
                     TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
diff --git a/compiler/rustc_errors/src/lock.rs b/compiler/rustc_errors/src/lock.rs
index 7557969f374..85199c34a14 100644
--- a/compiler/rustc_errors/src/lock.rs
+++ b/compiler/rustc_errors/src/lock.rs
@@ -16,11 +16,11 @@ pub(crate) fn acquire_global_lock(name: &str) -> Box<dyn Any> {
     use std::ffi::CString;
     use std::io;
 
-    use windows::core::PCSTR;
     use windows::Win32::Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0};
     use windows::Win32::System::Threading::{
-        CreateMutexA, ReleaseMutex, WaitForSingleObject, INFINITE,
+        CreateMutexA, INFINITE, ReleaseMutex, WaitForSingleObject,
     };
+    use windows::core::PCSTR;
 
     struct Handle(HANDLE);
 
diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs
index b1db44dd215..8dd146c1c33 100644
--- a/compiler/rustc_errors/src/markdown/parse.rs
+++ b/compiler/rustc_errors/src/markdown/parse.rs
@@ -487,7 +487,7 @@ fn is_break_ty(val: &MdTree<'_>) -> bool {
         || matches!(val, MdTree::PlainText(txt) if txt.trim().is_empty())
 }
 
-/// Perform tranformations to text. This splits paragraphs, replaces patterns,
+/// Perform transformations to text. This splits paragraphs, replaces patterns,
 /// and corrects newlines.
 ///
 /// To avoid allocating strings (and using a different heavier tt type), our
diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs
index bfe4c9f2a3a..17a1635bd11 100644
--- a/compiler/rustc_errors/src/tests.rs
+++ b/compiler/rustc_errors/src/tests.rs
@@ -1,11 +1,11 @@
 use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
 use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
-use rustc_error_messages::{langid, DiagMessage};
+use rustc_error_messages::{DiagMessage, langid};
 
+use crate::FluentBundle;
 use crate::error::{TranslateError, TranslateErrorKind};
 use crate::fluent_bundle::*;
 use crate::translation::Translate;
-use crate::FluentBundle;
 
 struct Dummy {
     bundle: FluentBundle,