about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs2
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs2
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs10
-rw-r--r--compiler/rustc_errors/src/json.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs96
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs2
-rw-r--r--compiler/rustc_lint/src/context.rs4
-rw-r--r--compiler/rustc_lint/src/expect.rs16
-rw-r--r--compiler/rustc_lint/src/levels.rs22
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs14
-rw-r--r--compiler/rustc_middle/src/lint.rs16
-rw-r--r--compiler/rustc_session/src/config.rs2
-rw-r--r--compiler/rustc_session/src/session.rs15
-rw-r--r--library/alloc/src/collections/btree/map.rs7
-rw-r--r--library/std/src/collections/hash/map.rs6
-rw-r--r--src/bootstrap/bin/rustc.rs12
-rw-r--r--src/etc/natvis/liballoc.natvis126
-rw-r--r--src/test/debuginfo/rc_arc.rs144
-rw-r--r--src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.rs48
-rw-r--r--src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr40
-rw-r--r--src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.rs49
-rw-r--r--src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.stderr38
-rw-r--r--src/tools/rustfmt/src/parse/session.rs8
24 files changed, 550 insertions, 133 deletions
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index d05d09a11ea..ab8874d796d 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -336,7 +336,7 @@ fn report_inline_asm(
     }
     let level = match level {
         llvm::DiagnosticLevel::Error => Level::Error { lint: false },
-        llvm::DiagnosticLevel::Warning => Level::Warning,
+        llvm::DiagnosticLevel::Warning => Level::Warning(None),
         llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
     };
     cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 02c7c1a435f..632f07c5c2d 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1761,7 +1761,7 @@ impl SharedEmitterMain {
 
                     let mut err = match level {
                         Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
-                        Level::Warning => sess.struct_warn(msg),
+                        Level::Warning(_) => sess.struct_warn(msg),
                         Level::Note => sess.struct_note_without_error(msg),
                         _ => bug!("Invalid inline asm diagnostic level"),
                     };
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 1f270fcf56b..0fcd61d1e58 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -87,7 +87,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
         Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
             AnnotationType::Error
         }
-        Level::Warning => AnnotationType::Warning,
+        Level::Warning(_) => AnnotationType::Warning,
         Level::Note | Level::OnceNote => AnnotationType::Note,
         Level::Help => AnnotationType::Help,
         // FIXME(#59346): Not sure how to map this level
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 00c0ff8bcaf..b8545139cec 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -209,7 +209,7 @@ impl Diagnostic {
             | Level::Error { .. }
             | Level::FailureNote => true,
 
-            Level::Warning
+            Level::Warning(_)
             | Level::Note
             | Level::OnceNote
             | Level::Help
@@ -222,7 +222,9 @@ impl Diagnostic {
         &mut self,
         unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
     ) {
-        if let Level::Expect(expectation_id) = &mut self.level {
+        if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
+            &mut self.level
+        {
             if expectation_id.is_stable() {
                 return;
             }
@@ -450,7 +452,7 @@ impl Diagnostic {
     /// Add a warning attached to this diagnostic.
     #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Warning, msg, MultiSpan::new(), None);
+        self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
         self
     }
 
@@ -462,7 +464,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Warning, msg, sp.into(), None);
+        self.sub(Level::Warning(None), msg, sp.into(), None);
         self
     }
 
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index fff35ac6ac8..d4d1491c169 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -154,7 +154,7 @@ impl Emitter for JsonEmitter {
             .into_iter()
             .map(|mut diag| {
                 if diag.level == crate::Level::Allow {
-                    diag.level = crate::Level::Warning;
+                    diag.level = crate::Level::Warning(None);
                 }
                 FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) }
             })
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 8b6eba122f8..aa4ea82dffb 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -660,6 +660,23 @@ impl Handler {
         result
     }
 
+    /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
+    /// The `id` is used for lint emissions which should also fulfill a lint expectation.
+    ///
+    /// Attempting to `.emit()` the builder will only emit if either:
+    /// * `can_emit_warnings` is `true`
+    /// * `is_force_warn` was set in `DiagnosticId::Lint`
+    pub fn struct_span_warn_with_expectation(
+        &self,
+        span: impl Into<MultiSpan>,
+        msg: impl Into<DiagnosticMessage>,
+        id: LintExpectationId,
+    ) -> DiagnosticBuilder<'_, ()> {
+        let mut result = self.struct_warn_with_expectation(msg, id);
+        result.set_span(span);
+        result
+    }
+
     /// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
     #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
     pub fn struct_span_allow(
@@ -693,7 +710,21 @@ impl Handler {
     /// * `is_force_warn` was set in `DiagnosticId::Lint`
     #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Level::Warning, msg)
+        DiagnosticBuilder::new(self, Level::Warning(None), msg)
+    }
+
+    /// Construct a builder at the `Warning` level with the `msg`. The `id` is used for
+    /// lint emissions which should also fulfill a lint expectation.
+    ///
+    /// Attempting to `.emit()` the builder will only emit if either:
+    /// * `can_emit_warnings` is `true`
+    /// * `is_force_warn` was set in `DiagnosticId::Lint`
+    pub fn struct_warn_with_expectation(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+        id: LintExpectationId,
+    ) -> DiagnosticBuilder<'_, ()> {
+        DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg)
     }
 
     /// Construct a builder at the `Allow` level with the `msg`.
@@ -864,7 +895,7 @@ impl Handler {
 
     #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
     pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
-        self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
+        self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span);
     }
 
     #[cfg_attr(not(bootstrap), rustc_lint_diagnostics)]
@@ -874,7 +905,7 @@ impl Handler {
         msg: impl Into<DiagnosticMessage>,
         code: DiagnosticId,
     ) {
-        self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span);
+        self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span);
     }
 
     pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
@@ -928,7 +959,7 @@ impl Handler {
     }
 
     pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
-        let mut db = DiagnosticBuilder::new(self, Warning, msg);
+        let mut db = DiagnosticBuilder::new(self, Warning(None), msg);
         db.emit();
     }
 
@@ -1033,13 +1064,10 @@ impl Handler {
             for mut diag in diags.into_iter() {
                 diag.update_unstable_expectation_id(unstable_to_stable);
 
-                let stable_id = diag
-                    .level
-                    .get_expectation_id()
-                    .expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`");
-                inner.fulfilled_expectations.insert(stable_id);
-
-                (*TRACK_DIAGNOSTICS)(&diag);
+                // 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(&mut diag);
             }
         }
 
@@ -1089,6 +1117,15 @@ impl HandlerInner {
 
     // FIXME(eddyb) this should ideally take `diagnostic` by value.
     fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> {
+        // 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 Some(LintExpectationId::Unstable { .. }) = diagnostic.level.get_expectation_id() {
+            self.unstable_expect_diagnostics.push(diagnostic.clone());
+            return None;
+        }
+
         if diagnostic.level == Level::DelayedBug {
             // FIXME(eddyb) this should check for `has_errors` and stop pushing
             // once *any* errors were emitted (and truncate `delayed_span_bugs`
@@ -1105,7 +1142,12 @@ impl HandlerInner {
             self.future_breakage_diagnostics.push(diagnostic.clone());
         }
 
-        if diagnostic.level == Warning
+        if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
+            self.suppressed_expected_diag = true;
+            self.fulfilled_expectations.insert(expectation_id);
+        }
+
+        if matches!(diagnostic.level, Warning(_))
             && !self.flags.can_emit_warnings
             && !diagnostic.is_force_warn()
         {
@@ -1115,22 +1157,9 @@ impl HandlerInner {
             return None;
         }
 
-        // 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 Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level {
-            self.unstable_expect_diagnostics.push(diagnostic.clone());
-            return None;
-        }
-
         (*TRACK_DIAGNOSTICS)(diagnostic);
 
-        if let Level::Expect(expectation_id) = diagnostic.level {
-            self.suppressed_expected_diag = true;
-            self.fulfilled_expectations.insert(expectation_id);
-            return None;
-        } else if diagnostic.level == Allow {
+        if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) {
             return None;
         }
 
@@ -1167,7 +1196,7 @@ impl HandlerInner {
             self.emitter.emit_diagnostic(&diagnostic);
             if diagnostic.is_error() {
                 self.deduplicated_err_count += 1;
-            } else if diagnostic.level == Warning {
+            } else if let Warning(_) = diagnostic.level {
                 self.deduplicated_warn_count += 1;
             }
         }
@@ -1220,7 +1249,7 @@ impl HandlerInner {
         match (errors.len(), warnings.len()) {
             (0, 0) => return,
             (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(
-                Level::Warning,
+                Level::Warning(None),
                 DiagnosticMessage::Str(warnings),
             )),
             (_, 0) => {
@@ -1453,7 +1482,10 @@ pub enum Level {
         /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
         lint: bool,
     },
-    Warning,
+    /// This [`LintExpectationId`] is used for expected lint diagnostics, which should
+    /// also emit a warning due to the `force-warn` flag. In all other cases this should
+    /// be `None`.
+    Warning(Option<LintExpectationId>),
     Note,
     /// A note that is only emitted once.
     OnceNote,
@@ -1476,7 +1508,7 @@ impl Level {
             Bug | DelayedBug | Fatal | Error { .. } => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
-            Warning => {
+            Warning(_) => {
                 spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
             }
             Note | OnceNote => {
@@ -1495,7 +1527,7 @@ impl Level {
         match self {
             Bug | DelayedBug => "error: internal compiler error",
             Fatal | Error { .. } => "error",
-            Warning => "warning",
+            Warning(_) => "warning",
             Note | OnceNote => "note",
             Help => "help",
             FailureNote => "failure-note",
@@ -1510,7 +1542,7 @@ impl Level {
 
     pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
         match self {
-            Level::Expect(id) => Some(*id),
+            Level::Expect(id) | Level::Warning(Some(id)) => Some(*id),
             _ => None,
         }
     }
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index d4407c03d03..e7ce9e7f1b7 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -267,7 +267,7 @@ impl ToInternal<rustc_errors::Level> for Level {
     fn to_internal(self) -> rustc_errors::Level {
         match self {
             Level::Error => rustc_errors::Level::Error { lint: false },
-            Level::Warning => rustc_errors::Level::Warning,
+            Level::Warning => rustc_errors::Level::Warning(None),
             Level::Note => rustc_errors::Level::Note,
             Level::Help => rustc_errors::Level::Help,
             _ => unreachable!("unknown proc_macro::Level variant: {:?}", self),
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 2084494f771..eeb66f2d738 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -324,7 +324,7 @@ impl LintStore {
         registered_tools: &RegisteredTools,
     ) {
         let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
-        if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn {
+        if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
             struct_span_err!(
                 sess,
                 DUMMY_SP,
@@ -375,7 +375,7 @@ impl LintStore {
                 match level {
                     Level::Allow => "-A",
                     Level::Warn => "-W",
-                    Level::ForceWarn => "--force-warn",
+                    Level::ForceWarn(_) => "--force-warn",
                     Level::Deny => "-D",
                     Level::Forbid => "-F",
                     Level::Expect(_) => {
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index dc48ac0a618..95e3125045d 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -19,16 +19,16 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
     let lint_expectations = &tcx.lint_levels(()).lint_expectations;
 
     for (id, expectation) in lint_expectations {
-        if !fulfilled_expectations.contains(id)
-            && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
-        {
-            // This check will always be true, since `lint_expectations` only
-            // holds stable ids
-            if let LintExpectationId::Stable { hir_id, .. } = id {
+        // This check will always be true, since `lint_expectations` only
+        // holds stable ids
+        if let LintExpectationId::Stable { hir_id, .. } = id {
+            if !fulfilled_expectations.contains(&id)
+                && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
+            {
                 emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation);
-            } else {
-                unreachable!("at this stage all `LintExpectationId`s are stable");
             }
+        } else {
+            unreachable!("at this stage all `LintExpectationId`s are stable");
         }
     }
 }
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 7faac5b5296..4773feded12 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -117,7 +117,9 @@ impl<'s> LintLevelsBuilder<'s> {
             };
             for id in ids {
                 // ForceWarn and Forbid cannot be overridden
-                if let Some((Level::ForceWarn | Level::Forbid, _)) = self.current_specs().get(&id) {
+                if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
+                    self.current_specs().get(&id)
+                {
                     continue;
                 }
 
@@ -226,11 +228,18 @@ impl<'s> LintLevelsBuilder<'s> {
             return;
         }
 
-        if let Level::ForceWarn = old_level {
-            self.current_specs_mut().insert(id, (old_level, old_src));
-        } else {
-            self.current_specs_mut().insert(id, (level, src));
-        }
+        match (old_level, level) {
+            // If the new level is an expectation store it in `ForceWarn`
+            (Level::ForceWarn(_), Level::Expect(expectation_id)) => self
+                .current_specs_mut()
+                .insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)),
+            // Keep `ForceWarn` level but drop the expectation
+            (Level::ForceWarn(_), _) => {
+                self.current_specs_mut().insert(id, (Level::ForceWarn(None), old_src))
+            }
+            // Set the lint level as normal
+            _ => self.current_specs_mut().insert(id, (level, src)),
+        };
     }
 
     /// Pushes a list of AST lint attributes onto this context.
@@ -269,6 +278,7 @@ impl<'s> LintLevelsBuilder<'s> {
 
             let level = match Level::from_attr(attr) {
                 None => continue,
+                // This is the only lint level with a `LintExpectationId` that can be created from an attribute
                 Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
                     let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index);
 
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 913dc58a102..cb1c6f40987 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -162,13 +162,19 @@ pub enum Level {
     ///
     /// See RFC 2383.
     ///
-    /// The `LintExpectationId` is used to later link a lint emission to the actual
+    /// The [`LintExpectationId`] is used to later link a lint emission to the actual
     /// expectation. It can be ignored in most cases.
     Expect(LintExpectationId),
     /// The `warn` level will produce a warning if the lint was violated, however the
     /// compiler will continue with its execution.
     Warn,
-    ForceWarn,
+    /// This lint level is a special case of [`Warn`], that can't be overridden. This is used
+    /// to ensure that a lint can't be suppressed. This lint level can currently only be set
+    /// via the console and is therefore session specific.
+    ///
+    /// The [`LintExpectationId`] is intended to fulfill expectations marked via the
+    /// `#[expect]` attribute, that will still be suppressed due to the level.
+    ForceWarn(Option<LintExpectationId>),
     /// The `deny` level will produce an error and stop further execution after the lint
     /// pass is complete.
     Deny,
@@ -184,7 +190,7 @@ impl Level {
             Level::Allow => "allow",
             Level::Expect(_) => "expect",
             Level::Warn => "warn",
-            Level::ForceWarn => "force-warn",
+            Level::ForceWarn(_) => "force-warn",
             Level::Deny => "deny",
             Level::Forbid => "forbid",
         }
@@ -219,7 +225,7 @@ impl Level {
 
     pub fn is_error(self) -> bool {
         match self {
-            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn => false,
+            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
             Level::Deny | Level::Forbid => true,
         }
     }
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index c7c5f56867a..215d8decf2a 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -115,7 +115,7 @@ impl LintLevelSets {
 
         // Ensure that we never exceed the `--cap-lints` argument
         // unless the source is a --force-warn
-        level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
+        level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
             level
         } else {
             cmp::min(level, self.lint_cap)
@@ -266,7 +266,7 @@ pub fn explain_lint_level_source(
                 Level::Deny => "-D",
                 Level::Forbid => "-F",
                 Level::Allow => "-A",
-                Level::ForceWarn => "--force-warn",
+                Level::ForceWarn(_) => "--force-warn",
                 Level::Expect(_) => {
                     unreachable!("the expect level does not have a commandline flag")
                 }
@@ -352,8 +352,14 @@ pub fn struct_lint_level<'s, 'd>(
                 // create a `DiagnosticBuilder` and continue as we would for warnings.
                 sess.struct_expect("", expect_id)
             }
-            (Level::Warn | Level::ForceWarn, Some(span)) => sess.struct_span_warn(span, ""),
-            (Level::Warn | Level::ForceWarn, None) => sess.struct_warn(""),
+            (Level::ForceWarn(Some(expect_id)), Some(span)) => {
+                sess.struct_span_warn_with_expectation(span, "", expect_id)
+            }
+            (Level::ForceWarn(Some(expect_id)), None) => {
+                sess.struct_warn_with_expectation("", expect_id)
+            }
+            (Level::Warn | Level::ForceWarn(None), Some(span)) => sess.struct_span_warn(span, ""),
+            (Level::Warn | Level::ForceWarn(None), None) => sess.struct_warn(""),
             (Level::Deny | Level::Forbid, Some(span)) => {
                 let mut builder = sess.diagnostic().struct_err_lint("");
                 builder.set_span(span);
@@ -398,7 +404,7 @@ pub fn struct_lint_level<'s, 'd>(
         explain_lint_level_source(lint, level, src, &mut err);
 
         let name = lint.name_lower();
-        let is_force_warn = matches!(level, Level::ForceWarn);
+        let is_force_warn = matches!(level, Level::ForceWarn(_));
         err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
 
         if let Some(future_incompatible) = future_incompatible {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index c4a67006504..89d724626cc 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1432,7 +1432,7 @@ pub fn get_cmd_lint_options(
     let mut lint_opts_with_position = vec![];
     let mut describe_lints = false;
 
-    for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
+    for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] {
         for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
             if lint_name == "help" {
                 describe_lints = true;
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 2186456576a..f1814eebfa6 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -287,6 +287,14 @@ impl Session {
     ) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_span_warn(sp, msg)
     }
+    pub fn struct_span_warn_with_expectation<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        msg: impl Into<DiagnosticMessage>,
+        id: lint::LintExpectationId,
+    ) -> DiagnosticBuilder<'_, ()> {
+        self.diagnostic().struct_span_warn_with_expectation(sp, msg, id)
+    }
     pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
@@ -298,6 +306,13 @@ impl Session {
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_warn(msg)
     }
+    pub fn struct_warn_with_expectation(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+        id: lint::LintExpectationId,
+    ) -> DiagnosticBuilder<'_, ()> {
+        self.diagnostic().struct_warn_with_expectation(msg, id)
+    }
     pub fn struct_span_allow<S: Into<MultiSpan>>(
         &self,
         sp: S,
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 95d9c3142dd..f23980faa04 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -161,6 +161,9 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT;
 /// // update a key, guarding against the key possibly not being set
 /// let stat = player_stats.entry("attack").or_insert(100);
 /// *stat += random_stat_buff();
+///
+/// // modify an entry before an insert with in-place mutation
+/// player_stats.entry("mana").and_modify(|mana| *mana += 200).or_insert(100);
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "BTreeMap")]
@@ -1211,10 +1214,12 @@ impl<K, V, A: Allocator> BTreeMap<K, V, A> {
     ///
     /// // count the number of occurrences of letters in the vec
     /// for x in ["a", "b", "a", "c", "a", "b"] {
-    ///     *count.entry(x).or_insert(0) += 1;
+    ///     count.entry(x).and_modify(|curr| *curr += 1).or_insert(1);
     /// }
     ///
     /// assert_eq!(count["a"], 3);
+    /// assert_eq!(count["b"], 2);
+    /// assert_eq!(count["c"], 1);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn entry(&mut self, key: K) -> Entry<'_, K, V, A>
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index d38fecc45b2..192a21f2ffc 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -164,6 +164,9 @@ use crate::sys;
 /// // update a key, guarding against the key possibly not being set
 /// let stat = player_stats.entry("attack").or_insert(100);
 /// *stat += random_stat_buff();
+///
+/// // modify an entry before an insert with in-place mutation
+/// player_stats.entry("mana").and_modify(|mana| *mana += 200).or_insert(100);
 /// ```
 ///
 /// The easiest way to use `HashMap` with a custom key type is to derive [`Eq`] and [`Hash`].
@@ -829,8 +832,7 @@ where
     /// let mut letters = HashMap::new();
     ///
     /// for ch in "a short treatise on fungi".chars() {
-    ///     let counter = letters.entry(ch).or_insert(0);
-    ///     *counter += 1;
+    ///     letters.entry(ch).and_modify(|counter| *counter += 1).or_insert(1);
     /// }
     ///
     /// assert_eq!(letters[&'s'], 2);
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index d9467e8fd6b..40a3cc6d12c 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -127,12 +127,18 @@ fn main() {
             }
         }
 
+        // Cargo doesn't pass RUSTFLAGS to proc_macros:
+        // https://github.com/rust-lang/cargo/issues/4423
+        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
+        // We also declare that the flag is expected, which is mainly needed for
+        // later stages so that they don't warn about #[cfg(bootstrap)],
+        // but enabling it for stage 0 too lets any warnings, if they occur,
+        // occur more early on, e.g. about #[cfg(bootstrap = "foo")].
         if stage == "0" {
-            // Cargo doesn't pass RUSTFLAGS to proc_macros:
-            // https://github.com/rust-lang/cargo/issues/4423
-            // Set `--cfg=bootstrap` explicitly instead.
             cmd.arg("--cfg=bootstrap");
         }
+        cmd.arg("-Zunstable-options");
+        cmd.arg("--check-cfg=values(bootstrap)");
     }
 
     if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") {
diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis
index 83ca8ed932e..912418fa7d1 100644
--- a/src/etc/natvis/liballoc.natvis
+++ b/src/etc/natvis/liballoc.natvis
@@ -59,39 +59,133 @@
     </Expand>
   </Type>
 
+  <!--
+      The display string for Rc, Arc, etc is optional because the expression cannot be evaluated
+      if the pointee is unsized (i.e. if `ptr.pointer` is a fat pointer).
+
+      There are also two versions for the reference count fields, one for sized and one for
+      dyn pointees.
+
+      Rc<[T]> and Arc<[T]> are handled separately altogether so we can actually show
+      the slice values.
+  -->
+  <!-- alloc::rc::Rc<T> -->
   <Type Name="alloc::rc::Rc&lt;*&gt;">
-    <DisplayString>{ptr.pointer->value}</DisplayString>
+    <DisplayString Optional="true">{ptr.pointer->value}</DisplayString>
     <Expand>
-      <ExpandedItem>ptr.pointer->value</ExpandedItem>
-      <Item Name="[Reference count]">ptr.pointer->strong</Item>
-      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+      <!-- thin -->
+      <ExpandedItem Optional="true">ptr.pointer->value</ExpandedItem>
+      <Item Name="[Reference count]" Optional="true">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer->weak</Item>
+
+      <!-- dyn -->
+      <Item Name="[Reference count]" Optional="true">ptr.pointer.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer.pointer->weak</Item>
+    </Expand>
+  </Type>
+
+  <!-- alloc::rc::Rc<[T]> -->
+  <Type Name="alloc::rc::Rc&lt;slice$&lt;*&gt; &gt;">
+    <DisplayString>{{ len={ptr.pointer.length} }}</DisplayString>
+    <Expand>
+      <Item Name="[Length]" ExcludeView="simple">ptr.pointer.length</Item>
+      <Item Name="[Reference count]">ptr.pointer.data_ptr->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer.data_ptr->weak</Item>
+      <ArrayItems>
+        <Size>ptr.pointer.length</Size>
+        <!-- We add +2 to the data_ptr in order to skip the ref count fields in the RcBox -->
+        <ValuePointer>($T1*)(((size_t*)ptr.pointer.data_ptr) + 2)</ValuePointer>
+      </ArrayItems>
     </Expand>
   </Type>
+
+  <!-- alloc::rc::Weak<T> -->
   <Type Name="alloc::rc::Weak&lt;*&gt;">
-    <DisplayString>{ptr.pointer->value}</DisplayString>
+    <DisplayString Optional="true">{ptr.pointer->value}</DisplayString>
+    <Expand>
+      <!-- thin -->
+      <ExpandedItem Optional="true">ptr.pointer->value</ExpandedItem>
+      <Item Name="[Reference count]" Optional="true">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer->weak</Item>
+
+      <!-- dyn -->
+      <Item Name="[Reference count]" Optional="true">ptr.pointer.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer.pointer->weak</Item>
+    </Expand>
+  </Type>
+
+  <!-- alloc::rc::Weak<[T]> -->
+  <Type Name="alloc::rc::Weak&lt;slice$&lt;*&gt; &gt;">
+    <DisplayString>{{ len={ptr.pointer.length} }}</DisplayString>
     <Expand>
-      <ExpandedItem>ptr.pointer->value</ExpandedItem>
-      <Item Name="[Reference count]">ptr.pointer->strong</Item>
-      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+      <Item Name="[Length]" ExcludeView="simple">ptr.pointer.length</Item>
+      <Item Name="[Reference count]">ptr.pointer.data_ptr->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer.data_ptr->weak</Item>
+      <ArrayItems>
+        <Size>ptr.pointer.length</Size>
+        <ValuePointer>($T1*)(((size_t*)ptr.pointer.data_ptr) + 2)</ValuePointer>
+      </ArrayItems>
     </Expand>
   </Type>
 
+  <!-- alloc::sync::Arc<T> -->
   <Type Name="alloc::sync::Arc&lt;*&gt;">
-    <DisplayString>{ptr.pointer->data}</DisplayString>
+    <DisplayString Optional="true">{ptr.pointer->data}</DisplayString>
     <Expand>
-      <ExpandedItem>ptr.pointer->data</ExpandedItem>
-      <Item Name="[Reference count]">ptr.pointer->strong</Item>
-      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+      <!-- thin -->
+      <ExpandedItem Optional="true">ptr.pointer->data</ExpandedItem>
+      <Item Name="[Reference count]" Optional="true">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer->weak</Item>
+
+      <!-- dyn -->
+      <Item Name="[Reference count]" Optional="true">ptr.pointer.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer.pointer->weak</Item>
+    </Expand>
+  </Type>
+
+  <!-- alloc::sync::Arc<[T]> -->
+  <Type Name="alloc::sync::Arc&lt;slice$&lt;*&gt; &gt;">
+    <DisplayString>{{ len={ptr.pointer.length} }}</DisplayString>
+    <Expand>
+      <Item Name="[Length]" ExcludeView="simple">ptr.pointer.length</Item>
+      <Item Name="[Reference count]">ptr.pointer.data_ptr->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer.data_ptr->weak</Item>
+      <ArrayItems>
+        <Size>ptr.pointer.length</Size>
+        <ValuePointer>($T1*)(((size_t*)ptr.pointer.data_ptr) + 2)</ValuePointer>
+      </ArrayItems>
     </Expand>
   </Type>
+
+  <!-- alloc::sync::Weak<T> -->
   <Type Name="alloc::sync::Weak&lt;*&gt;">
-    <DisplayString>{ptr.pointer->data}</DisplayString>
+    <DisplayString Optional="true">{ptr.pointer->data}</DisplayString>
     <Expand>
-      <ExpandedItem>ptr.pointer->data</ExpandedItem>
-      <Item Name="[Reference count]">ptr.pointer->strong</Item>
-      <Item Name="[Weak reference count]">ptr.pointer->weak</Item>
+      <!-- thin -->
+      <ExpandedItem Optional="true">ptr.pointer->data</ExpandedItem>
+      <Item Name="[Reference count]" Optional="true">ptr.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer->weak</Item>
+
+      <!-- dyn -->
+      <Item Name="[Reference count]" Optional="true">ptr.pointer.pointer->strong</Item>
+      <Item Name="[Weak reference count]" Optional="true">ptr.pointer.pointer->weak</Item>
+    </Expand>
+  </Type>
+
+  <!-- alloc::sync::Weak<[T]> -->
+  <Type Name="alloc::sync::Weak&lt;slice$&lt;*&gt; &gt;">
+    <DisplayString>{{ len={ptr.pointer.length} }}</DisplayString>
+    <Expand>
+      <Item Name="[Length]" ExcludeView="simple">ptr.pointer.length</Item>
+      <Item Name="[Reference count]">ptr.pointer.data_ptr->strong</Item>
+      <Item Name="[Weak reference count]">ptr.pointer.data_ptr->weak</Item>
+      <ArrayItems>
+        <Size>ptr.pointer.length</Size>
+        <ValuePointer>($T1*)(((size_t*)ptr.pointer.data_ptr) + 2)</ValuePointer>
+      </ArrayItems>
     </Expand>
   </Type>
+
   <Type Name="alloc::borrow::Cow&lt;*&gt;">
     <DisplayString Condition="RUST$ENUM$DISR == 0x0">Borrowed({__0})</DisplayString>
     <DisplayString Condition="RUST$ENUM$DISR == 0x1">Owned({__0})</DisplayString>
diff --git a/src/test/debuginfo/rc_arc.rs b/src/test/debuginfo/rc_arc.rs
index 8470ace24b8..c05c565d956 100644
--- a/src/test/debuginfo/rc_arc.rs
+++ b/src/test/debuginfo/rc_arc.rs
@@ -8,74 +8,138 @@
 
 // gdb-command:run
 
-// gdb-command:print r
-// gdb-check:[...]$1 = Rc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}
-// gdb-command:print a
-// gdb-check:[...]$2 = Arc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}
-
+// gdb-command:print rc
+// gdb-check:[...]$1 = Rc(strong=11, weak=1) = {value = 111, strong = 11, weak = 1}
+// gdb-command:print arc
+// gdb-check:[...]$2 = Arc(strong=21, weak=1) = {value = 222, strong = 21, weak = 1}
 
 // === LLDB TESTS ==================================================================================
 
 // lldb-command:run
 
-// lldb-command:print r
-// lldb-check:[...]$0 = strong=2, weak=1 { value = 42 }
-// lldb-command:print a
-// lldb-check:[...]$1 = strong=2, weak=1 { data = 42 }
+// lldb-command:print rc
+// lldb-check:[...]$0 = strong=11, weak=1 { value = 111 }
+// lldb-command:print arc
+// lldb-check:[...]$1 = strong=21, weak=1 { data = 222 }
 
 // === CDB TESTS ==================================================================================
 
 // cdb-command:g
 
-// cdb-command:dx r,d
-// cdb-check:r,d              : 42 [Type: alloc::rc::Rc<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::rc::Rc<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-command:dx rc,d
+// cdb-check:rc,d             : 111 [Type: alloc::rc::Rc<i32>]
+// cdb-check:    [Reference count] : 11 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
+
+// cdb-command:dx weak_rc,d
+// cdb-check:weak_rc,d        : 111 [Type: alloc::rc::Weak<i32>]
+// cdb-check:    [Reference count] : 11 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
+
+// cdb-command:dx arc,d
+// cdb-check:arc,d            : 222 [Type: alloc::sync::Arc<i32>]
+// cdb-check:    [Reference count] : 21 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+
+// cdb-command:dx weak_arc,d
+// cdb-check:weak_arc,d       : 222 [Type: alloc::sync::Weak<i32>]
+// cdb-check:    [Reference count] : 21 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+
+// cdb-command:dx dyn_rc,d
+// cdb-check:dyn_rc,d         [Type: alloc::rc::Rc<dyn$<core::fmt::Debug> >]
+// cdb-check:    [Reference count] : 31 [Type: core::cell::Cell<usize>]
 // cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
 
-// cdb-command:dx r1,d
-// cdb-check:r1,d             : 42 [Type: alloc::rc::Rc<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::rc::Rc<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-command:dx dyn_rc_weak,d
+// cdb-check:dyn_rc_weak,d    [Type: alloc::rc::Weak<dyn$<core::fmt::Debug> >]
+// cdb-check:    [Reference count] : 31 [Type: core::cell::Cell<usize>]
 // cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
 
-// cdb-command:dx w1,d
-// cdb-check:w1,d             : 42 [Type: alloc::rc::Weak<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::rc::Weak<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-command:dx slice_rc,d
+// cdb-check:slice_rc,d       : { len=3 } [Type: alloc::rc::Rc<slice$<u32> >]
+// cdb-check:    [Length]         : 3 [Type: [...]]
+// cdb-check:    [Reference count] : 41 [Type: core::cell::Cell<usize>]
 // cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check:    [0]              : 1 [Type: u32]
+// cdb-check:    [1]              : 2 [Type: u32]
+// cdb-check:    [2]              : 3 [Type: u32]
+
+// cdb-command:dx slice_rc_weak,d
+// cdb-check:slice_rc_weak,d  : { len=3 } [Type: alloc::rc::Weak<slice$<u32> >]
+// cdb-check:    [Length]         : 3 [Type: [...]]
+// cdb-check:    [Reference count] : 41 [Type: core::cell::Cell<usize>]
+// cdb-check:    [Weak reference count] : 2 [Type: core::cell::Cell<usize>]
+// cdb-check:    [0]              : 1 [Type: u32]
+// cdb-check:    [1]              : 2 [Type: u32]
+// cdb-check:    [2]              : 3 [Type: u32]
 
-// cdb-command:dx a,d
-// cdb-check:a,d              : 42 [Type: alloc::sync::Arc<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::sync::Arc<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-command:dx dyn_arc,d
+// cdb-check:dyn_arc,d        [Type: alloc::sync::Arc<dyn$<core::fmt::Debug> >]
+// cdb-check:    [Reference count] : 51 [Type: core::sync::atomic::AtomicUsize]
 // cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
 
-// cdb-command:dx a1,d
-// cdb-check:a1,d             : 42 [Type: alloc::sync::Arc<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::sync::Arc<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-command:dx dyn_arc_weak,d
+// cdb-check:dyn_arc_weak,d   [Type: alloc::sync::Weak<dyn$<core::fmt::Debug> >]
+// cdb-check:    [Reference count] : 51 [Type: core::sync::atomic::AtomicUsize]
 // cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
 
-// cdb-command:dx w2,d
-// cdb-check:w2,d             : 42 [Type: alloc::sync::Weak<i32>]
-// cdb-check:    [<Raw View>]     [Type: alloc::sync::Weak<i32>]
-// cdb-check:    [Reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-command:dx slice_arc,d
+// cdb-check:slice_arc,d      : { len=3 } [Type: alloc::sync::Arc<slice$<u32> >]
+// cdb-check:    [Length]         : 3 [Type: [...]]
+// cdb-check:    [Reference count] : 61 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [0]              : 4 [Type: u32]
+// cdb-check:    [1]              : 5 [Type: u32]
+// cdb-check:    [2]              : 6 [Type: u32]
+
+// cdb-command:dx slice_arc_weak,d
+// cdb-check:slice_arc_weak,d : { len=3 } [Type: alloc::sync::Weak<slice$<u32> >]
+// cdb-check:    [Length]         : 3 [Type: [...]]
+// cdb-check:    [Reference count] : 61 [Type: core::sync::atomic::AtomicUsize]
 // cdb-check:    [Weak reference count] : 2 [Type: core::sync::atomic::AtomicUsize]
+// cdb-check:    [0]              : 4 [Type: u32]
+// cdb-check:    [1]              : 5 [Type: u32]
+// cdb-check:    [2]              : 6 [Type: u32]
 
+use std::fmt::Debug;
 use std::rc::Rc;
 use std::sync::Arc;
 
 fn main() {
-    let r = Rc::new(42);
-    let r1 = Rc::clone(&r);
-    let w1 = Rc::downgrade(&r);
+    let rc = Rc::new(111);
+    inc_ref_count(&rc, 10);
+    let weak_rc = Rc::downgrade(&rc);
+
+    let arc = Arc::new(222);
+    inc_ref_count(&arc, 20);
+    let weak_arc = Arc::downgrade(&arc);
+
+    let dyn_rc: Rc<dyn Debug> = Rc::new(333);
+    inc_ref_count(&dyn_rc, 30);
+    let dyn_rc_weak = Rc::downgrade(&dyn_rc);
+
+    let slice_rc: Rc<[u32]> = Rc::from(vec![1, 2, 3]);
+    inc_ref_count(&slice_rc, 40);
+    let slice_rc_weak = Rc::downgrade(&slice_rc);
 
-    let a = Arc::new(42);
-    let a1 = Arc::clone(&a);
-    let w2 = Arc::downgrade(&a);
+    let dyn_arc: Arc<dyn Debug> = Arc::new(444);
+    inc_ref_count(&dyn_arc, 50);
+    let dyn_arc_weak = Arc::downgrade(&dyn_arc);
+
+    let slice_arc: Arc<[u32]> = Arc::from(vec![4, 5, 6]);
+    inc_ref_count(&slice_arc, 60);
+    let slice_arc_weak = Arc::downgrade(&slice_arc);
 
     zzz(); // #break
 }
 
-fn zzz() { () }
+fn inc_ref_count<T: Clone>(rc: &T, count: usize) {
+    for _ in 0..count {
+        std::mem::forget(rc.clone());
+    }
+}
+
+fn zzz() {
+    ()
+}
diff --git a/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.rs b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.rs
new file mode 100644
index 00000000000..a3c3933d700
--- /dev/null
+++ b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.rs
@@ -0,0 +1,48 @@
+// compile-flags: --force-warn while_true
+// compile-flags: --force-warn unused_variables
+// compile-flags: --force-warn unused_mut
+// check-pass
+
+#![feature(lint_reasons)]
+
+fn expect_early_pass_lint() {
+    #[expect(while_true)]
+    while true {
+        //~^ WARNING denote infinite loops with `loop { ... }` [while_true]
+        //~| NOTE requested on the command line with `--force-warn while-true`
+        //~| HELP use `loop`
+        println!("I never stop")
+    }
+}
+
+#[expect(unused_variables, reason="<this should fail and display this reason>")]
+fn check_specific_lint() {
+    let x = 2;
+    //~^ WARNING unused variable: `x` [unused_variables]
+    //~| NOTE requested on the command line with `--force-warn unused-variables`
+    //~| HELP if this is intentional, prefix it with an underscore
+}
+
+#[expect(unused)]
+fn check_multiple_lints_with_lint_group() {
+    let fox_name = "Sir Nibbles";
+    //~^ WARNING unused variable: `fox_name` [unused_variables]
+    //~| HELP if this is intentional, prefix it with an underscore
+
+    let mut what_does_the_fox_say = "*ding* *deng* *dung*";
+    //~^ WARNING variable does not need to be mutable [unused_mut]
+    //~| NOTE requested on the command line with `--force-warn unused-mut`
+    //~| HELP remove this `mut`
+
+    println!("The fox says: {what_does_the_fox_say}");
+}
+
+#[allow(unused_variables)]
+fn check_expect_overrides_allow_lint_level() {
+    #[expect(unused_variables)]
+    let this_should_fulfill_the_expectation = "The `#[allow]` has no power here";
+    //~^ WARNING unused variable: `this_should_fulfill_the_expectation` [unused_variables]
+    //~| HELP if this is intentional, prefix it with an underscore
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr
new file mode 100644
index 00000000000..06befcbb511
--- /dev/null
+++ b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_fulfilled.stderr
@@ -0,0 +1,40 @@
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/force_warn_expected_lints_fulfilled.rs:10:5
+   |
+LL |     while true {
+   |     ^^^^^^^^^^ help: use `loop`
+   |
+   = note: requested on the command line with `--force-warn while-true`
+
+warning: unused variable: `x`
+  --> $DIR/force_warn_expected_lints_fulfilled.rs:20:9
+   |
+LL |     let x = 2;
+   |         ^ help: if this is intentional, prefix it with an underscore: `_x`
+   |
+   = note: requested on the command line with `--force-warn unused-variables`
+
+warning: unused variable: `fox_name`
+  --> $DIR/force_warn_expected_lints_fulfilled.rs:28:9
+   |
+LL |     let fox_name = "Sir Nibbles";
+   |         ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_fox_name`
+
+warning: unused variable: `this_should_fulfill_the_expectation`
+  --> $DIR/force_warn_expected_lints_fulfilled.rs:43:9
+   |
+LL |     let this_should_fulfill_the_expectation = "The `#[allow]` has no power here";
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_this_should_fulfill_the_expectation`
+
+warning: variable does not need to be mutable
+  --> $DIR/force_warn_expected_lints_fulfilled.rs:32:9
+   |
+LL |     let mut what_does_the_fox_say = "*ding* *deng* *dung*";
+   |         ----^^^^^^^^^^^^^^^^^^^^^
+   |         |
+   |         help: remove this `mut`
+   |
+   = note: requested on the command line with `--force-warn unused-mut`
+
+warning: 5 warnings emitted
+
diff --git a/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.rs b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.rs
new file mode 100644
index 00000000000..080e300232b
--- /dev/null
+++ b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.rs
@@ -0,0 +1,49 @@
+// compile-flags: --force-warn while_true
+// compile-flags: --force-warn unused_variables
+// compile-flags: --force-warn unused_mut
+// check-pass
+
+#![feature(lint_reasons)]
+
+fn expect_early_pass_lint(terminate: bool) {
+    #[expect(while_true)]
+    //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
+    //~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default
+    while !terminate {
+        println!("Do you know what a spin lock is?")
+    }
+}
+
+#[expect(unused_variables, reason="<this should fail and display this reason>")]
+//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
+//~| NOTE <this should fail and display this reason>
+fn check_specific_lint() {
+    let _x = 2;
+}
+
+#[expect(unused)]
+//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
+fn check_multiple_lints_with_lint_group() {
+    let fox_name = "Sir Nibbles";
+
+    let what_does_the_fox_say = "*ding* *deng* *dung*";
+
+    println!("The fox says: {what_does_the_fox_say}");
+    println!("~ {fox_name}")
+}
+
+
+#[expect(unused)]
+//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations]
+fn check_overridden_expectation_lint_level() {
+    #[allow(unused_variables)]
+    let this_should_not_fulfill_the_expectation = "maybe";
+    //~^ WARNING unused variable: `this_should_not_fulfill_the_expectation` [unused_variables]
+    //~| NOTE requested on the command line with `--force-warn unused-variables`
+    //~| HELP if this is intentional, prefix it with an underscore
+}
+
+fn main() {
+    check_multiple_lints_with_lint_group();
+    check_overridden_expectation_lint_level();
+}
diff --git a/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.stderr b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.stderr
new file mode 100644
index 00000000000..c74fabe27dc
--- /dev/null
+++ b/src/test/ui/lint/rfc-2383-lint-reason/force_warn_expected_lints_unfulfilled.stderr
@@ -0,0 +1,38 @@
+warning: unused variable: `this_should_not_fulfill_the_expectation`
+  --> $DIR/force_warn_expected_lints_unfulfilled.rs:40:9
+   |
+LL |     let this_should_not_fulfill_the_expectation = "maybe";
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_this_should_not_fulfill_the_expectation`
+   |
+   = note: requested on the command line with `--force-warn unused-variables`
+
+warning: this lint expectation is unfulfilled
+  --> $DIR/force_warn_expected_lints_unfulfilled.rs:9:14
+   |
+LL |     #[expect(while_true)]
+   |              ^^^^^^^^^^
+   |
+   = note: `#[warn(unfulfilled_lint_expectations)]` on by default
+
+warning: this lint expectation is unfulfilled
+  --> $DIR/force_warn_expected_lints_unfulfilled.rs:17:10
+   |
+LL | #[expect(unused_variables, reason="<this should fail and display this reason>")]
+   |          ^^^^^^^^^^^^^^^^
+   |
+   = note: <this should fail and display this reason>
+
+warning: this lint expectation is unfulfilled
+  --> $DIR/force_warn_expected_lints_unfulfilled.rs:24:10
+   |
+LL | #[expect(unused)]
+   |          ^^^^^^
+
+warning: this lint expectation is unfulfilled
+  --> $DIR/force_warn_expected_lints_unfulfilled.rs:36:10
+   |
+LL | #[expect(unused)]
+   |          ^^^^^^
+
+warning: 5 warnings emitted
+
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index a0f71c2b8ba..23db5421910 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -433,7 +433,7 @@ mod tests {
                 Some(ignore_list),
             );
             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
+            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
             emitter.emit_diagnostic(&non_fatal_diagnostic);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
@@ -457,7 +457,7 @@ mod tests {
                 None,
             );
             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
-            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
+            let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(span));
             emitter.emit_diagnostic(&non_fatal_diagnostic);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
@@ -494,8 +494,8 @@ mod tests {
             );
             let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
             let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
-            let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
-            let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
+            let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(bar_span));
+            let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning(None), Some(foo_span));
             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
             emitter.emit_diagnostic(&bar_diagnostic);
             emitter.emit_diagnostic(&foo_diagnostic);