diff --git a/Cargo.lock b/Cargo.lock
index 65d20190c0db5..a8e4490aa1a45 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3638,6 +3638,7 @@ dependencies = [
  "annotate-snippets 0.8.0",
  "atty",
  "rustc_data_structures",
+ "rustc_lint_defs",
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
@@ -3830,6 +3831,18 @@ dependencies = [
  "unicode-security",
 ]
 
+[[package]]
+name = "rustc_lint_defs"
+version = "0.0.0"
+dependencies = [
+ "rustc_ast",
+ "rustc_data_structures",
+ "rustc_macros",
+ "rustc_serialize",
+ "rustc_span",
+ "tracing",
+]
+
 [[package]]
 name = "rustc_llvm"
 version = "0.0.0"
@@ -4112,6 +4125,7 @@ dependencies = [
  "rustc_errors",
  "rustc_feature",
  "rustc_fs_util",
+ "rustc_lint_defs",
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index e1eed168b31bb..6afed355dc338 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -308,8 +308,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             E0726,
                             "implicit elided lifetime not allowed here"
                         );
-                        rustc_session::lint::add_elided_lifetime_in_path_suggestion(
-                            &self.sess,
+                        rustc_errors::add_elided_lifetime_in_path_suggestion(
+                            &self.sess.source_map(),
                             &mut err,
                             expected_lifetimes,
                             path_span,
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index e4dbb8db38104..5d8ff601e7993 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -13,6 +13,7 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_lint_defs = { path = "../rustc_lint_defs" }
 unicode-width = "0.1.4"
 atty = "0.2"
 termcolor = "1.0"
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 265ba59cccb2a..6f365c07f6d30 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -72,6 +72,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
         Level::Help => AnnotationType::Help,
         // FIXME(#59346): Not sure how to map these two levels
         Level::Cancelled | Level::FailureNote => AnnotationType::Error,
+        Level::Allow => panic!("Should not call with Allow"),
     }
 }
 
@@ -143,7 +144,8 @@ impl AnnotateSnippetEmitterWriter {
                 title: Some(Annotation {
                     label: Some(&message),
                     id: code.as_ref().map(|c| match c {
-                        DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val.as_str(),
+                        DiagnosticId::Error(val)
+                        | DiagnosticId::Lint { name: val, has_future_breakage: _ } => val.as_str(),
                     }),
                     annotation_type: annotation_type_for_level(*level),
                 }),
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 91bfc6296b1c7..decbf03b9de85 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -1,10 +1,10 @@
 use crate::snippet::Style;
-use crate::Applicability;
 use crate::CodeSuggestion;
 use crate::Level;
 use crate::Substitution;
 use crate::SubstitutionPart;
 use crate::SuggestionStyle;
+use rustc_lint_defs::Applicability;
 use rustc_span::{MultiSpan, Span, DUMMY_SP};
 use std::fmt;
 
@@ -27,7 +27,7 @@ pub struct Diagnostic {
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
 pub enum DiagnosticId {
     Error(String),
-    Lint(String),
+    Lint { name: String, has_future_breakage: bool },
 }
 
 /// For example a note attached to an error.
@@ -107,7 +107,14 @@ impl Diagnostic {
         match self.level {
             Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
 
-            Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
+            Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false,
+        }
+    }
+
+    pub fn has_future_breakage(&self) -> bool {
+        match self.code {
+            Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
+            _ => false,
         }
     }
 
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index d1ff6f721c415..56acdf699ef45 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,5 +1,6 @@
-use crate::{Applicability, Handler, Level, StashKey};
 use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
+use crate::{Handler, Level, StashKey};
+use rustc_lint_defs::Applicability;
 
 use rustc_span::{MultiSpan, Span};
 use std::fmt::{self, Debug};
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 08e9bdf308798..2dc7b7e2da383 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -9,14 +9,15 @@
 
 use Destination::*;
 
+use rustc_lint_defs::FutureBreakage;
 use rustc_span::source_map::SourceMap;
 use rustc_span::{MultiSpan, SourceFile, Span};
 
 use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
 use crate::styled_buffer::StyledBuffer;
-use crate::{
-    pluralize, CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle,
-};
+use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle};
+
+use rustc_lint_defs::pluralize;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -192,6 +193,8 @@ pub trait Emitter {
     /// other formats can, and will, simply ignore it.
     fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
 
+    fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {}
+
     /// Checks if should show explanations about "rustc --explain"
     fn should_show_explain(&self) -> bool {
         true
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index fbe3588280ad1..d57beb1148a25 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -13,8 +13,9 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
 
 use crate::emitter::{Emitter, HumanReadableErrorType};
 use crate::registry::Registry;
-use crate::{Applicability, DiagnosticId};
+use crate::DiagnosticId;
 use crate::{CodeSuggestion, SubDiagnostic};
+use rustc_lint_defs::{Applicability, FutureBreakage};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_span::hygiene::ExpnData;
@@ -131,6 +132,31 @@ impl Emitter for JsonEmitter {
         }
     }
 
+    fn emit_future_breakage_report(&mut self, diags: Vec<(FutureBreakage, crate::Diagnostic)>) {
+        let data: Vec<FutureBreakageItem> = diags
+            .into_iter()
+            .map(|(breakage, mut diag)| {
+                if diag.level == crate::Level::Allow {
+                    diag.level = crate::Level::Warning;
+                }
+                FutureBreakageItem {
+                    future_breakage_date: breakage.date,
+                    diagnostic: Diagnostic::from_errors_diagnostic(&diag, self),
+                }
+            })
+            .collect();
+        let report = FutureIncompatReport { future_incompat_report: data };
+        let result = if self.pretty {
+            writeln!(&mut self.dst, "{}", as_pretty_json(&report))
+        } else {
+            writeln!(&mut self.dst, "{}", as_json(&report))
+        }
+        .and_then(|_| self.dst.flush());
+        if let Err(e) = result {
+            panic!("failed to print future breakage report: {:?}", e);
+        }
+    }
+
     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
         Some(&self.sm)
     }
@@ -223,6 +249,17 @@ struct ArtifactNotification<'a> {
     emit: &'a str,
 }
 
+#[derive(Encodable)]
+struct FutureBreakageItem {
+    future_breakage_date: Option<&'static str>,
+    diagnostic: Diagnostic,
+}
+
+#[derive(Encodable)]
+struct FutureIncompatReport {
+    future_incompat_report: Vec<FutureBreakageItem>,
+}
+
 impl Diagnostic {
     fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
         let sugg = diag.suggestions.iter().map(|sugg| Diagnostic {
@@ -432,7 +469,7 @@ impl DiagnosticCode {
         s.map(|s| {
             let s = match s {
                 DiagnosticId::Error(s) => s,
-                DiagnosticId::Lint(s) => s,
+                DiagnosticId::Lint { name, has_future_breakage: _ } => name,
             };
             let je_result =
                 je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap();
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 2ebc4a7e9d923..593e0d92031ff 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -21,6 +21,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{self, Lock, Lrc};
 use rustc_data_structures::AtomicRef;
+use rustc_lint_defs::FutureBreakage;
+pub use rustc_lint_defs::{pluralize, Applicability};
 use rustc_span::source_map::SourceMap;
 use rustc_span::{Loc, MultiSpan, Span};
 
@@ -49,30 +51,6 @@ pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
 #[cfg(target_arch = "x86_64")]
 rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16);
 
-/// Indicates the confidence in the correctness of a suggestion.
-///
-/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
-/// to determine whether it should be automatically applied or if the user should be consulted
-/// before applying the suggestion.
-#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
-pub enum Applicability {
-    /// The suggestion is definitely what the user intended. This suggestion should be
-    /// automatically applied.
-    MachineApplicable,
-
-    /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
-    /// result in valid Rust code if it is applied.
-    MaybeIncorrect,
-
-    /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
-    /// cannot be applied automatically because it will not result in valid Rust code. The user
-    /// will need to fill in the placeholders.
-    HasPlaceholders,
-
-    /// The applicability of the suggestion is unknown.
-    Unspecified,
-}
-
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
 pub enum SuggestionStyle {
     /// Hide the suggested code when displaying this suggestion inline.
@@ -321,6 +299,8 @@ struct HandlerInner {
 
     /// The warning count, used for a recap upon finishing
     deduplicated_warn_count: usize,
+
+    future_breakage_diagnostics: Vec<Diagnostic>,
 }
 
 /// A key denoting where from a diagnostic was stashed.
@@ -434,6 +414,7 @@ impl Handler {
                 emitted_diagnostic_codes: Default::default(),
                 emitted_diagnostics: Default::default(),
                 stashed_diagnostics: Default::default(),
+                future_breakage_diagnostics: Vec::new(),
             }),
         }
     }
@@ -503,6 +484,17 @@ impl Handler {
         result
     }
 
+    /// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
+    pub fn struct_span_allow(
+        &self,
+        span: impl Into<MultiSpan>,
+        msg: &str,
+    ) -> DiagnosticBuilder<'_> {
+        let mut result = self.struct_allow(msg);
+        result.set_span(span);
+        result
+    }
+
     /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
     /// Also include a code.
     pub fn struct_span_warn_with_code(
@@ -525,6 +517,11 @@ impl Handler {
         result
     }
 
+    /// Construct a builder at the `Allow` level with the `msg`.
+    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
+        DiagnosticBuilder::new(self, Level::Allow, msg)
+    }
+
     /// Construct a builder at the `Error` level at the given `span` and with the `msg`.
     pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
         let mut result = self.struct_err(msg);
@@ -693,6 +690,10 @@ impl Handler {
         self.inner.borrow_mut().print_error_count(registry)
     }
 
+    pub fn take_future_breakage_diagnostics(&self) -> Vec<Diagnostic> {
+        std::mem::take(&mut self.inner.borrow_mut().future_breakage_diagnostics)
+    }
+
     pub fn abort_if_errors(&self) {
         self.inner.borrow_mut().abort_if_errors()
     }
@@ -723,6 +724,10 @@ impl Handler {
         self.inner.borrow_mut().emit_artifact_notification(path, artifact_type)
     }
 
+    pub fn emit_future_breakage_report(&self, diags: Vec<(FutureBreakage, Diagnostic)>) {
+        self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
+    }
+
     pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
         self.inner.borrow_mut().delay_as_bug(diagnostic)
     }
@@ -748,12 +753,23 @@ impl HandlerInner {
             return;
         }
 
+        if diagnostic.has_future_breakage() {
+            self.future_breakage_diagnostics.push(diagnostic.clone());
+        }
+
         if diagnostic.level == Warning && !self.flags.can_emit_warnings {
+            if diagnostic.has_future_breakage() {
+                (*TRACK_DIAGNOSTICS)(diagnostic);
+            }
             return;
         }
 
         (*TRACK_DIAGNOSTICS)(diagnostic);
 
+        if diagnostic.level == Allow {
+            return;
+        }
+
         if let Some(ref code) = diagnostic.code {
             self.emitted_diagnostic_codes.insert(code.clone());
         }
@@ -992,6 +1008,7 @@ pub enum Level {
     Help,
     Cancelled,
     FailureNote,
+    Allow,
 }
 
 impl fmt::Display for Level {
@@ -1017,7 +1034,7 @@ impl Level {
                 spec.set_fg(Some(Color::Cyan)).set_intense(true);
             }
             FailureNote => {}
-            Cancelled => unreachable!(),
+            Allow | Cancelled => unreachable!(),
         }
         spec
     }
@@ -1031,6 +1048,7 @@ impl Level {
             Help => "help",
             FailureNote => "failure-note",
             Cancelled => panic!("Shouldn't call on cancelled error"),
+            Allow => panic!("Shouldn't call on allowed error"),
         }
     }
 
@@ -1039,11 +1057,46 @@ impl Level {
     }
 }
 
-#[macro_export]
-macro_rules! pluralize {
-    ($x:expr) => {
-        if $x != 1 { "s" } else { "" }
+pub fn add_elided_lifetime_in_path_suggestion(
+    source_map: &SourceMap,
+    db: &mut DiagnosticBuilder<'_>,
+    n: usize,
+    path_span: Span,
+    incl_angl_brckt: bool,
+    insertion_span: Span,
+    anon_lts: String,
+) {
+    let (replace_span, suggestion) = if incl_angl_brckt {
+        (insertion_span, anon_lts)
+    } else {
+        // When possible, prefer a suggestion that replaces the whole
+        // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
+        // at a point (which makes for an ugly/confusing label)
+        if let Ok(snippet) = source_map.span_to_snippet(path_span) {
+            // But our spans can get out of whack due to macros; if the place we think
+            // we want to insert `'_` isn't even within the path expression's span, we
+            // should bail out of making any suggestion rather than panicking on a
+            // subtract-with-overflow or string-slice-out-out-bounds (!)
+            // FIXME: can we do better?
+            if insertion_span.lo().0 < path_span.lo().0 {
+                return;
+            }
+            let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
+            if insertion_index > snippet.len() {
+                return;
+            }
+            let (before, after) = snippet.split_at(insertion_index);
+            (path_span, format!("{}{}{}", before, anon_lts, after))
+        } else {
+            (insertion_span, anon_lts)
+        }
     };
+    db.span_suggestion(
+        replace_span,
+        &format!("indicate the anonymous lifetime{}", pluralize!(n)),
+        suggestion,
+        Applicability::MachineApplicable,
+    );
 }
 
 // Useful type to use with `Result<>` indicate that an error has already
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index a1487aa0060d5..072194e332a5b 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -286,7 +286,10 @@ pub fn register_plugins<'a>(
         }
     });
 
-    Ok((krate, Lrc::new(lint_store)))
+    let lint_store = Lrc::new(lint_store);
+    sess.init_lint_store(lint_store.clone());
+
+    Ok((krate, lint_store))
 }
 
 fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 235e049c3f566..9332554eede33 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -477,6 +477,7 @@ fn test_debugging_options_tracking_hash() {
     untracked!(dump_mir_dir, String::from("abc"));
     untracked!(dump_mir_exclude_pass_number, true);
     untracked!(dump_mir_graphviz, true);
+    untracked!(emit_future_incompat_report, true);
     untracked!(emit_stack_sizes, true);
     untracked!(hir_stats, true);
     untracked!(identify_regions, true);
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index e6be082da0e99..0b5bd39f7f984 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -3,7 +3,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_session::lint::FutureIncompatibleInfo;
+use rustc_session::lint::FutureBreakage;
 use rustc_span::symbol::sym;
 
 declare_lint! {
@@ -38,6 +38,9 @@ declare_lint! {
     @future_incompatible = FutureIncompatibleInfo {
         reference: "issue #66145 <https://github.com/rust-lang/rust/issues/66145>",
         edition: None,
+        future_breakage: Some(FutureBreakage {
+            date: None
+        })
     };
 }
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 7e029aa7a1928..c65cf65b1c777 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -44,7 +44,6 @@ use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::{GenericArgKind, Subst};
 use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
-use rustc_session::lint::FutureIncompatibleInfo;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 48270eb59a050..4cfeb0d968b95 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -22,7 +22,7 @@ use rustc_ast as ast;
 use rustc_ast::util::lev_distance::find_best_match_for_name;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId};
@@ -33,9 +33,10 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
-use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics};
+use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::Session;
+use rustc_session::SessionLintStore;
 use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
 use rustc_target::abi::LayoutOf;
 
@@ -69,6 +70,20 @@ pub struct LintStore {
     lint_groups: FxHashMap<&'static str, LintGroup>,
 }
 
+impl SessionLintStore for LintStore {
+    fn name_to_lint(&self, lint_name: &str) -> LintId {
+        let lints = self
+            .find_lints(lint_name)
+            .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name));
+
+        if let &[lint] = lints.as_slice() {
+            return lint;
+        } else {
+            panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints);
+        }
+    }
+}
+
 /// The target of the `by_name` map, which accounts for renaming/deprecation.
 enum TargetLint {
     /// A direct lint target
@@ -543,7 +558,7 @@ pub trait LintContext: Sized {
                     anon_lts,
                 ) => {
                     add_elided_lifetime_in_path_suggestion(
-                        sess,
+                        sess.source_map(),
                         &mut db,
                         n,
                         path_span,
diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml
new file mode 100644
index 0000000000000..7f908088cf5b0
--- /dev/null
+++ b/compiler/rustc_lint_defs/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_lint_defs"
+version = "0.0.0"
+edition = "2018"
+
+[dependencies]
+log = { package = "tracing", version = "0.1" }
+rustc_ast = { path = "../rustc_ast" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_span = { path = "../rustc_span" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
similarity index 99%
rename from compiler/rustc_session/src/lint/builtin.rs
rename to compiler/rustc_lint_defs/src/builtin.rs
index b8826a548b8be..048f096aabe13 100644
--- a/compiler/rustc_session/src/lint/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -4,7 +4,6 @@
 //! compiler code, rather than using their own custom pass. Those
 //! lints are all available in `rustc_lint::builtin`.
 
-use crate::lint::FutureIncompatibleInfo;
 use crate::{declare_lint, declare_lint_pass, declare_tool_lint};
 use rustc_span::edition::Edition;
 use rustc_span::symbol::sym;
diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_lint_defs/src/lib.rs
similarity index 83%
rename from compiler/rustc_session/src/lint.rs
rename to compiler/rustc_lint_defs/src/lib.rs
index 62e021d5e45bd..25a7bfcabb728 100644
--- a/compiler/rustc_session/src/lint.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -1,12 +1,45 @@
+#[macro_use]
+extern crate rustc_macros;
+
 pub use self::Level::*;
 use rustc_ast::node_id::{NodeId, NodeMap};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
-use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
 use rustc_span::edition::Edition;
 use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
 
 pub mod builtin;
 
+#[macro_export]
+macro_rules! pluralize {
+    ($x:expr) => {
+        if $x != 1 { "s" } else { "" }
+    };
+}
+
+/// Indicates the confidence in the correctness of a suggestion.
+///
+/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
+/// to determine whether it should be automatically applied or if the user should be consulted
+/// before applying the suggestion.
+#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
+pub enum Applicability {
+    /// The suggestion is definitely what the user intended. This suggestion should be
+    /// automatically applied.
+    MachineApplicable,
+
+    /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
+    /// result in valid Rust code if it is applied.
+    MaybeIncorrect,
+
+    /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
+    /// cannot be applied automatically because it will not result in valid Rust code. The user
+    /// will need to fill in the placeholders.
+    HasPlaceholders,
+
+    /// The applicability of the suggestion is unknown.
+    Unspecified,
+}
+
 /// Setting for how to handle a lint.
 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
 pub enum Level {
@@ -106,6 +139,21 @@ pub struct FutureIncompatibleInfo {
     /// If this is an edition fixing lint, the edition in which
     /// this lint becomes obsolete
     pub edition: Option<Edition>,
+    /// Information about a future breakage, which will
+    /// be emitted in JSON messages to be displayed by Cargo
+    /// for upstream deps
+    pub future_breakage: Option<FutureBreakage>,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct FutureBreakage {
+    pub date: Option<&'static str>,
+}
+
+impl FutureIncompatibleInfo {
+    pub const fn default_fields_for_macro() -> Self {
+        FutureIncompatibleInfo { reference: "", edition: None, future_breakage: None }
+    }
 }
 
 impl Lint {
@@ -331,31 +379,34 @@ macro_rules! declare_lint {
         );
     );
     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
-     $(@future_incompatible = $fi:expr;)?
      $(@feature_gate = $gate:expr;)?
+     $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)*  }; )?
      $($v:ident),*) => (
         $(#[$attr])*
-        $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint {
+        $vis static $NAME: &$crate::Lint = &$crate::Lint {
             name: stringify!($NAME),
-            default_level: $crate::lint::$Level,
+            default_level: $crate::$Level,
             desc: $desc,
             edition_lint_opts: None,
             is_plugin: false,
             $($v: true,)*
-            $(future_incompatible: Some($fi),)*
             $(feature_gate: Some($gate),)*
-            ..$crate::lint::Lint::default_fields_for_macro()
+            $(future_incompatible: Some($crate::FutureIncompatibleInfo {
+                $($field: $val,)*
+                ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
+            }),)*
+            ..$crate::Lint::default_fields_for_macro()
         };
     );
     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
      $lint_edition: expr => $edition_level: ident
     ) => (
         $(#[$attr])*
-        $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint {
+        $vis static $NAME: &$crate::Lint = &$crate::Lint {
             name: stringify!($NAME),
-            default_level: $crate::lint::$Level,
+            default_level: $crate::$Level,
             desc: $desc,
-            edition_lint_opts: Some(($lint_edition, $crate::lint::Level::$edition_level)),
+            edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
             report_in_external_macro: false,
             is_plugin: false,
         };
@@ -380,9 +431,9 @@ macro_rules! declare_tool_lint {
         $external:expr
     ) => (
         $(#[$attr])*
-        $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint {
+        $vis static $NAME: &$crate::Lint = &$crate::Lint {
             name: &concat!(stringify!($tool), "::", stringify!($NAME)),
-            default_level: $crate::lint::$Level,
+            default_level: $crate::$Level,
             desc: $desc,
             edition_lint_opts: None,
             report_in_external_macro: $external,
@@ -413,11 +464,11 @@ pub trait LintPass {
 #[macro_export]
 macro_rules! impl_lint_pass {
     ($ty:ty => [$($lint:expr),* $(,)?]) => {
-        impl $crate::lint::LintPass for $ty {
+        impl $crate::LintPass for $ty {
             fn name(&self) -> &'static str { stringify!($ty) }
         }
         impl $ty {
-            pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) }
+            pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
         }
     };
 }
@@ -431,45 +482,3 @@ macro_rules! declare_lint_pass {
         $crate::impl_lint_pass!($name => [$($lint),*]);
     };
 }
-
-pub fn add_elided_lifetime_in_path_suggestion(
-    sess: &crate::Session,
-    db: &mut DiagnosticBuilder<'_>,
-    n: usize,
-    path_span: Span,
-    incl_angl_brckt: bool,
-    insertion_span: Span,
-    anon_lts: String,
-) {
-    let (replace_span, suggestion) = if incl_angl_brckt {
-        (insertion_span, anon_lts)
-    } else {
-        // When possible, prefer a suggestion that replaces the whole
-        // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
-        // at a point (which makes for an ugly/confusing label)
-        if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
-            // But our spans can get out of whack due to macros; if the place we think
-            // we want to insert `'_` isn't even within the path expression's span, we
-            // should bail out of making any suggestion rather than panicking on a
-            // subtract-with-overflow or string-slice-out-out-bounds (!)
-            // FIXME: can we do better?
-            if insertion_span.lo().0 < path_span.lo().0 {
-                return;
-            }
-            let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
-            if insertion_index > snippet.len() {
-                return;
-            }
-            let (before, after) = snippet.split_at(insertion_index);
-            (path_span, format!("{}{}{}", before, anon_lts, after))
-        } else {
-            (insertion_span, anon_lts)
-        }
-    };
-    db.span_suggestion(
-        replace_span,
-        &format!("indicate the anonymous lifetime{}", pluralize!(n)),
-        suggestion,
-        Applicability::MachineApplicable,
-    );
-}
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 91e1d6e0b0b72..6e67f0d828c05 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -225,9 +225,24 @@ pub fn struct_lint_level<'s, 'd>(
         span: Option<MultiSpan>,
         decorate: Box<dyn for<'b> FnOnce(LintDiagnosticBuilder<'b>) + 'd>,
     ) {
+        // Check for future incompatibility lints and issue a stronger warning.
+        let lint_id = LintId::of(lint);
+        let future_incompatible = lint.future_incompatible;
+
+        let has_future_breakage =
+            future_incompatible.map_or(false, |incompat| incompat.future_breakage.is_some());
+
         let mut err = match (level, span) {
-            (Level::Allow, _) => {
-                return;
+            (Level::Allow, span) => {
+                if has_future_breakage {
+                    if let Some(span) = span {
+                        sess.struct_span_allow(span, "")
+                    } else {
+                        sess.struct_allow("")
+                    }
+                } else {
+                    return;
+                }
             }
             (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
             (Level::Warn, None) => sess.struct_warn(""),
@@ -235,10 +250,6 @@ pub fn struct_lint_level<'s, 'd>(
             (Level::Deny | Level::Forbid, None) => sess.struct_err(""),
         };
 
-        // Check for future incompatibility lints and issue a stronger warning.
-        let lint_id = LintId::of(lint);
-        let future_incompatible = lint.future_incompatible;
-
         // If this code originates in a foreign macro, aka something that this crate
         // did not itself author, then it's likely that there's nothing this crate
         // can do about it. We probably want to skip the lint entirely.
@@ -321,7 +332,7 @@ pub fn struct_lint_level<'s, 'd>(
             }
         }
 
-        err.code(DiagnosticId::Lint(name));
+        err.code(DiagnosticId::Lint { name, has_future_breakage });
 
         if let Some(future_incompatible) = future_incompatible {
             const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index cdff1662fdb0e..4c72920502f5f 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -18,3 +18,4 @@ rustc_span = { path = "../rustc_span" }
 rustc_fs_util = { path = "../rustc_fs_util" }
 num_cpus = "1.0"
 rustc_ast = { path = "../rustc_ast" }
+rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index a808261798da7..d002f59739166 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -9,8 +9,8 @@ extern crate rustc_macros;
 
 pub mod cgu_reuse_tracker;
 pub mod utils;
-#[macro_use]
-pub mod lint;
+pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
+pub use rustc_lint_defs as lint;
 pub mod parse;
 
 mod code_stats;
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 750f2e19ee257..578caf2192d5f 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -893,6 +893,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         all `statement`s (including terminators), only `terminator` spans, or \
         computed `block` spans (one span encompassing a block's terminator and \
         all statements)."),
+    emit_future_incompat_report: bool = (false, parse_bool, [UNTRACKED],
+        "emits a future-incompatibility report for lints (RFC 2834)"),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
     fewer_names: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 8312f89b2714c..0b7c35a8afd50 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -3,7 +3,7 @@ use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
 use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath};
 use crate::filesearch;
-use crate::lint;
+use crate::lint::{self, LintId};
 use crate::parse::ParseSess;
 use crate::search_paths::{PathKind, SearchPath};
 
@@ -21,7 +21,8 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
 use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported};
+use rustc_lint_defs::FutureBreakage;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
 use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
@@ -40,6 +41,10 @@ use std::str::FromStr;
 use std::sync::Arc;
 use std::time::Duration;
 
+pub trait SessionLintStore: sync::Send + sync::Sync {
+    fn name_to_lint(&self, lint_name: &str) -> LintId;
+}
+
 pub struct OptimizationFuel {
     /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
     remaining: u64,
@@ -131,6 +136,8 @@ pub struct Session {
 
     features: OnceCell<rustc_feature::Features>,
 
+    lint_store: OnceCell<Lrc<dyn SessionLintStore>>,
+
     /// The maximum recursion limit for potentially infinitely recursive
     /// operations such as auto-dereference and monomorphization.
     pub recursion_limit: OnceCell<Limit>,
@@ -297,6 +304,35 @@ impl Session {
     pub fn finish_diagnostics(&self, registry: &Registry) {
         self.check_miri_unleashed_features();
         self.diagnostic().print_error_count(registry);
+        self.emit_future_breakage();
+    }
+
+    fn emit_future_breakage(&self) {
+        if !self.opts.debugging_opts.emit_future_incompat_report {
+            return;
+        }
+
+        let diags = self.diagnostic().take_future_breakage_diagnostics();
+        if diags.is_empty() {
+            return;
+        }
+        // If any future-breakage lints were registered, this lint store
+        // should be available
+        let lint_store = self.lint_store.get().expect("`lint_store` not initialized!");
+        let diags_and_breakage: Vec<(FutureBreakage, Diagnostic)> = diags
+            .into_iter()
+            .map(|diag| {
+                let lint_name = match &diag.code {
+                    Some(DiagnosticId::Lint { name, has_future_breakage: true }) => name,
+                    _ => panic!("Unexpected code in diagnostic {:?}", diag),
+                };
+                let lint = lint_store.name_to_lint(&lint_name);
+                let future_breakage =
+                    lint.lint.future_incompatible.unwrap().future_breakage.unwrap();
+                (future_breakage, diag)
+            })
+            .collect();
+        self.parse_sess.span_diagnostic.emit_future_breakage_report(diags_and_breakage);
     }
 
     pub fn local_crate_disambiguator(&self) -> CrateDisambiguator {
@@ -337,6 +373,12 @@ impl Session {
     pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
         self.diagnostic().struct_warn(msg)
     }
+    pub fn struct_span_allow<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
+        self.diagnostic().struct_span_allow(sp, msg)
+    }
+    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
+        self.diagnostic().struct_allow(msg)
+    }
     pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
         self.diagnostic().struct_span_err(sp, msg)
     }
@@ -611,6 +653,13 @@ impl Session {
         }
     }
 
+    pub fn init_lint_store(&self, lint_store: Lrc<dyn SessionLintStore>) {
+        self.lint_store
+            .set(lint_store)
+            .map_err(|_| ())
+            .expect("`lint_store` was initialized twice");
+    }
+
     /// Calculates the flavor of LTO to use for this compilation.
     pub fn lto(&self) -> config::Lto {
         // If our target has codegen requirements ignore the command line
@@ -1388,6 +1437,7 @@ pub fn build_session(
         crate_types: OnceCell::new(),
         crate_disambiguator: OnceCell::new(),
         features: OnceCell::new(),
+        lint_store: OnceCell::new(),
         recursion_limit: OnceCell::new(),
         type_length_limit: OnceCell::new(),
         const_eval_limit: OnceCell::new(),
diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.fixed b/src/test/ui/iterators/into-iter-on-arrays-lint.fixed
index 561376c8f059d..7f511bde3cbfc 100644
--- a/src/test/ui/iterators/into-iter-on-arrays-lint.fixed
+++ b/src/test/ui/iterators/into-iter-on-arrays-lint.fixed
@@ -55,4 +55,7 @@ fn main() {
     (&small as &[_]).into_iter();
     small[..].into_iter();
     std::iter::IntoIterator::into_iter(&[1, 2]);
+
+    #[allow(array_into_iter)]
+    [0, 1].into_iter();
 }
diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.rs b/src/test/ui/iterators/into-iter-on-arrays-lint.rs
index cc310191f0caf..d5fe83a7834b6 100644
--- a/src/test/ui/iterators/into-iter-on-arrays-lint.rs
+++ b/src/test/ui/iterators/into-iter-on-arrays-lint.rs
@@ -55,4 +55,7 @@ fn main() {
     (&small as &[_]).into_iter();
     small[..].into_iter();
     std::iter::IntoIterator::into_iter(&[1, 2]);
+
+    #[allow(array_into_iter)]
+    [0, 1].into_iter();
 }
diff --git a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr
index b31f444b36e99..211315c3fcf05 100644
--- a/src/test/ui/iterators/into-iter-on-arrays-lint.stderr
+++ b/src/test/ui/iterators/into-iter-on-arrays-lint.stderr
@@ -109,3 +109,139 @@ LL |     Box::new(Box::new([0u8; 33])).into_iter();
 
 warning: 12 warnings emitted
 
+Future incompatibility report: Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:9:11
+   |
+LL |     small.into_iter();
+   |           ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = note: `#[warn(array_into_iter)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:12:12
+   |
+LL |     [1, 2].into_iter();
+   |            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:15:9
+   |
+LL |     big.into_iter();
+   |         ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:18:15
+   |
+LL |     [0u8; 33].into_iter();
+   |               ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:22:21
+   |
+LL |     Box::new(small).into_iter();
+   |                     ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:25:22
+   |
+LL |     Box::new([1, 2]).into_iter();
+   |                      ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:28:19
+   |
+LL |     Box::new(big).into_iter();
+   |                   ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:31:25
+   |
+LL |     Box::new([0u8; 33]).into_iter();
+   |                         ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:35:31
+   |
+LL |     Box::new(Box::new(small)).into_iter();
+   |                               ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:38:32
+   |
+LL |     Box::new(Box::new([1, 2])).into_iter();
+   |                                ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:41:29
+   |
+LL |     Box::new(Box::new(big)).into_iter();
+   |                             ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:44:35
+   |
+LL |     Box::new(Box::new([0u8; 33])).into_iter();
+   |                                   ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
+Future breakage date: None, diagnostic:
+warning: this method call currently resolves to `<&[T; N] as IntoIterator>::into_iter` (due to autoref coercions), but that might change in the future when `IntoIterator` impls for arrays are added.
+  --> $DIR/into-iter-on-arrays-lint.rs:60:12
+   |
+LL |     [0, 1].into_iter();
+   |            ^^^^^^^^^ help: use `.iter()` instead of `.into_iter()` to avoid ambiguity: `iter`
+   |
+note: the lint level is defined here
+  --> $DIR/into-iter-on-arrays-lint.rs:59:13
+   |
+LL |     #[allow(array_into_iter)]
+   |             ^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #66145 <https://github.com/rust-lang/rust/issues/66145>
+
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
index cd72fdd61fd3a..736a531eda657 100644
--- a/src/tools/clippy/clippy_lints/src/utils/paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs
@@ -59,7 +59,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
-pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
+pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
 pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index 6a03a76c566ef..19aec0ea598f9 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -36,6 +36,17 @@ struct DiagnosticSpan {
     expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
 }
 
+#[derive(Deserialize)]
+struct FutureIncompatReport {
+    future_incompat_report: Vec<FutureBreakageItem>,
+}
+
+#[derive(Deserialize)]
+struct FutureBreakageItem {
+    future_breakage_date: Option<String>,
+    diagnostic: Diagnostic,
+}
+
 impl DiagnosticSpan {
     /// Returns the deepest source span in the macro call stack with a given file name.
     /// This is either the supplied span, or the span for some macro callsite that expanded to it.
@@ -68,6 +79,13 @@ struct DiagnosticCode {
     explanation: Option<String>,
 }
 
+pub fn rustfix_diagnostics_only(output: &str) -> String {
+    output
+        .lines()
+        .filter(|line| line.starts_with('{') && serde_json::from_str::<Diagnostic>(line).is_ok())
+        .collect()
+}
+
 pub fn extract_rendered(output: &str) -> String {
     output
         .lines()
@@ -75,6 +93,28 @@ pub fn extract_rendered(output: &str) -> String {
             if line.starts_with('{') {
                 if let Ok(diagnostic) = serde_json::from_str::<Diagnostic>(line) {
                     diagnostic.rendered
+                } else if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(line) {
+                    if report.future_incompat_report.is_empty() {
+                        None
+                    } else {
+                        Some(format!(
+                            "Future incompatibility report: {}",
+                            report
+                                .future_incompat_report
+                                .into_iter()
+                                .map(|item| {
+                                    format!(
+                                        "Future breakage date: {}, diagnostic:\n{}",
+                                        item.future_breakage_date
+                                            .unwrap_or_else(|| "None".to_string()),
+                                        item.diagnostic
+                                            .rendered
+                                            .unwrap_or_else(|| "Not rendered".to_string())
+                                    )
+                                })
+                                .collect::<String>()
+                        ))
+                    }
                 } else if serde_json::from_str::<ArtifactNotification>(line).is_ok() {
                     // Ignore the notification.
                     None
@@ -108,11 +148,17 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) ->
                 expected_errors
             }
             Err(error) => {
-                proc_res.fatal(Some(&format!(
-                    "failed to decode compiler output as json: \
-                     `{}`\nline: {}\noutput: {}",
-                    error, line, output
-                )));
+                // Ignore the future compat report message - this is handled
+                // by `extract_rendered`
+                if serde_json::from_str::<FutureIncompatReport>(line).is_ok() {
+                    vec![]
+                } else {
+                    proc_res.fatal(Some(&format!(
+                        "failed to decode compiler output as json: \
+                         `{}`\nline: {}\noutput: {}",
+                        error, line, output
+                    )));
+                }
             }
         }
     } else {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 666e5d402ef1e..19ddcf3a01091 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1942,6 +1942,7 @@ impl<'test> TestCx<'test> {
                 }
                 rustc.arg("-Zui-testing");
                 rustc.arg("-Zdeduplicate-diagnostics=no");
+                rustc.arg("-Zemit-future-incompat-report");
             }
             MirOpt => {
                 rustc.args(&[
@@ -2978,6 +2979,7 @@ impl<'test> TestCx<'test> {
         self.prune_duplicate_outputs(&modes_to_prune);
 
         let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
+        let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
 
         if self.config.compare_mode.is_some() {
             // don't test rustfix with nll right now
@@ -2988,7 +2990,7 @@ impl<'test> TestCx<'test> {
             // This will return an empty `Vec` in case the executed test file has a
             // `compile-flags: --error-format=xxxx` header with a value other than `json`.
             let suggestions = get_suggestions_from_json(
-                &proc_res.stderr,
+                &rustfix_input,
                 &HashSet::new(),
                 Filter::MachineApplicableOnly,
             )
@@ -3015,7 +3017,7 @@ impl<'test> TestCx<'test> {
             // Apply suggestions from rustc to the code itself
             let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
             let suggestions = get_suggestions_from_json(
-                &proc_res.stderr,
+                &rustfix_input,
                 &HashSet::new(),
                 if self.props.rustfix_only_machine_applicable {
                     Filter::MachineApplicableOnly
@@ -3121,7 +3123,9 @@ impl<'test> TestCx<'test> {
                 self.fatal_proc_rec("failed to compile fixed code", &res);
             }
             if !res.stderr.is_empty() && !self.props.rustfix_only_machine_applicable {
-                self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
+                if !json::rustfix_diagnostics_only(&res.stderr).is_empty() {
+                    self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
+                }
             }
         }
     }