From ca7df9a2a9c4b1f751e91040fe659f682c976919 Mon Sep 17 00:00:00 2001
From: Rejyr <jerrylwang123@gmail.com>
Date: Wed, 9 Nov 2022 19:34:49 -0500
Subject: [PATCH] migrate: `for_loops_over_fallibles.rs`

---
 .../locales/en-US/lint.ftl                    |  7 ++
 .../src/for_loops_over_fallibles.rs           | 73 +++++++------------
 compiler/rustc_lint/src/lints.rs              | 49 +++++++++++++
 3 files changed, 84 insertions(+), 45 deletions(-)

diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl
index 1080ba2d2b5..5330ce504b2 100644
--- a/compiler/rustc_error_messages/locales/en-US/lint.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl
@@ -16,6 +16,13 @@ lint_enum_intrinsics_mem_variant =
 lint_expectation = this lint expectation is unfulfilled
     .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
 
+lint_for_loops_over_fallibles =
+    for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
+    .suggestion = consider using `if let` to clear intent
+    .remove_next = to iterate over `{$recv_snip}` remove the call to `next`
+    .use_while_let = to check pattern in a loop use `while let`
+    .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
+
 lint_non_binding_let_on_sync_lock =
     non-binding let on a synchronization lock
 
diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
index 182734fa9fc..7526b8c0632 100644
--- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
@@ -1,7 +1,14 @@
-use crate::{LateContext, LateLintPass, LintContext};
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
+use crate::{
+    lints::{
+        ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
+        ForLoopsOverFalliblesSuggestion,
+    },
+    LateContext, LateLintPass, LintContext,
+};
 
 use hir::{Expr, Pat};
-use rustc_errors::{Applicability, DelayDm};
 use rustc_hir as hir;
 use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
 use rustc_middle::ty::{self, List};
@@ -53,53 +60,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
             _ => return,
         };
 
-        let msg = DelayDm(|| {
-            format!(
-                "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
-            )
-        });
-
-        cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
-            if let Some(recv) = extract_iterator_next_call(cx, arg)
+        let sub =  if let Some(recv) = extract_iterator_next_call(cx, arg)
             && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
             {
-                lint.span_suggestion(
-                    recv.span.between(arg.span.shrink_to_hi()),
-                    format!("to iterate over `{recv_snip}` remove the call to `next`"),
-                    ".by_ref()",
-                    Applicability::MaybeIncorrect
-                );
+                ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
             } else {
-                lint.multipart_suggestion_verbose(
-                    "to check pattern in a loop use `while let`",
-                    vec![
-                        // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
-                        (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
-                        (pat.span.between(arg.span), ") = ".to_string()),
-                    ],
-                    Applicability::MaybeIncorrect
-                );
-            }
+                ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
+            } ;
+        let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
+            Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
+        } else {
+            None
+        };
+        let suggestion = ForLoopsOverFalliblesSuggestion {
+            var,
+            start_span: expr.span.with_hi(pat.span.lo()),
+            end_span: pat.span.between(arg.span),
+        };
 
-            if suggest_question_mark(cx, adt, substs, expr.span) {
-                lint.span_suggestion(
-                    arg.span.shrink_to_hi(),
-                    "consider unwrapping the `Result` with `?` to iterate over its contents",
-                    "?",
-                    Applicability::MaybeIncorrect,
-                );
-            }
-
-            lint.multipart_suggestion_verbose(
-                "consider using `if let` to clear intent",
-                vec![
-                    // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
-                    (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
-                    (pat.span.between(arg.span), ") = ".to_string()),
-                ],
-                Applicability::MaybeIncorrect,
-            )
-        })
+        cx.emit_spanned_lint(
+            FOR_LOOPS_OVER_FALLIBLES,
+            arg.span,
+            ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
+        );
     }
 }
 
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 782cf668b29..a3549346604 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -333,6 +333,55 @@ impl<'a> DecorateLint<'a, ()> for Expectation<'_> {
     }
 }
 
+// for_loops_over_fallibles.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_for_loops_over_fallibles)]
+pub struct ForLoopsOverFalliblesDiag<'a> {
+    pub article: &'static str,
+    pub ty: &'static str,
+    #[subdiagnostic]
+    pub sub: ForLoopsOverFalliblesLoopSub<'a>,
+    #[subdiagnostic]
+    pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>,
+    #[subdiagnostic]
+    pub suggestion: ForLoopsOverFalliblesSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ForLoopsOverFalliblesLoopSub<'a> {
+    #[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
+    RemoveNext {
+        #[primary_span]
+        suggestion: Span,
+        recv_snip: String,
+    },
+    #[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
+    UseWhileLet {
+        #[suggestion_part(code = "while let {var}(")]
+        start_span: Span,
+        #[suggestion_part(code = ") = ")]
+        end_span: Span,
+        var: &'a str,
+    },
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesQuestionMark {
+    #[primary_span]
+    pub suggestion: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesSuggestion<'a> {
+    pub var: &'a str,
+    #[suggestion_part(code = "if let {var}(")]
+    pub start_span: Span,
+    #[suggestion_part(code = ") = ")]
+    pub end_span: Span,
+}
+
 // internal.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_default_hash_types)]