From e540425a2425819be4717ff0e4217c40cc52c99f Mon Sep 17 00:00:00 2001
From: Josh Triplett <josh@joshtriplett.org>
Date: Fri, 1 Jul 2022 23:30:47 -0700
Subject: [PATCH 01/30] Add a `File::create_new` constructor

We have `File::create` for creating a file or opening an existing file,
but the secure way to guarantee creating a new file requires a longhand
invocation via `OpenOptions`.

Add `File::create_new` to handle this case, to make it easier for people
to do secure file creation.
---
 library/std/src/fs.rs | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index f46997b807ab2..97093ffb46fba 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -366,6 +366,35 @@ impl File {
         OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
     }
 
+    /// Creates a new file in read-write mode; error if the file exists.
+    ///
+    /// This function will create a file if it does not exist, or return an error if it does. This
+    /// way, if the call succeeds, the file returned is guaranteed to be new.
+    ///
+    /// This option is useful because it is atomic. Otherwise between checking whether a file
+    /// exists and creating a new one, the file may have been created by another process (a TOCTOU
+    /// race condition / attack).
+    ///
+    /// This can also be written using
+    /// `File::options().read(true).write(true).create_new(true).open(...)`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(file_create_new)]
+    ///
+    /// use std::fs::File;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let mut f = File::create_new("foo.txt")?;
+    ///     Ok(())
+    /// }
+    /// ```
+    #[unstable(feature = "file_create_new", issue = "none")]
+    pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
+        OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
+    }
+
     /// Returns a new OpenOptions object.
     ///
     /// This function returns a new OpenOptions object that you can use to

From c04568b90b8921c9abc4598f5195a7f567ea81d1 Mon Sep 17 00:00:00 2001
From: Noah Lev <camelidcamel@gmail.com>
Date: Tue, 9 Aug 2022 11:08:56 -0700
Subject: [PATCH 02/30] Stabilize `std::io::read_to_string`

---
 library/std/src/io/mod.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 96addbd1a0558..9d2e621f210da 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -1028,8 +1028,6 @@ pub trait Read {
 /// # Examples
 ///
 /// ```no_run
-/// #![feature(io_read_to_string)]
-///
 /// # use std::io;
 /// fn main() -> io::Result<()> {
 ///     let stdin = io::read_to_string(io::stdin())?;
@@ -1038,7 +1036,7 @@ pub trait Read {
 ///     Ok(())
 /// }
 /// ```
-#[unstable(feature = "io_read_to_string", issue = "80218")]
+#[stable(feature = "io_read_to_string", since = "1.65.0")]
 pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
     let mut buf = String::new();
     reader.read_to_string(&mut buf)?;

From 7b4cd17f81e9350ab2d267668e945ccb3c988425 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 18 Jul 2022 02:58:59 +0400
Subject: [PATCH 03/30] Start uplifting `clippy::for_loops_over_fallibles`

I refactored the code:
- Removed handling of methods, as it felt entirely unnecessary
- Removed clippy utils (obviously...)
- Used some shiny compiler features
  (let-else is very handy for lints :eyes:)
- I also renamed the lint to `for_loop_over_fallibles` (note: no `s`).
  I'm not sure what's the naming convention here, so maybe I'm wrong.
---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 99 +++++++++++++++++++
 compiler/rustc_lint/src/lib.rs                |  3 +
 2 files changed, 102 insertions(+)
 create mode 100644 compiler/rustc_lint/src/for_loop_over_fallibles.rs

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
new file mode 100644
index 0000000000000..c96c9efe1d8a1
--- /dev/null
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -0,0 +1,99 @@
+use crate::{LateContext, LateLintPass, LintContext};
+
+use hir::{Expr, Pat};
+use rustc_hir as hir;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+declare_lint! {
+    /// ### What it does
+    ///
+    /// Checks for `for` loops over `Option` or `Result` values.
+    ///
+    /// ### Why is this bad?
+    /// Readability. This is more clearly expressed as an `if
+    /// let`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # let opt = Some(1);
+    /// # let res: Result<i32, std::io::Error> = Ok(1);
+    /// for x in opt {
+    ///     // ..
+    /// }
+    ///
+    /// for x in &res {
+    ///     // ..
+    /// }
+    ///
+    /// for x in res.iter() {
+    ///     // ..
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let opt = Some(1);
+    /// # let res: Result<i32, std::io::Error> = Ok(1);
+    /// if let Some(x) = opt {
+    ///     // ..
+    /// }
+    ///
+    /// if let Ok(x) = res {
+    ///     // ..
+    /// }
+    /// ```
+    pub FOR_LOOP_OVER_FALLIBLES,
+    Warn,
+    "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
+}
+
+declare_lint_pass!(ForLoopOverFallibles => [FOR_LOOP_OVER_FALLIBLES]);
+
+impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some((pat, arg)) = extract_for_loop(expr) else { return };
+
+        let ty = cx.typeck_results().expr_ty(arg);
+
+        let ty::Adt(adt, _) = ty.kind() else { return };
+
+        let (article, ty, var) = match adt.did() {
+            did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
+            did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
+            _ => return,
+        };
+
+        let Ok(pat_snip) = cx.sess().source_map().span_to_snippet(pat.span) else { return };
+        let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
+
+        let help_string = format!(
+            "consider replacing `for {pat_snip} in {arg_snip}` with `if let {var}({pat_snip}) = {arg_snip}`"
+        );
+        let msg = format!(
+            "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
+        );
+
+        cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
+            diag.build(msg).help(help_string).emit()
+        })
+    }
+}
+
+fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>)> {
+    if let hir::ExprKind::DropTemps(e) = expr.kind
+    && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind
+    && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind
+    && let hir::ExprKind::Loop(block, ..) = arm.body.kind
+    && let [stmt] = block.stmts
+    && let hir::StmtKind::Expr(e) = stmt.kind
+    && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
+    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind 
+    {
+        Some((field.pat, arg))
+    } else {
+        None
+    }
+    
+}
\ No newline at end of file
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f087c624917e8..d9c5d47cadbf5 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -48,6 +48,7 @@ mod context;
 mod early;
 mod enum_intrinsics_non_enums;
 mod expect;
+mod for_loop_over_fallibles;
 pub mod hidden_unicode_codepoints;
 mod internal;
 mod late;
@@ -80,6 +81,7 @@ use rustc_span::Span;
 use array_into_iter::ArrayIntoIter;
 use builtin::*;
 use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
+use for_loop_over_fallibles::*;
 use hidden_unicode_codepoints::*;
 use internal::*;
 use methods::*;
@@ -178,6 +180,7 @@ macro_rules! late_lint_mod_passes {
         $macro!(
             $args,
             [
+                ForLoopOverFallibles: ForLoopOverFallibles,
                 HardwiredLints: HardwiredLints,
                 ImproperCTypesDeclarations: ImproperCTypesDeclarations,
                 ImproperCTypesDefinitions: ImproperCTypesDefinitions,

From 810cf60fddb56056759d1a80b41c7d9dc12ad28d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 18 Jul 2022 21:08:59 +0400
Subject: [PATCH 04/30] Use structured suggestions for
 `for_loop_over_fallibles` lint

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 22 ++++++++++++-------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index c96c9efe1d8a1..d765131442f6f 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -1,6 +1,7 @@
 use crate::{LateContext, LateLintPass, LintContext};
 
 use hir::{Expr, Pat};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_middle::ty;
 use rustc_span::sym;
@@ -65,18 +66,24 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
             _ => return,
         };
 
-        let Ok(pat_snip) = cx.sess().source_map().span_to_snippet(pat.span) else { return };
         let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
 
-        let help_string = format!(
-            "consider replacing `for {pat_snip} in {arg_snip}` with `if let {var}({pat_snip}) = {arg_snip}`"
-        );
         let msg = format!(
             "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
-            diag.build(msg).help(help_string).emit()
+            diag.build(msg)
+                .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), format!(") = ")),
+                    ],
+                    Applicability::MachineApplicable,
+                )
+                .emit()
         })
     }
 }
@@ -89,11 +96,10 @@ fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx E
     && let [stmt] = block.stmts
     && let hir::StmtKind::Expr(e) = stmt.kind
     && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
-    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind 
+    && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind
     {
         Some((field.pat, arg))
     } else {
         None
     }
-    
-}
\ No newline at end of file
+}

From b6611571c4ff709e03df8089401a49808ca583ab Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 21:07:23 +0400
Subject: [PATCH 05/30] `for_loop_over_fallibles`: Suggest removing `.next()`

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 49 ++++++++++++++-----
 1 file changed, 38 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index d765131442f6f..78b6ca6a653c0 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -73,17 +73,30 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {
-            diag.build(msg)
-                .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), format!(") = ")),
-                    ],
-                    Applicability::MachineApplicable,
-                )
-                .emit()
+            let mut warn = diag.build(msg);
+
+            if let Some(recv) = extract_iterator_next_call(cx, arg)
+            && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
+            {
+                warn.span_suggestion(
+                    recv.span.between(arg.span.shrink_to_hi()),
+                    format!("to iterate over `{recv_snip}` remove the call to `next`"),
+                    "",
+                    Applicability::MaybeIncorrect
+                );
+            }
+
+            warn.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), format!(") = ")),
+                ],
+                Applicability::MachineApplicable,
+            );
+
+            warn.emit()
         })
     }
 }
@@ -103,3 +116,17 @@ fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx E
         None
     }
 }
+
+fn extract_iterator_next_call<'tcx>(
+    cx: &LateContext<'_>,
+    expr: &Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+    // This won't work for `Iterator::next(iter)`, is this an issue?
+    if let hir::ExprKind::MethodCall(_, [recv], _) = expr.kind
+    && cx.typeck_results().type_dependent_def_id(expr.hir_id) == cx.tcx.lang_items().next_fn()
+    {
+        Some(recv)
+    } else {
+        return None
+    }
+}

From 7cf94ad91568cc1f2e94c0cca4987dc40aa4e452 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 21:16:44 +0400
Subject: [PATCH 06/30] `for_loop_over_fallibles`: suggest `while let` loop

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 78b6ca6a653c0..1a6188827819c 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -84,6 +84,16 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                     "",
                     Applicability::MaybeIncorrect
                 );
+            } else {
+                warn.multipart_suggestion_verbose(
+                    format!("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), format!(") = ")),
+                    ],
+                    Applicability::MaybeIncorrect
+                );
             }
 
             warn.multipart_suggestion_verbose(

From 14b8f241cb0797032c43c1c85bb0be0f80ba2c0d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 24 Jul 2022 23:46:04 +0400
Subject: [PATCH 07/30] `for_loop_over_fallibles`: suggest using `?` in some
 cases

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 68 ++++++++++++++++++-
 1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 1a6188827819c..7807fd3a2807a 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -3,8 +3,11 @@ use crate::{LateContext, LateLintPass, LintContext};
 use hir::{Expr, Pat};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_middle::ty;
-use rustc_span::sym;
+use rustc_infer::traits::TraitEngine;
+use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
+use rustc_middle::ty::{self, List};
+use rustc_span::{sym, Span};
+use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
     /// ### What it does
@@ -58,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
 
         let ty = cx.typeck_results().expr_ty(arg);
 
-        let ty::Adt(adt, _) = ty.kind() else { return };
+        let &ty::Adt(adt, substs) = ty.kind() else { return };
 
         let (article, ty, var) = match adt.did() {
             did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
@@ -96,6 +99,15 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                 );
             }
 
+            if suggest_question_mark(cx, adt, substs, expr.span) {
+                warn.span_suggestion(
+                    arg.span.shrink_to_hi(),
+                    "consider unwrapping the `Result` with `?` to iterate over its contents",
+                    "?",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
             warn.multipart_suggestion_verbose(
                 "consider using `if let` to clear intent",
                 vec![
@@ -140,3 +152,53 @@ fn extract_iterator_next_call<'tcx>(
         return None
     }
 }
+
+fn suggest_question_mark<'tcx>(
+    cx: &LateContext<'tcx>,
+    adt: ty::AdtDef<'tcx>,
+    substs: &List<ty::GenericArg<'tcx>>,
+    span: Span,
+) -> bool {
+    let Some(body_id) = cx.enclosing_body else { return false };
+    let Some(into_iterator_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { return false };
+
+    if !cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
+        return false;
+    }
+
+    // Check that the function/closure/constant we are in has a `Result` type.
+    // Otherwise suggesting using `?` may not be a good idea.
+    {
+        let ty = cx.typeck_results().expr_ty(&cx.tcx.hir().body(body_id).value);
+        let ty::Adt(ret_adt, ..) = ty.kind() else { return false };
+        if !cx.tcx.is_diagnostic_item(sym::Result, ret_adt.did()) {
+            return false;
+        }
+    }
+
+    let ty = substs.type_at(0);
+    let is_iterator = cx.tcx.infer_ctxt().enter(|infcx| {
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+
+        let cause = ObligationCause::new(
+            span,
+            body_id.hir_id,
+            rustc_infer::traits::ObligationCauseCode::MiscObligation,
+        );
+        fulfill_cx.register_bound(
+            &infcx,
+            ty::ParamEnv::empty(),
+            // Erase any region vids from the type, which may not be resolved
+            infcx.tcx.erase_regions(ty),
+            into_iterator_did,
+            cause,
+        );
+
+        // Select all, including ambiguous predicates
+        let errors = fulfill_cx.select_all_or_error(&infcx);
+
+        errors.is_empty()
+    });
+
+    is_iterator
+}

From 2bf213bb0852e8109253838a403d042b203f807a Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:17:18 +0400
Subject: [PATCH 08/30] `for_loop_over_fallibles`: remove duplication from the
 message

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 7807fd3a2807a..69d8fd84b64d5 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -69,10 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
             _ => return,
         };
 
-        let Ok(arg_snip) = cx.sess().source_map().span_to_snippet(arg.span) else { return };
-
         let msg = format!(
-            "for loop over `{arg_snip}`, which is {article} `{ty}`. This is more readably written as an `if let` statement",
+            "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
         );
 
         cx.struct_span_lint(FOR_LOOP_OVER_FALLIBLES, arg.span, |diag| {

From 5128140b63bab45d6e8cba34f5a2231a5bb3d22d Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:37:11 +0400
Subject: [PATCH 09/30] Add a test for the `for_loop_over_fallibles` lint

---
 src/test/ui/lint/for_loop_over_fallibles.rs   |  43 ++++++++
 .../ui/lint/for_loop_over_fallibles.stderr    | 102 ++++++++++++++++++
 2 files changed, 145 insertions(+)
 create mode 100644 src/test/ui/lint/for_loop_over_fallibles.rs
 create mode 100644 src/test/ui/lint/for_loop_over_fallibles.stderr

diff --git a/src/test/ui/lint/for_loop_over_fallibles.rs b/src/test/ui/lint/for_loop_over_fallibles.rs
new file mode 100644
index 0000000000000..43d71c2e808a9
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.rs
@@ -0,0 +1,43 @@
+// check-pass
+
+fn main() {
+    // Common
+    for _ in Some(1) {}
+    //~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+    for _ in Ok::<_, ()>(1) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+
+    // `Iterator::next` specific
+    for _ in [0; 0].iter().next() {}
+    //~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
+    //~| HELP to iterate over `[0; 0].iter()` remove the call to `next`
+    //~| HELP consider using `if let` to clear intent
+
+    // `Result<impl Iterator, _>`, but function doesn't return `Result`
+    for _ in Ok::<_, ()>([0; 0].iter()) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider using `if let` to clear intent
+}
+
+fn _returns_result() -> Result<(), ()> {
+    // `Result<impl Iterator, _>`
+    for _ in Ok::<_, ()>([0; 0].iter()) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
+    //~| HELP consider using `if let` to clear intent
+
+    // `Result<impl IntoIterator>`
+    for _ in Ok::<_, ()>([0; 0]) {}
+    //~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
+    //~| HELP to check pattern in a loop use `while let`
+    //~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
+    //~| HELP consider using `if let` to clear intent
+
+    Ok(())
+}
diff --git a/src/test/ui/lint/for_loop_over_fallibles.stderr b/src/test/ui/lint/for_loop_over_fallibles.stderr
new file mode 100644
index 0000000000000..52eac945d8571
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.stderr
@@ -0,0 +1,102 @@
+warning: for loop over an `Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:5:14
+   |
+LL |     for _ in Some(1) {}
+   |              ^^^^^^^
+   |
+   = note: `#[warn(for_loop_over_fallibles)]` on by default
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Some(_) = Some(1) {}
+   |     ~~~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = Some(1) {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:9:14
+   |
+LL |     for _ in Ok::<_, ()>(1) {}
+   |              ^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>(1) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over an `Option`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:15:14
+   |
+LL |     for _ in [0; 0].iter().next() {}
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: to iterate over `[0; 0].iter()` remove the call to `next`
+   |
+LL -     for _ in [0; 0].iter().next() {}
+LL +     for _ in [0; 0].iter() {}
+   |
+help: consider using `if let` to clear intent
+   |
+LL |     if let Some(_) = [0; 0].iter().next() {}
+   |     ~~~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:21:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter()) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:29:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter()) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider unwrapping the `Result` with `?` to iterate over its contents
+   |
+LL |     for _ in Ok::<_, ()>([0; 0].iter())? {}
+   |                                        +
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: for loop over a `Result`. This is more readably written as an `if let` statement
+  --> $DIR/for_loop_over_fallibles.rs:36:14
+   |
+LL |     for _ in Ok::<_, ()>([0; 0]) {}
+   |              ^^^^^^^^^^^^^^^^^^^
+   |
+help: to check pattern in a loop use `while let`
+   |
+LL |     while let Ok(_) = Ok::<_, ()>([0; 0]) {}
+   |     ~~~~~~~~~~~~~ ~~~
+help: consider unwrapping the `Result` with `?` to iterate over its contents
+   |
+LL |     for _ in Ok::<_, ()>([0; 0])? {}
+   |                                 +
+help: consider using `if let` to clear intent
+   |
+LL |     if let Ok(_) = Ok::<_, ()>([0; 0]) {}
+   |     ~~~~~~~~~~ ~~~
+
+warning: 6 warnings emitted
+

From 34815a90dd06af1bdb3504caf59138c2c5a6202f Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 00:58:04 +0400
Subject: [PATCH 10/30] `for_loop_over_fallibles`: fix suggestion for "remove
 `.next()`" case

if the iterator is used after the loop, we need to use `.by_ref()`
---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 2 +-
 src/test/ui/lint/for_loop_over_fallibles.stderr    | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 69d8fd84b64d5..c8d5586d39f9c 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                 warn.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
                 );
             } else {
diff --git a/src/test/ui/lint/for_loop_over_fallibles.stderr b/src/test/ui/lint/for_loop_over_fallibles.stderr
index 52eac945d8571..56e3126dc09a4 100644
--- a/src/test/ui/lint/for_loop_over_fallibles.stderr
+++ b/src/test/ui/lint/for_loop_over_fallibles.stderr
@@ -37,9 +37,8 @@ LL |     for _ in [0; 0].iter().next() {}
    |
 help: to iterate over `[0; 0].iter()` remove the call to `next`
    |
-LL -     for _ in [0; 0].iter().next() {}
-LL +     for _ in [0; 0].iter() {}
-   |
+LL |     for _ in [0; 0].iter().by_ref() {}
+   |                           ~~~~~~~~~
 help: consider using `if let` to clear intent
    |
 LL |     if let Some(_) = [0; 0].iter().next() {}

From c4ab59e1921262cb9e07eaeb193192883317af04 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 25 Jul 2022 01:04:27 +0400
Subject: [PATCH 11/30] `for_loop_over_fallibles`: don't use
 `MachineApplicable`

The loop could contain `break;` that won't work with an `if let`
---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index c8d5586d39f9c..6870942af941f 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
                     (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
                     (pat.span.between(arg.span), format!(") = ")),
                 ],
-                Applicability::MachineApplicable,
+                Applicability::MaybeIncorrect,
             );
 
             warn.emit()

From 41fccb1517db488acd12ba8d962157da88bc4bd4 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Tue, 26 Jul 2022 14:17:15 +0400
Subject: [PATCH 12/30] allow or avoid for loops over option in compiler and
 tests

---
 compiler/rustc_ast/src/visit.rs                    | 14 ++++++--------
 .../rustc_borrowck/src/type_check/input_output.rs  |  2 +-
 src/test/ui/drop/dropck_legal_cycles.rs            | 14 +++++++-------
 src/test/ui/issues/issue-30371.rs                  |  1 +
 4 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 4b485b547f495..5820776d04f3d 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -244,14 +244,12 @@ pub trait Visitor<'ast>: Sized {
 
 #[macro_export]
 macro_rules! walk_list {
-    ($visitor: expr, $method: ident, $list: expr) => {
-        for elem in $list {
-            $visitor.$method(elem)
-        }
-    };
-    ($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => {
-        for elem in $list {
-            $visitor.$method(elem, $($extra_args,)*)
+    ($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => {
+        {
+            #[cfg_attr(not(bootstrap), allow(for_loop_over_fallibles))]
+            for elem in $list {
+                $visitor.$method(elem $(, $($extra_args,)* )?)
+            }
         }
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 4431a2e8ec60d..84d2bfe04de05 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -225,7 +225,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
         debug!("{:?} normalized to {:?}", t, norm_ty);
 
-        for data in constraints {
+        if let Some(data) = constraints {
             ConstraintConversion::new(
                 self.infcx,
                 &self.borrowck_context.universal_regions,
diff --git a/src/test/ui/drop/dropck_legal_cycles.rs b/src/test/ui/drop/dropck_legal_cycles.rs
index 27a599315dc1c..6a0fe7784fbcc 100644
--- a/src/test/ui/drop/dropck_legal_cycles.rs
+++ b/src/test/ui/drop/dropck_legal_cycles.rs
@@ -1017,7 +1017,7 @@ impl<'a> Children<'a> for HM<'a> {
         where C: Context + PrePost<Self>, Self: Sized
     {
         if let Some(ref hm) = self.contents.get() {
-            for (k, v) in hm.iter().nth(index / 2) {
+            if let Some((k, v)) = hm.iter().nth(index / 2) {
                 [k, v][index % 2].descend_into_self(context);
             }
         }
@@ -1032,7 +1032,7 @@ impl<'a> Children<'a> for VD<'a> {
         where C: Context + PrePost<Self>, Self: Sized
     {
         if let Some(ref vd) = self.contents.get() {
-            for r in vd.iter().nth(index) {
+            if let Some(r) = vd.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1047,7 +1047,7 @@ impl<'a> Children<'a> for VM<'a> {
         where C: Context + PrePost<VM<'a>>
     {
         if let Some(ref vd) = self.contents.get() {
-            for (_idx, r) in vd.iter().nth(index) {
+            if let Some((_idx, r)) = vd.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1062,7 +1062,7 @@ impl<'a> Children<'a> for LL<'a> {
         where C: Context + PrePost<LL<'a>>
     {
         if let Some(ref ll) = self.contents.get() {
-            for r in ll.iter().nth(index) {
+            if let Some(r) = ll.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1077,7 +1077,7 @@ impl<'a> Children<'a> for BH<'a> {
         where C: Context + PrePost<BH<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for r in bh.iter().nth(index) {
+            if let Some(r) = bh.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
@@ -1092,7 +1092,7 @@ impl<'a> Children<'a> for BTM<'a> {
         where C: Context + PrePost<BTM<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for (k, v) in bh.iter().nth(index / 2) {
+            if let Some((k, v)) = bh.iter().nth(index / 2) {
                 [k, v][index % 2].descend_into_self(context);
             }
         }
@@ -1107,7 +1107,7 @@ impl<'a> Children<'a> for BTS<'a> {
         where C: Context + PrePost<BTS<'a>>
     {
         if let Some(ref bh) = self.contents.get() {
-            for r in bh.iter().nth(index) {
+            if let Some(r) = bh.iter().nth(index) {
                 r.descend_into_self(context);
             }
         }
diff --git a/src/test/ui/issues/issue-30371.rs b/src/test/ui/issues/issue-30371.rs
index a1ae9a36bc1d7..880558eb5b697 100644
--- a/src/test/ui/issues/issue-30371.rs
+++ b/src/test/ui/issues/issue-30371.rs
@@ -1,5 +1,6 @@
 // run-pass
 #![allow(unreachable_code)]
+#![allow(for_loop_over_fallibles)]
 #![deny(unused_variables)]
 
 fn main() {

From 86360f41d9f350273d480986526d1c5a15673f1c Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Tue, 26 Jul 2022 16:19:58 +0400
Subject: [PATCH 13/30] allow `for_loop_over_fallibles` in a `core` test

---
 library/core/tests/option.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs
index 9f5e537dcefc0..84eb4fc0aa329 100644
--- a/library/core/tests/option.rs
+++ b/library/core/tests/option.rs
@@ -57,6 +57,7 @@ fn test_get_resource() {
 }
 
 #[test]
+#[cfg_attr(not(bootstrap), allow(for_loop_over_fallibles))]
 fn test_option_dance() {
     let x = Some(());
     let mut y = Some(5);

From d7b8a65c3aec77b8d6ccf7127cbdfdcf085a6873 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Sun, 14 Aug 2022 21:42:29 +0400
Subject: [PATCH 14/30] Edit documentation for `for_loop_over_fallibles` lint

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 44 +++++++++----------
 1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 6870942af941f..48a876b157bda 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -10,43 +10,41 @@ use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
-    /// ### What it does
-    ///
     /// Checks for `for` loops over `Option` or `Result` values.
     ///
-    /// ### Why is this bad?
-    /// Readability. This is more clearly expressed as an `if
-    /// let`.
+    /// ### Explanation
+    ///
+    /// Both `Option` and `Result` implement `IntoIterator` trait, which allows using them in a `for` loop.
+    /// `for` loop over `Option` or `Result` will iterate either 0 (if the value is `None`/`Err(_)`)
+    /// or 1 time (if the value is `Some(_)`/`Ok(_)`). This is not very useful and is more clearly expressed
+    /// via `if let`.
+    ///
+    /// `for` loop can also be accidentally written with the intention to call a function multiple times,
+    /// while the function returns `Some(_)`, in these cases `while let` loop should be used instead.
+    ///
+    /// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
+    /// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
+    /// to optionally add a value to an iterator.
     ///
     /// ### Example
     ///
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// for x in opt {
-    ///     // ..
-    /// }
-    ///
-    /// for x in &res {
-    ///     // ..
-    /// }
-    ///
-    /// for x in res.iter() {
-    ///     // ..
-    /// }
+    /// # let recv = || Some(1);
+    /// for x in opt { /* ... */}
+    /// for x in res { /* ... */ }
+    /// for x in recv() { /* ... */ }
     /// ```
     ///
     /// Use instead:
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// if let Some(x) = opt {
-    ///     // ..
-    /// }
-    ///
-    /// if let Ok(x) = res {
-    ///     // ..
-    /// }
+    /// # let recv = || Some(1);
+    /// if let Some(x) = opt { /* ... */}
+    /// if let Ok(x) = res { /* ... */ }
+    /// while let Some(x) = recv() { /* ... */ }
     /// ```
     pub FOR_LOOP_OVER_FALLIBLES,
     Warn,

From aed1ae44eb70f8e7c3a859d3e46d4533b2941066 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Mon, 15 Aug 2022 05:22:00 +0400
Subject: [PATCH 15/30] remove an infinite loop

---
 compiler/rustc_lint/src/for_loop_over_fallibles.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 48a876b157bda..0a6b6e4163622 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -31,7 +31,7 @@ declare_lint! {
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || Some(1);
+    /// # let recv = || None::<i32>;
     /// for x in opt { /* ... */}
     /// for x in res { /* ... */ }
     /// for x in recv() { /* ... */ }
@@ -41,7 +41,7 @@ declare_lint! {
     /// ```rust
     /// # let opt = Some(1);
     /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || Some(1);
+    /// # let recv = || None::<i32>;
     /// if let Some(x) = opt { /* ... */}
     /// if let Ok(x) = res { /* ... */ }
     /// while let Some(x) = recv() { /* ... */ }

From 71b8c89a5b467dda27ca29a91a17b8c933d5ce25 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Thu, 18 Aug 2022 11:43:10 +0400
Subject: [PATCH 16/30] fix `for_loop_over_fallibles` lint docs

---
 .../rustc_lint/src/for_loop_over_fallibles.rs | 32 ++++++-------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_lint/src/for_loop_over_fallibles.rs b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
index 0a6b6e4163622..2253546b5d357 100644
--- a/compiler/rustc_lint/src/for_loop_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loop_over_fallibles.rs
@@ -10,7 +10,16 @@ use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::TraitEngineExt;
 
 declare_lint! {
-    /// Checks for `for` loops over `Option` or `Result` values.
+    /// The `for_loop_over_fallibles` lint checks for `for` loops over `Option` or `Result` values.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let opt = Some(1);
+    /// for x in opt { /* ... */}
+    /// ```
+    ///
+    /// {{produces}}
     ///
     /// ### Explanation
     ///
@@ -25,27 +34,6 @@ declare_lint! {
     /// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
     /// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
     /// to optionally add a value to an iterator.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// # let opt = Some(1);
-    /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || None::<i32>;
-    /// for x in opt { /* ... */}
-    /// for x in res { /* ... */ }
-    /// for x in recv() { /* ... */ }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let opt = Some(1);
-    /// # let res: Result<i32, std::io::Error> = Ok(1);
-    /// # let recv = || None::<i32>;
-    /// if let Some(x) = opt { /* ... */}
-    /// if let Ok(x) = res { /* ... */ }
-    /// while let Some(x) = recv() { /* ... */ }
-    /// ```
     pub FOR_LOOP_OVER_FALLIBLES,
     Warn,
     "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"

From 36c42fa617ef0a0c824fd6d87e214fed407a66e5 Mon Sep 17 00:00:00 2001
From: Markus Reiter <me@reitermark.us>
Date: Tue, 16 Aug 2022 20:23:32 +0200
Subject: [PATCH 17/30] Use `DisplayBuffer` for socket addresses.

---
 .../src/net/{ip => addr}/display_buffer.rs    |  6 +-
 library/std/src/net/{ip.rs => addr/ip/mod.rs} |  7 +--
 library/std/src/net/{ => addr}/ip/tests.rs    |  0
 library/std/src/net/addr/mod.rs               |  4 ++
 .../src/net/{addr.rs => addr/socket/mod.rs}   | 58 +++++++------------
 .../std/src/net/addr/{ => socket}/tests.rs    |  0
 library/std/src/net/mod.rs                    |  5 +-
 7 files changed, 34 insertions(+), 46 deletions(-)
 rename library/std/src/net/{ip => addr}/display_buffer.rs (86%)
 rename library/std/src/net/{ip.rs => addr/ip/mod.rs} (99%)
 rename library/std/src/net/{ => addr}/ip/tests.rs (100%)
 create mode 100644 library/std/src/net/addr/mod.rs
 rename library/std/src/net/{addr.rs => addr/socket/mod.rs} (94%)
 rename library/std/src/net/addr/{ => socket}/tests.rs (100%)

diff --git a/library/std/src/net/ip/display_buffer.rs b/library/std/src/net/addr/display_buffer.rs
similarity index 86%
rename from library/std/src/net/ip/display_buffer.rs
rename to library/std/src/net/addr/display_buffer.rs
index bd852d5da8ec5..7aadf06e92fc6 100644
--- a/library/std/src/net/ip/display_buffer.rs
+++ b/library/std/src/net/addr/display_buffer.rs
@@ -3,12 +3,12 @@ use crate::mem::MaybeUninit;
 use crate::str;
 
 /// Used for slow path in `Display` implementations when alignment is required.
-pub struct IpDisplayBuffer<const SIZE: usize> {
+pub struct DisplayBuffer<const SIZE: usize> {
     buf: [MaybeUninit<u8>; SIZE],
     len: usize,
 }
 
-impl<const SIZE: usize> IpDisplayBuffer<SIZE> {
+impl<const SIZE: usize> DisplayBuffer<SIZE> {
     #[inline]
     pub const fn new() -> Self {
         Self { buf: MaybeUninit::uninit_array(), len: 0 }
@@ -25,7 +25,7 @@ impl<const SIZE: usize> IpDisplayBuffer<SIZE> {
     }
 }
 
-impl<const SIZE: usize> fmt::Write for IpDisplayBuffer<SIZE> {
+impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
         let bytes = s.as_bytes();
 
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/addr/ip/mod.rs
similarity index 99%
rename from library/std/src/net/ip.rs
rename to library/std/src/net/addr/ip/mod.rs
index 6004810655ebb..a670f7168334a 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/addr/ip/mod.rs
@@ -8,8 +8,7 @@ use crate::mem::transmute;
 use crate::sys::net::netc as c;
 use crate::sys_common::{FromInner, IntoInner};
 
-mod display_buffer;
-use display_buffer::IpDisplayBuffer;
+use super::display_buffer::DisplayBuffer;
 
 /// An IP address, either IPv4 or IPv6.
 ///
@@ -997,7 +996,7 @@ impl fmt::Display for Ipv4Addr {
         } else {
             const LONGEST_IPV4_ADDR: &str = "255.255.255.255";
 
-            let mut buf = IpDisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new();
+            let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new();
             // Buffer is long enough for the longest possible IPv4 address, so this should never fail.
             write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap();
 
@@ -1844,7 +1843,7 @@ impl fmt::Display for Ipv6Addr {
         } else {
             const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
 
-            let mut buf = IpDisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new();
+            let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new();
             // Buffer is long enough for the longest possible IPv6 address, so this should never fail.
             write!(buf, "{}", self).unwrap();
 
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/addr/ip/tests.rs
similarity index 100%
rename from library/std/src/net/ip/tests.rs
rename to library/std/src/net/addr/ip/tests.rs
diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs
new file mode 100644
index 0000000000000..afecab3049848
--- /dev/null
+++ b/library/std/src/net/addr/mod.rs
@@ -0,0 +1,4 @@
+mod display_buffer;
+
+pub mod ip;
+pub mod socket;
diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr/socket/mod.rs
similarity index 94%
rename from library/std/src/net/addr.rs
rename to library/std/src/net/addr/socket/mod.rs
index 53fee952a7a7a..33b0dfa03e0ed 100644
--- a/library/std/src/net/addr.rs
+++ b/library/std/src/net/addr/socket/mod.rs
@@ -2,9 +2,9 @@
 mod tests;
 
 use crate::cmp::Ordering;
-use crate::fmt;
+use crate::fmt::{self, Write};
 use crate::hash;
-use crate::io::{self, Write};
+use crate::io;
 use crate::iter;
 use crate::mem;
 use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@@ -15,6 +15,8 @@ use crate::sys_common::net::LookupHost;
 use crate::sys_common::{FromInner, IntoInner};
 use crate::vec;
 
+use super::display_buffer::DisplayBuffer;
+
 /// An internet socket address, either IPv4 or IPv6.
 ///
 /// Internet socket addresses consist of an [IP address], a 16-bit port number, as well
@@ -616,25 +618,18 @@ impl fmt::Debug for SocketAddr {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV4 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Fast path: if there's no alignment stuff, write to the output buffer
-        // directly
+        // If there are no alignment requirements, write the socket address directly to `f`.
+        // Otherwise, write it to a local buffer and then use `f.pad`.
         if f.precision().is_none() && f.width().is_none() {
             write!(f, "{}:{}", self.ip(), self.port())
         } else {
-            const IPV4_SOCKET_BUF_LEN: usize = (3 * 4)  // the segments
-                + 3  // the separators
-                + 1 + 5; // the port
-            let mut buf = [0; IPV4_SOCKET_BUF_LEN];
-            let mut buf_slice = &mut buf[..];
-
-            // Unwrap is fine because writing to a sufficiently-sized
-            // buffer is infallible
-            write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
-            let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
-
-            // This unsafe is OK because we know what is being written to the buffer
-            let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
-            f.pad(buf)
+            const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536";
+
+            let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new();
+            // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail.
+            write!(buf, "{}:{}", self.ip(), self.port()).unwrap();
+
+            f.pad(buf.as_str())
         }
     }
 }
@@ -649,35 +644,26 @@ impl fmt::Debug for SocketAddrV4 {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV6 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Fast path: if there's no alignment stuff, write to the output
-        // buffer directly
+        // If there are no alignment requirements, write the socket address directly to `f`.
+        // Otherwise, write it to a local buffer and then use `f.pad`.
         if f.precision().is_none() && f.width().is_none() {
             match self.scope_id() {
                 0 => write!(f, "[{}]:{}", self.ip(), self.port()),
                 scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
             }
         } else {
-            const IPV6_SOCKET_BUF_LEN: usize = (4 * 8)  // The address
-            + 7  // The colon separators
-            + 2  // The brackets
-            + 1 + 10 // The scope id
-            + 1 + 5; // The port
-
-            let mut buf = [0; IPV6_SOCKET_BUF_LEN];
-            let mut buf_slice = &mut buf[..];
+            const LONGEST_IPV6_SOCKET_ADDR: &str =
+                "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536";
 
+            let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new();
             match self.scope_id() {
-                0 => write!(buf_slice, "[{}]:{}", self.ip(), self.port()),
-                scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
+                0 => write!(buf, "[{}]:{}", self.ip(), self.port()),
+                scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()),
             }
-            // Unwrap is fine because writing to a sufficiently-sized
-            // buffer is infallible
+            // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail.
             .unwrap();
-            let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
 
-            // This unsafe is OK because we know what is being written to the buffer
-            let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
-            f.pad(buf)
+            f.pad(buf.as_str())
         }
     }
 }
diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/socket/tests.rs
similarity index 100%
rename from library/std/src/net/addr/tests.rs
rename to library/std/src/net/addr/socket/tests.rs
diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs
index e7a40bdaf8e99..c188de7168d69 100644
--- a/library/std/src/net/mod.rs
+++ b/library/std/src/net/mod.rs
@@ -24,9 +24,9 @@
 use crate::io::{self, ErrorKind};
 
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
+pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::parser::AddrParseError;
 #[unstable(feature = "tcplistener_into_incoming", issue = "88339")]
@@ -37,7 +37,6 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream};
 pub use self::udp::UdpSocket;
 
 mod addr;
-mod ip;
 mod parser;
 mod tcp;
 #[cfg(test)]

From 63700a8ed15124853f34d2e1c783c51dd69b05ae Mon Sep 17 00:00:00 2001
From: Markus Reiter <me@reitermark.us>
Date: Sat, 20 Aug 2022 00:06:47 +0200
Subject: [PATCH 18/30] Add tests for `SockAddr` `Display`.

---
 library/std/src/net/addr/socket/tests.rs | 69 ++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/library/std/src/net/addr/socket/tests.rs b/library/std/src/net/addr/socket/tests.rs
index 585a17451a0b7..15211f81981ba 100644
--- a/library/std/src/net/addr/socket/tests.rs
+++ b/library/std/src/net/addr/socket/tests.rs
@@ -51,6 +51,75 @@ fn to_socket_addr_string() {
     // s has been moved into the tsa call
 }
 
+#[test]
+fn ipv4_socket_addr_to_string() {
+    // Shortest possible IPv4 length.
+    assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0");
+
+    // Longest possible IPv4 length.
+    assert_eq!(
+        SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(),
+        "255.255.255.255:65535"
+    );
+
+    // Test padding.
+    assert_eq!(
+        &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+        "1.1.1.1:53      "
+    );
+    assert_eq!(
+        &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+        "      1.1.1.1:53"
+    );
+}
+
+#[test]
+fn ipv6_socket_addr_to_string() {
+    // IPv4-mapped address.
+    assert_eq!(
+        SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0)
+            .to_string(),
+        "[::ffff:192.0.2.128]:8080"
+    );
+
+    // IPv4-compatible address.
+    assert_eq!(
+        SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(),
+        "[::192.0.2.128]:8080"
+    );
+
+    // IPv6 address with no zero segments.
+    assert_eq!(
+        SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(),
+        "[8:9:a:b:c:d:e:f]:80"
+    );
+
+    // Shortest possible IPv6 length.
+    assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0");
+
+    // Longest possible IPv6 length.
+    assert_eq!(
+        SocketAddrV6::new(
+            Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888),
+            u16::MAX,
+            u32::MAX,
+            u32::MAX,
+        )
+        .to_string(),
+        "[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535"
+    );
+
+    // Test padding.
+    assert_eq!(
+        &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+        "[1:2:3:4:5:6:7:8]:9   "
+    );
+    assert_eq!(
+        &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+        "   [1:2:3:4:5:6:7:8]:9"
+    );
+}
+
 #[test]
 fn bind_udp_socket_bad() {
     // rust-lang/rust#53957: This is a regression test for a parsing problem

From 89c74e8e25216d9e18d515cf4792b0f2ee92226c Mon Sep 17 00:00:00 2001
From: Markus Reiter <me@reitermark.us>
Date: Sat, 20 Aug 2022 00:24:49 +0200
Subject: [PATCH 19/30] Move `net::parser` into `net::addr` module.

---
 library/std/src/net/addr/mod.rs                       | 1 +
 library/std/src/net/{parser.rs => addr/parser/mod.rs} | 0
 library/std/src/net/{ => addr}/parser/tests.rs        | 0
 library/std/src/net/mod.rs                            | 5 ++---
 4 files changed, 3 insertions(+), 3 deletions(-)
 rename library/std/src/net/{parser.rs => addr/parser/mod.rs} (100%)
 rename library/std/src/net/{ => addr}/parser/tests.rs (100%)

diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs
index afecab3049848..da33666a5805a 100644
--- a/library/std/src/net/addr/mod.rs
+++ b/library/std/src/net/addr/mod.rs
@@ -1,4 +1,5 @@
 mod display_buffer;
 
 pub mod ip;
+pub mod parser;
 pub mod socket;
diff --git a/library/std/src/net/parser.rs b/library/std/src/net/addr/parser/mod.rs
similarity index 100%
rename from library/std/src/net/parser.rs
rename to library/std/src/net/addr/parser/mod.rs
diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/addr/parser/tests.rs
similarity index 100%
rename from library/std/src/net/parser/tests.rs
rename to library/std/src/net/addr/parser/tests.rs
diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs
index c188de7168d69..316a456395000 100644
--- a/library/std/src/net/mod.rs
+++ b/library/std/src/net/mod.rs
@@ -26,9 +26,9 @@ use crate::io::{self, ErrorKind};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+pub use self::addr::parser::AddrParseError;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::parser::AddrParseError;
+pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
 #[unstable(feature = "tcplistener_into_incoming", issue = "88339")]
 pub use self::tcp::IntoIncoming;
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -37,7 +37,6 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream};
 pub use self::udp::UdpSocket;
 
 mod addr;
-mod parser;
 mod tcp;
 #[cfg(test)]
 mod test;

From d61ecec44e1249dfa972a6acc8643827bfaa169a Mon Sep 17 00:00:00 2001
From: Markus Reiter <me@reitermark.us>
Date: Wed, 24 Aug 2022 13:13:12 +0200
Subject: [PATCH 20/30] Flatten `net` module again.

---
 library/std/src/net/addr/mod.rs                       |  5 -----
 library/std/src/net/{addr => }/display_buffer.rs      |  0
 library/std/src/net/{addr/ip/mod.rs => ip_addr.rs}    |  0
 library/std/src/net/{addr/ip => ip_addr}/tests.rs     |  0
 library/std/src/net/mod.rs                            | 11 +++++++----
 library/std/src/net/{addr/parser/mod.rs => parser.rs} |  0
 library/std/src/net/{addr => }/parser/tests.rs        |  0
 .../src/net/{addr/socket/mod.rs => socket_addr.rs}    |  0
 .../std/src/net/{addr/socket => socket_addr}/tests.rs |  0
 9 files changed, 7 insertions(+), 9 deletions(-)
 delete mode 100644 library/std/src/net/addr/mod.rs
 rename library/std/src/net/{addr => }/display_buffer.rs (100%)
 rename library/std/src/net/{addr/ip/mod.rs => ip_addr.rs} (100%)
 rename library/std/src/net/{addr/ip => ip_addr}/tests.rs (100%)
 rename library/std/src/net/{addr/parser/mod.rs => parser.rs} (100%)
 rename library/std/src/net/{addr => }/parser/tests.rs (100%)
 rename library/std/src/net/{addr/socket/mod.rs => socket_addr.rs} (100%)
 rename library/std/src/net/{addr/socket => socket_addr}/tests.rs (100%)

diff --git a/library/std/src/net/addr/mod.rs b/library/std/src/net/addr/mod.rs
deleted file mode 100644
index da33666a5805a..0000000000000
--- a/library/std/src/net/addr/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-mod display_buffer;
-
-pub mod ip;
-pub mod parser;
-pub mod socket;
diff --git a/library/std/src/net/addr/display_buffer.rs b/library/std/src/net/display_buffer.rs
similarity index 100%
rename from library/std/src/net/addr/display_buffer.rs
rename to library/std/src/net/display_buffer.rs
diff --git a/library/std/src/net/addr/ip/mod.rs b/library/std/src/net/ip_addr.rs
similarity index 100%
rename from library/std/src/net/addr/ip/mod.rs
rename to library/std/src/net/ip_addr.rs
diff --git a/library/std/src/net/addr/ip/tests.rs b/library/std/src/net/ip_addr/tests.rs
similarity index 100%
rename from library/std/src/net/addr/ip/tests.rs
rename to library/std/src/net/ip_addr/tests.rs
diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs
index 316a456395000..cc2e0d9742fea 100644
--- a/library/std/src/net/mod.rs
+++ b/library/std/src/net/mod.rs
@@ -24,11 +24,11 @@
 use crate::io::{self, ErrorKind};
 
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::ip::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
+pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::parser::AddrParseError;
+pub use self::parser::AddrParseError;
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::addr::socket::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
 #[unstable(feature = "tcplistener_into_incoming", issue = "88339")]
 pub use self::tcp::IntoIncoming;
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -36,7 +36,10 @@ pub use self::tcp::{Incoming, TcpListener, TcpStream};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::udp::UdpSocket;
 
-mod addr;
+mod display_buffer;
+mod ip_addr;
+mod parser;
+mod socket_addr;
 mod tcp;
 #[cfg(test)]
 mod test;
diff --git a/library/std/src/net/addr/parser/mod.rs b/library/std/src/net/parser.rs
similarity index 100%
rename from library/std/src/net/addr/parser/mod.rs
rename to library/std/src/net/parser.rs
diff --git a/library/std/src/net/addr/parser/tests.rs b/library/std/src/net/parser/tests.rs
similarity index 100%
rename from library/std/src/net/addr/parser/tests.rs
rename to library/std/src/net/parser/tests.rs
diff --git a/library/std/src/net/addr/socket/mod.rs b/library/std/src/net/socket_addr.rs
similarity index 100%
rename from library/std/src/net/addr/socket/mod.rs
rename to library/std/src/net/socket_addr.rs
diff --git a/library/std/src/net/addr/socket/tests.rs b/library/std/src/net/socket_addr/tests.rs
similarity index 100%
rename from library/std/src/net/addr/socket/tests.rs
rename to library/std/src/net/socket_addr/tests.rs

From 4e976262a1598fa48cd08fa0e429f05299d1ae3e Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 02:00:37 +0000
Subject: [PATCH 21/30] Call them constants instead of types

---
 .../src/infer/error_reporting/mod.rs          |  5 +++
 .../generic-expr-default-concrete.stderr      |  4 +--
 ...neric-expr-default-mismatched-types.stderr |  4 +--
 .../abstract-const-as-cast-3.stderr           | 32 +++++++++----------
 .../generic_const_exprs/different-fn.stderr   |  4 +--
 .../issue-62504.full.stderr                   |  4 +--
 ...ue-72819-generic-in-const-eval.full.stderr |  8 ++---
 .../generic_const_exprs/issue-83765.stderr    |  8 ++---
 .../generic_const_exprs/issue-85848.stderr    |  4 +--
 .../const-generics/issues/issue-73260.stderr  |  8 ++---
 .../const-generics/issues/issue-79674.stderr  |  4 +--
 .../types-mismatch-const-args.full.stderr     |  4 +--
 12 files changed, 47 insertions(+), 42 deletions(-)

diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 59ea1f3f9de45..16cffb45f0f06 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1588,9 +1588,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                             Mismatch::Variable(infer::ExpectedFound { expected, found }),
                         )
                     }
+                    ValuePairs::Terms(infer::ExpectedFound {
+                        expected: ty::Term::Const(_),
+                        found: ty::Term::Const(_),
+                    }) => (false, Mismatch::Fixed("constant")),
                     ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => {
                         (false, Mismatch::Fixed("trait"))
                     }
+                    ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
                     _ => (false, Mismatch::Fixed("type")),
                 };
                 let vals = match self.values_str(values) {
diff --git a/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr b/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
index e8826ce4335e7..61b3551182c90 100644
--- a/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
+++ b/src/test/ui/const-generics/defaults/generic-expr-default-concrete.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     Foo::<10, 12>
    |     ^^^^^^^^^^^^^ expected `11`, found `12`
    |
-   = note: expected type `11`
-              found type `12`
+   = note: expected constant `11`
+              found constant `12`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr b/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
index d5a3071b77d15..e83f89a60333f 100644
--- a/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
+++ b/src/test/ui/const-generics/defaults/generic-expr-default-mismatched-types.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     Foo::<N, { N + 2 }>
    |     ^^^^^^^^^^^^^^^^^^^ expected `{ N + 1 }`, found `{ N + 2 }`
    |
-   = note: expected type `{ N + 1 }`
-              found type `{ N + 2 }`
+   = note: expected constant `{ N + 1 }`
+              found constant `{ N + 2 }`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
index 615dc875f67a3..9e1297d5ee4bf 100644
--- a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
@@ -22,8 +22,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as u128 }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as u128 }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as u128 }`
+              found constant `{ O as u128 }`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:20:19
@@ -49,8 +49,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as _ }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as _ }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as _ }`
+              found constant `{ O as u128 }`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:23:5
@@ -58,8 +58,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `12`, found `13`
    |
-   = note: expected type `12`
-              found type `13`
+   = note: expected constant `12`
+              found constant `13`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:25:5
@@ -67,8 +67,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `13`, found `14`
    |
-   = note: expected type `13`
-              found type `14`
+   = note: expected constant `13`
+              found constant `14`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:35:19
@@ -94,8 +94,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as u128 }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as u128 }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as u128 }`
+              found constant `{ O as u128 }`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:38:19
@@ -121,8 +121,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ N as _ }`, found `{ O as u128 }`
    |
-   = note: expected type `{ N as _ }`
-              found type `{ O as u128 }`
+   = note: expected constant `{ N as _ }`
+              found constant `{ O as u128 }`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:41:5
@@ -130,8 +130,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `12`, found `13`
    |
-   = note: expected type `12`
-              found type `13`
+   = note: expected constant `12`
+              found constant `13`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:43:5
@@ -139,8 +139,8 @@ error[E0308]: mismatched types
 LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `13`, found `14`
    |
-   = note: expected type `13`
-              found type `14`
+   = note: expected constant `13`
+              found constant `14`
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr b/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
index 2aeb9b961ffde..83a2f3740b146 100644
--- a/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/different-fn.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     [0; size_of::<Foo<T>>()]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ expected `size_of::<T>()`, found `size_of::<Foo<T>>()`
    |
-   = note: expected type `size_of::<T>()`
-              found type `size_of::<Foo<T>>()`
+   = note: expected constant `size_of::<T>()`
+              found constant `size_of::<Foo<T>>()`
 
 error: unconstrained generic constant
   --> $DIR/different-fn.rs:10:9
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
index f2ae361dc81d9..0742db398c9c4 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-62504.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |         ArrayHolder([0; Self::SIZE])
    |                     ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
    |
-   = note: expected type `X`
-              found type `Self::SIZE`
+   = note: expected constant `X`
+              found constant `Self::SIZE`
 
 error: unconstrained generic constant
   --> $DIR/issue-62504.rs:18:25
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
index d536f6fd1d557..38dfa65e4091f 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |            ^^^^^^^^^^^^^^^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error[E0308]: mismatched types
   --> $DIR/issue-72819-generic-in-const-eval.rs:20:32
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |                                ^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
index 0332e82fe0727..b693023f125a4 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-83765.stderr
@@ -4,8 +4,8 @@ error[E0308]: method not compatible with trait
 LL |     fn size(&self) -> [usize; DIM] {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
+   = note: expected constant `Self::DIM`
+              found constant `DIM`
 
 error: unconstrained generic constant
   --> $DIR/issue-83765.rs:32:24
@@ -26,8 +26,8 @@ error[E0308]: mismatched types
 LL |         self.reference.size()
    |         ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM`
    |
-   = note: expected type `DIM`
-              found type `Self::DIM`
+   = note: expected constant `DIM`
+              found constant `Self::DIM`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
index 808b305c680f3..09bcb0860b71b 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
@@ -54,8 +54,8 @@ error[E0308]: mismatched types
 LL |     writes_to_specific_path(&cap);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `true`, found `{ contains::<T, U>() }`
    |
-   = note: expected type `true`
-              found type `{ contains::<T, U>() }`
+   = note: expected constant `true`
+              found constant `{ contains::<T, U>() }`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-73260.stderr b/src/test/ui/const-generics/issues/issue-73260.stderr
index f1fc50e6e5914..3d1f90271f9a3 100644
--- a/src/test/ui/const-generics/issues/issue-73260.stderr
+++ b/src/test/ui/const-generics/issues/issue-73260.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |            ^^^^^^^^^^^^^^^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error[E0308]: mismatched types
   --> $DIR/issue-73260.rs:16:32
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |     let x: Arr<{usize::MAX}> = Arr {};
    |                                ^^^ expected `false`, found `true`
    |
-   = note: expected type `false`
-              found type `true`
+   = note: expected constant `false`
+              found constant `true`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-79674.stderr b/src/test/ui/const-generics/issues/issue-79674.stderr
index 8c029289cbb0d..344b2c5631064 100644
--- a/src/test/ui/const-generics/issues/issue-79674.stderr
+++ b/src/test/ui/const-generics/issues/issue-79674.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     requires_distinct("str", 12);
    |     ^^^^^^^^^^^^^^^^^ expected `true`, found `false`
    |
-   = note: expected type `true`
-              found type `false`
+   = note: expected constant `true`
+              found constant `false`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
index 486506239ddfd..b6a22df74369a 100644
--- a/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
+++ b/src/test/ui/const-generics/types-mismatch-const-args.full.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |     let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {2u32 + 2u32}, {3u32}> { data: PhantomData };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `4`
    |
-   = note: expected type `2`
-              found type `4`
+   = note: expected constant `2`
+              found constant `4`
 
 error[E0308]: mismatched types
   --> $DIR/types-mismatch-const-args.rs:16:41

From 4ff587263e0a7f2081e2ad5fd3e88460a94adbb5 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 03:13:45 +0000
Subject: [PATCH 22/30] Note binding obligation causes for const equate errors

---
 compiler/rustc_middle/src/traits/mod.rs       |  7 ++++
 .../src/traits/error_reporting/mod.rs         | 19 +++++++--
 .../abstract-const-as-cast-3.stderr           | 40 +++++++++++++++++++
 ...ue-72819-generic-in-const-eval.full.stderr | 14 +++++++
 .../const-generics/issues/issue-73260.stderr  | 16 ++++++++
 .../const-generics/issues/issue-79674.stderr  |  8 ++++
 6 files changed, 101 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 9b82320e556b3..ab7e5ba3a1067 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -469,6 +469,13 @@ impl<'tcx> ObligationCauseCode<'tcx> {
             _ => None,
         }
     }
+
+    pub fn peel_match_impls(&self) -> &Self {
+        match self {
+            MatchImpl(cause, _) => cause.code(),
+            _ => self,
+        }
+    }
 }
 
 // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 70fac83325a9c..1f031d33e0653 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1506,13 +1506,26 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 .emit();
             }
             FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
-                self.report_mismatched_consts(
+                let mut diag = self.report_mismatched_consts(
                     &error.obligation.cause,
                     expected_found.expected,
                     expected_found.found,
                     err.clone(),
-                )
-                .emit();
+                );
+                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
+                if let ObligationCauseCode::BindingObligation(..)
+                | ObligationCauseCode::ItemObligation(..) = code
+                {
+                    self.note_obligation_cause_code(
+                        &mut diag,
+                        &error.obligation.predicate,
+                        error.obligation.param_env,
+                        code,
+                        &mut vec![],
+                        &mut Default::default(),
+                    );
+                }
+                diag.emit();
             }
         }
     }
diff --git a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
index 9e1297d5ee4bf..ada1050d35f35 100644
--- a/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr
@@ -24,6 +24,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |
    = note: expected constant `{ N as u128 }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:20:19
@@ -51,6 +56,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |
    = note: expected constant `{ N as _ }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:23:5
@@ -60,6 +70,11 @@ LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |
    = note: expected constant `12`
               found constant `13`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:25:5
@@ -69,6 +84,11 @@ LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |
    = note: expected constant `13`
               found constant `14`
+note: required by a bound in `use_trait_impl::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:14:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:35:19
@@ -96,6 +116,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
    |
    = note: expected constant `{ N as u128 }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error: unconstrained generic constant
   --> $DIR/abstract-const-as-cast-3.rs:38:19
@@ -123,6 +148,11 @@ LL |     assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
    |
    = note: expected constant `{ N as _ }`
               found constant `{ O as u128 }`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:41:5
@@ -132,6 +162,11 @@ LL |     assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
    |
    = note: expected constant `12`
               found constant `13`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error[E0308]: mismatched types
   --> $DIR/abstract-const-as-cast-3.rs:43:5
@@ -141,6 +176,11 @@ LL |     assert_impl::<HasCastInTraitImpl<14, 13>>();
    |
    = note: expected constant `13`
               found constant `14`
+note: required by a bound in `use_trait_impl_2::assert_impl`
+  --> $DIR/abstract-const-as-cast-3.rs:32:23
+   |
+LL |     fn assert_impl<T: Trait>() {}
+   |                       ^^^^^ required by this bound in `use_trait_impl_2::assert_impl`
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
index 38dfa65e4091f..f2fddfbfbb52a 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-72819-generic-in-const-eval.full.stderr
@@ -6,6 +6,13 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-72819-generic-in-const-eval.rs:8:39
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                       ^^^^^^ required by this bound in `Arr`
 
 error[E0308]: mismatched types
   --> $DIR/issue-72819-generic-in-const-eval.rs:20:32
@@ -15,6 +22,13 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-72819-generic-in-const-eval.rs:8:39
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                       ^^^^^^ required by this bound in `Arr`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-73260.stderr b/src/test/ui/const-generics/issues/issue-73260.stderr
index 3d1f90271f9a3..7670032e5b758 100644
--- a/src/test/ui/const-generics/issues/issue-73260.stderr
+++ b/src/test/ui/const-generics/issues/issue-73260.stderr
@@ -6,6 +6,14 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-73260.rs:6:37
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where
+LL |     Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                     ^^^^^^ required by this bound in `Arr`
 
 error[E0308]: mismatched types
   --> $DIR/issue-73260.rs:16:32
@@ -15,6 +23,14 @@ LL |     let x: Arr<{usize::MAX}> = Arr {};
    |
    = note: expected constant `false`
               found constant `true`
+note: required by a bound in `Arr`
+  --> $DIR/issue-73260.rs:6:37
+   |
+LL | struct Arr<const N: usize>
+   |        --- required by a bound in this
+LL | where
+LL |     Assert::<{N < usize::MAX / 2}>: IsTrue,
+   |                                     ^^^^^^ required by this bound in `Arr`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/issues/issue-79674.stderr b/src/test/ui/const-generics/issues/issue-79674.stderr
index 344b2c5631064..02b48b55f8b38 100644
--- a/src/test/ui/const-generics/issues/issue-79674.stderr
+++ b/src/test/ui/const-generics/issues/issue-79674.stderr
@@ -6,6 +6,14 @@ LL |     requires_distinct("str", 12);
    |
    = note: expected constant `true`
               found constant `false`
+note: required by a bound in `requires_distinct`
+  --> $DIR/issue-79674.rs:23:37
+   |
+LL | fn requires_distinct<A, B>(_a: A, _b: B) where
+   |    ----------------- required by a bound in this
+LL |     A: MiniTypeId, B: MiniTypeId,
+LL |     Lift<{is_same_type::<A, B>()}>: IsFalse {}
+   |                                     ^^^^^^^ required by this bound in `requires_distinct`
 
 error: aborting due to previous error
 

From d464d3a700138b48e13d62846a35165d661e2ea4 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 12 Aug 2022 03:41:57 +0000
Subject: [PATCH 23/30] Add test for #100414

---
 .../generic_const_exprs/obligation-cause.rs   | 24 +++++++++++++++++++
 .../obligation-cause.stderr                   | 20 ++++++++++++++++
 2 files changed, 44 insertions(+)
 create mode 100644 src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
 create mode 100644 src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr

diff --git a/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
new file mode 100644
index 0000000000000..e7c8e4f667d05
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.rs
@@ -0,0 +1,24 @@
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+
+trait True {}
+
+struct Is<const V: bool>;
+
+impl True for Is<true> {}
+
+fn g<T>()
+//~^ NOTE required by a bound in this
+where
+    Is<{ std::mem::size_of::<T>() == 0 }>: True,
+    //~^ NOTE required by a bound in `g`
+    //~| NOTE required by this bound in `g`
+{
+}
+
+fn main() {
+    g::<usize>();
+    //~^ ERROR mismatched types
+    //~| NOTE expected `false`, found `true`
+    //~| NOTE expected constant `false`
+}
diff --git a/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr
new file mode 100644
index 0000000000000..a253ec676f716
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/obligation-cause.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/obligation-cause.rs:20:5
+   |
+LL |     g::<usize>();
+   |     ^^^^^^^^^^ expected `false`, found `true`
+   |
+   = note: expected constant `false`
+              found constant `true`
+note: required by a bound in `g`
+  --> $DIR/obligation-cause.rs:13:44
+   |
+LL | fn g<T>()
+   |    - required by a bound in this
+...
+LL |     Is<{ std::mem::size_of::<T>() == 0 }>: True,
+   |                                            ^^^^ required by this bound in `g`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.

From 8189a4536be3730ecc161c77fc655b08be179b50 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 23 Aug 2022 05:38:18 +0000
Subject: [PATCH 24/30] Use ExprItemObligation and ExprBindingObligation too

---
 .../rustc_trait_selection/src/traits/error_reporting/mod.rs   | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 1f031d33e0653..e4af7022239b8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1514,7 +1514,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                 );
                 let code = error.obligation.cause.code().peel_derives().peel_match_impls();
                 if let ObligationCauseCode::BindingObligation(..)
-                | ObligationCauseCode::ItemObligation(..) = code
+                | ObligationCauseCode::ItemObligation(..)
+                | ObligationCauseCode::ExprBindingObligation(..)
+                | ObligationCauseCode::ExprItemObligation(..) = code
                 {
                     self.note_obligation_cause_code(
                         &mut diag,

From 252c65e9d24f8a6e309c46547bf618a45ab43206 Mon Sep 17 00:00:00 2001
From: Maybe Waffle <waffle.lapkin@gmail.com>
Date: Thu, 25 Aug 2022 14:03:13 +0400
Subject: [PATCH 25/30] Fix clippy tests that trigger `for_loop_over_fallibles`
 lint

---
 .../clippy/tests/ui/for_loop_unfixable.rs     |  1 +
 .../clippy/tests/ui/for_loop_unfixable.stderr |  2 +-
 .../tests/ui/for_loops_over_fallibles.rs      |  1 +
 .../tests/ui/for_loops_over_fallibles.stderr  | 22 +++++++++----------
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
index efcaffce24ea4..203656fa4d6c2 100644
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.rs
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.rs
@@ -8,6 +8,7 @@
     clippy::for_kv_map
 )]
 #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
+#[allow(for_loop_over_fallibles)]
 fn main() {
     let vec = vec![1, 2, 3, 4];
 
diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
index f769b4bdc9411..50a86eaa68f7d 100644
--- a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr
@@ -1,5 +1,5 @@
 error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
-  --> $DIR/for_loop_unfixable.rs:14:15
+  --> $DIR/for_loop_unfixable.rs:15:15
    |
 LL |     for _v in vec.iter().next() {}
    |               ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
index 3390111d0a8fe..3c7733e665356 100644
--- a/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::for_loops_over_fallibles)]
+#![allow(for_loop_over_fallibles)]
 
 fn for_loops_over_fallibles() {
     let option = Some(1);
diff --git a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
index 8c8c022243aeb..5f5a81d24cfde 100644
--- a/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
+++ b/src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr
@@ -1,5 +1,5 @@
 error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:9:14
+  --> $DIR/for_loops_over_fallibles.rs:10:14
    |
 LL |     for x in option {
    |              ^^^^^^
@@ -8,7 +8,7 @@ LL |     for x in option {
    = help: consider replacing `for x in option` with `if let Some(x) = option`
 
 error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:14:14
+  --> $DIR/for_loops_over_fallibles.rs:15:14
    |
 LL |     for x in option.iter() {
    |              ^^^^^^
@@ -16,7 +16,7 @@ LL |     for x in option.iter() {
    = help: consider replacing `for x in option.iter()` with `if let Some(x) = option`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:19:14
+  --> $DIR/for_loops_over_fallibles.rs:20:14
    |
 LL |     for x in result {
    |              ^^^^^^
@@ -24,7 +24,7 @@ LL |     for x in result {
    = help: consider replacing `for x in result` with `if let Ok(x) = result`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:24:14
+  --> $DIR/for_loops_over_fallibles.rs:25:14
    |
 LL |     for x in result.iter_mut() {
    |              ^^^^^^
@@ -32,7 +32,7 @@ LL |     for x in result.iter_mut() {
    = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result`
 
 error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:29:14
+  --> $DIR/for_loops_over_fallibles.rs:30:14
    |
 LL |     for x in result.into_iter() {
    |              ^^^^^^
@@ -40,7 +40,7 @@ LL |     for x in result.into_iter() {
    = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result`
 
 error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:33:14
+  --> $DIR/for_loops_over_fallibles.rs:34:14
    |
 LL |     for x in option.ok_or("x not found") {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     for x in option.ok_or("x not found") {
    = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
 
 error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
-  --> $DIR/for_loops_over_fallibles.rs:39:14
+  --> $DIR/for_loops_over_fallibles.rs:40:14
    |
 LL |     for x in v.iter().next() {
    |              ^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     for x in v.iter().next() {
    = note: `#[deny(clippy::iter_next_loop)]` on by default
 
 error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:44:14
+  --> $DIR/for_loops_over_fallibles.rs:45:14
    |
 LL |     for x in v.iter().next().and(Some(0)) {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     for x in v.iter().next().and(Some(0)) {
    = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
 
 error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
-  --> $DIR/for_loops_over_fallibles.rs:48:14
+  --> $DIR/for_loops_over_fallibles.rs:49:14
    |
 LL |     for x in v.iter().next().ok_or("x not found") {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     for x in v.iter().next().ok_or("x not found") {
    = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
 
 error: this loop never actually loops
-  --> $DIR/for_loops_over_fallibles.rs:60:5
+  --> $DIR/for_loops_over_fallibles.rs:61:5
    |
 LL | /     while let Some(x) = option {
 LL | |         println!("{}", x);
@@ -83,7 +83,7 @@ LL | |     }
    = note: `#[deny(clippy::never_loop)]` on by default
 
 error: this loop never actually loops
-  --> $DIR/for_loops_over_fallibles.rs:66:5
+  --> $DIR/for_loops_over_fallibles.rs:67:5
    |
 LL | /     while let Ok(x) = result {
 LL | |         println!("{}", x);

From c3f568b3312bed99d0e4b95daecd3c9eca1ae895 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 23 Aug 2022 01:58:33 +0000
Subject: [PATCH 26/30] Do not report too many expr field candidates

---
 compiler/rustc_span/src/symbol.rs             |  1 +
 compiler/rustc_typeck/src/check/expr.rs       | 51 ++++++-----
 .../rustc_typeck/src/check/method/suggest.rs  | 88 ++++++++++++-------
 src/test/ui/copy-a-resource.stderr            |  4 -
 src/test/ui/issues/issue-2823.stderr          |  4 -
 src/test/ui/noncopyable-class.stderr          |  8 --
 .../suggestions/too-many-field-suggestions.rs | 29 ++++++
 .../too-many-field-suggestions.stderr         | 44 ++++++++++
 8 files changed, 160 insertions(+), 69 deletions(-)
 create mode 100644 src/test/ui/suggestions/too-many-field-suggestions.rs
 create mode 100644 src/test/ui/suggestions/too-many-field-suggestions.stderr

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 156f53ac48626..43183fd8ec815 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -157,6 +157,7 @@ symbols! {
         BTreeSet,
         BinaryHeap,
         Borrow,
+        BorrowMut,
         Break,
         C,
         CStr,
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 5ff62f36b4527..43aababeea8ae 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -2588,32 +2588,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if let Some((fields, substs)) =
             self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
         {
-            for candidate_field in fields {
-                if let Some(mut field_path) = self.check_for_nested_field_satisfying(
-                    span,
-                    &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
-                    candidate_field,
-                    substs,
-                    vec![],
-                    mod_id,
-                ) {
-                    // field_path includes `field` that we're looking for, so pop it.
+            let candidate_fields: Vec<_> = fields
+                .filter_map(|candidate_field| {
+                    self.check_for_nested_field_satisfying(
+                        span,
+                        &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
+                        candidate_field,
+                        substs,
+                        vec![],
+                        mod_id,
+                    )
+                })
+                .map(|mut field_path| {
                     field_path.pop();
-
-                    let field_path_str = field_path
+                    field_path
                         .iter()
                         .map(|id| id.name.to_ident_string())
                         .collect::<Vec<String>>()
-                        .join(".");
-                    debug!("field_path_str: {:?}", field_path_str);
-
-                    err.span_suggestion_verbose(
-                        field.span.shrink_to_lo(),
-                        "one of the expressions' fields has a field of the same name",
-                        format!("{field_path_str}."),
-                        Applicability::MaybeIncorrect,
-                    );
-                }
+                        .join(".")
+                })
+                .collect::<Vec<_>>();
+
+            let len = candidate_fields.len();
+            if len > 0 {
+                err.span_suggestions(
+                    field.span.shrink_to_lo(),
+                    format!(
+                        "{} of the expressions' fields {} a field of the same name",
+                        if len > 1 { "some" } else { "one" },
+                        if len > 1 { "have" } else { "has" },
+                    ),
+                    candidate_fields.iter().map(|path| format!("{path}.")),
+                    Applicability::MaybeIncorrect,
+                );
             }
         }
         err
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 44c7c148c7533..70e6d33dae16a 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -1338,42 +1338,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         item_name: Ident,
     ) {
         if let SelfSource::MethodCall(expr) = source
-            && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
-            && let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
+        && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
+        && let Some((fields, substs)) =
+            self.get_field_candidates_considering_privacy(span, actual, mod_id)
         {
             let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
-            for candidate_field in fields {
-                if let Some(field_path) = self.check_for_nested_field_satisfying(
-                    span,
-                    &|_, field_ty| {
-                        self.lookup_probe(
-                            span,
-                            item_name,
-                            field_ty,
-                            call_expr,
-                            ProbeScope::AllTraits,
-                        )
-                        .is_ok()
-                    },
-                    candidate_field,
-                    substs,
-                    vec![],
-                    mod_id,
-                ) {
-                    let field_path_str = field_path
+
+            let lang_items = self.tcx.lang_items();
+            let never_mention_traits = [
+                lang_items.clone_trait(),
+                lang_items.deref_trait(),
+                lang_items.deref_mut_trait(),
+                self.tcx.get_diagnostic_item(sym::AsRef),
+                self.tcx.get_diagnostic_item(sym::AsMut),
+                self.tcx.get_diagnostic_item(sym::Borrow),
+                self.tcx.get_diagnostic_item(sym::BorrowMut),
+            ];
+            let candidate_fields: Vec<_> = fields
+                .filter_map(|candidate_field| {
+                    self.check_for_nested_field_satisfying(
+                        span,
+                        &|_, field_ty| {
+                            self.lookup_probe(
+                                span,
+                                item_name,
+                                field_ty,
+                                call_expr,
+                                ProbeScope::TraitsInScope,
+                            )
+                            .map_or(false, |pick| {
+                                !never_mention_traits
+                                    .iter()
+                                    .flatten()
+                                    .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
+                            })
+                        },
+                        candidate_field,
+                        substs,
+                        vec![],
+                        mod_id,
+                    )
+                })
+                .map(|field_path| {
+                    field_path
                         .iter()
                         .map(|id| id.name.to_ident_string())
                         .collect::<Vec<String>>()
-                        .join(".");
-                    debug!("field_path_str: {:?}", field_path_str);
-
-                    err.span_suggestion_verbose(
-                        item_name.span.shrink_to_lo(),
-                        "one of the expressions' fields has a method of the same name",
-                        format!("{field_path_str}."),
-                        Applicability::MaybeIncorrect,
-                    );
-                }
+                        .join(".")
+                })
+                .collect();
+
+            let len = candidate_fields.len();
+            if len > 0 {
+                err.span_suggestions(
+                    item_name.span.shrink_to_lo(),
+                    format!(
+                        "{} of the expressions' fields {} a method of the same name",
+                        if len > 1 { "some" } else { "one" },
+                        if len > 1 { "have" } else { "has" },
+                    ),
+                    candidate_fields.iter().map(|path| format!("{path}.")),
+                    Applicability::MaybeIncorrect,
+                );
             }
         }
     }
diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr
index b92449c6e0aff..128087f1e3755 100644
--- a/src/test/ui/copy-a-resource.stderr
+++ b/src/test/ui/copy-a-resource.stderr
@@ -10,10 +10,6 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let _y = x.i.clone();
-   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr
index 208b340d06410..b5a2b2f55a6d4 100644
--- a/src/test/ui/issues/issue-2823.stderr
+++ b/src/test/ui/issues/issue-2823.stderr
@@ -10,10 +10,6 @@ LL |     let _d = c.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let _d = c.x.clone();
-   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr
index 15e22e946da8a..0c696163a26c5 100644
--- a/src/test/ui/noncopyable-class.stderr
+++ b/src/test/ui/noncopyable-class.stderr
@@ -10,14 +10,6 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let _y = x.i.clone();
-   |                ++
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let _y = x.j.x.clone();
-   |                ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/too-many-field-suggestions.rs b/src/test/ui/suggestions/too-many-field-suggestions.rs
new file mode 100644
index 0000000000000..905f9502cf5be
--- /dev/null
+++ b/src/test/ui/suggestions/too-many-field-suggestions.rs
@@ -0,0 +1,29 @@
+struct Thing {
+    a0: Foo,
+    a1: Foo,
+    a2: Foo,
+    a3: Foo,
+    a4: Foo,
+    a5: Foo,
+    a6: Foo,
+    a7: Foo,
+    a8: Foo,
+    a9: Foo,
+}
+
+struct Foo {
+    field: Field,
+}
+
+struct Field;
+
+impl Foo {
+    fn bar(&self) {}
+}
+
+fn bar(t: Thing) {
+    t.bar(); //~ ERROR no method named `bar` found for struct `Thing`
+    t.field; //~ ERROR no field `field` on type `Thing`
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/too-many-field-suggestions.stderr b/src/test/ui/suggestions/too-many-field-suggestions.stderr
new file mode 100644
index 0000000000000..63ad6fdb16994
--- /dev/null
+++ b/src/test/ui/suggestions/too-many-field-suggestions.stderr
@@ -0,0 +1,44 @@
+error[E0599]: no method named `bar` found for struct `Thing` in the current scope
+  --> $DIR/too-many-field-suggestions.rs:25:7
+   |
+LL | struct Thing {
+   | ------------ method `bar` not found for this struct
+...
+LL |     t.bar();
+   |       ^^^ method not found in `Thing`
+   |
+help: some of the expressions' fields have a method of the same name
+   |
+LL |     t.a0.bar();
+   |       +++
+LL |     t.a1.bar();
+   |       +++
+LL |     t.a2.bar();
+   |       +++
+LL |     t.a3.bar();
+   |       +++
+     and 6 other candidates
+
+error[E0609]: no field `field` on type `Thing`
+  --> $DIR/too-many-field-suggestions.rs:26:7
+   |
+LL |     t.field;
+   |       ^^^^^ unknown field
+   |
+   = note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others
+help: some of the expressions' fields have a field of the same name
+   |
+LL |     t.a0.field;
+   |       +++
+LL |     t.a1.field;
+   |       +++
+LL |     t.a2.field;
+   |       +++
+LL |     t.a3.field;
+   |       +++
+     and 6 other candidates
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0599, E0609.
+For more information about an error, try `rustc --explain E0599`.

From 3e567bcd4f32d413770d464192a2ed6ee636883b Mon Sep 17 00:00:00 2001
From: 5225225 <5225225@mailbox.org>
Date: Mon, 4 Jul 2022 23:57:41 +0100
Subject: [PATCH 27/30] Make invalid-value trigger on uninit primitives

---
 compiler/rustc_lint/src/builtin.rs           |  9 +++++++
 src/test/ui/lint/uninitialized-zeroed.rs     |  6 +++--
 src/test/ui/lint/uninitialized-zeroed.stderr | 27 ++++++++++++++------
 3 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index bd58021f78fc0..5b617fb2e2733 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2475,6 +2475,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                 Char if init == InitKind::Uninit => {
                     Some(("characters must be a valid Unicode codepoint".to_string(), None))
                 }
+                Int(_) | Uint(_) if init == InitKind::Uninit => {
+                    Some(("integers must not be uninitialized".to_string(), None))
+                }
+                Float(_) if init == InitKind::Uninit => {
+                    Some(("floats must not be uninitialized".to_string(), None))
+                }
+                RawPtr(_) if init == InitKind::Uninit => {
+                    Some(("raw pointers must not be uninitialized".to_string(), None))
+                }
                 // Recurse and checks for some compound types.
                 Adt(adt_def, substs) if !adt_def.is_union() => {
                     // First check if this ADT has a layout attribute (like `NonNull` and friends).
diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs
index 5cd323c01db8c..2d1ee6492af55 100644
--- a/src/test/ui/lint/uninitialized-zeroed.rs
+++ b/src/test/ui/lint/uninitialized-zeroed.rs
@@ -100,6 +100,9 @@ fn main() {
         let _val: [bool; 2] = mem::zeroed();
         let _val: [bool; 2] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: i32 = mem::zeroed();
+        let _val: i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         // Transmute-from-0
         let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
         let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
@@ -114,13 +117,12 @@ fn main() {
         let _val: Option<&'static i32> = mem::zeroed();
         let _val: Option<fn()> = mem::zeroed();
         let _val: MaybeUninit<&'static i32> = mem::zeroed();
-        let _val: i32 = mem::zeroed();
         let _val: bool = MaybeUninit::zeroed().assume_init();
         let _val: [bool; 0] = MaybeUninit::uninit().assume_init();
         let _val: [!; 0] = MaybeUninit::zeroed().assume_init();
+
         // Some things that happen to work due to rustc implementation details,
         // but are not guaranteed to keep working.
-        let _val: i32 = mem::uninitialized();
         let _val: OneFruit = mem::uninitialized();
     }
 }
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr
index 88121a1836da4..69fce32153ce5 100644
--- a/src/test/ui/lint/uninitialized-zeroed.stderr
+++ b/src/test/ui/lint/uninitialized-zeroed.stderr
@@ -97,7 +97,7 @@ LL |         let _val: (i32, !) = mem::uninitialized();
    |                              this code causes undefined behavior when executed
    |                              help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
    |
-   = note: the `!` type has no valid value
+   = note: integers must not be uninitialized
 
 error: the type `Void` does not permit zero-initialization
   --> $DIR/uninitialized-zeroed.rs:57:26
@@ -414,8 +414,19 @@ LL |         let _val: [bool; 2] = mem::uninitialized();
    |
    = note: booleans must be either `true` or `false`
 
+error: the type `i32` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:104:25
+   |
+LL |         let _val: i32 = mem::uninitialized();
+   |                         ^^^^^^^^^^^^^^^^^^^^
+   |                         |
+   |                         this code causes undefined behavior when executed
+   |                         help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+   = note: integers must not be uninitialized
+
 error: the type `&i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:104:34
+  --> $DIR/uninitialized-zeroed.rs:107:34
    |
 LL |         let _val: &'static i32 = mem::transmute(0usize);
    |                                  ^^^^^^^^^^^^^^^^^^^^^^
@@ -426,7 +437,7 @@ LL |         let _val: &'static i32 = mem::transmute(0usize);
    = note: references must be non-null
 
 error: the type `&[i32]` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:105:36
+  --> $DIR/uninitialized-zeroed.rs:108:36
    |
 LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -437,7 +448,7 @@ LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    = note: references must be non-null
 
 error: the type `NonZeroU32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:106:32
+  --> $DIR/uninitialized-zeroed.rs:109:32
    |
 LL |         let _val: NonZeroU32 = mem::transmute(0);
    |                                ^^^^^^^^^^^^^^^^^
@@ -448,7 +459,7 @@ LL |         let _val: NonZeroU32 = mem::transmute(0);
    = note: `std::num::NonZeroU32` must be non-null
 
 error: the type `NonNull<i32>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:109:34
+  --> $DIR/uninitialized-zeroed.rs:112:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -459,7 +470,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `NonNull<i32>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:110:34
+  --> $DIR/uninitialized-zeroed.rs:113:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -470,7 +481,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `bool` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:111:26
+  --> $DIR/uninitialized-zeroed.rs:114:26
    |
 LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -480,5 +491,5 @@ LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |
    = note: booleans must be either `true` or `false`
 
-error: aborting due to 39 previous errors
+error: aborting due to 40 previous errors
 

From 57ddb2d02ed61666125602c399e4492a1e8d1f51 Mon Sep 17 00:00:00 2001
From: 5225225 <5225225@mailbox.org>
Date: Tue, 5 Jul 2022 08:03:06 +0100
Subject: [PATCH 28/30] Creating uninitialized integers is UB

---
 library/core/src/mem/maybe_uninit.rs |  3 ---
 library/core/src/mem/mod.rs          | 11 +++--------
 2 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index 997494c769ec7..2490c07679dd0 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -54,9 +54,6 @@ use crate::slice;
 /// // The equivalent code with `MaybeUninit<i32>`:
 /// let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️
 /// ```
-/// (Notice that the rules around uninitialized integers are not finalized yet, but
-/// until they are, it is advisable to avoid them.)
-///
 /// On top of that, remember that most types have additional invariants beyond merely
 /// being considered initialized at the type level. For example, a `1`-initialized [`Vec<T>`]
 /// is considered initialized (under the current implementation; this does not constitute
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 20b2d5e2681c2..20be406c0e5ac 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -665,14 +665,9 @@ pub unsafe fn zeroed<T>() -> T {
 /// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit].
 /// As the [`assume_init` documentation][assume_init] explains,
 /// [the Rust compiler assumes][inv] that values are properly initialized.
-/// As a consequence, calling e.g. `mem::uninitialized::<bool>()` causes immediate
-/// undefined behavior for returning a `bool` that is not definitely either `true`
-/// or `false`. Worse, truly uninitialized memory like what gets returned here
-/// is special in that the compiler knows that it does not have a fixed value.
-/// This makes it undefined behavior to have uninitialized data in a variable even
-/// if that variable has an integer type.
-/// (Notice that the rules around uninitialized integers are not finalized yet, but
-/// until they are, it is advisable to avoid them.)
+///
+/// Therefore, it is immediate undefined behavior to call this function on nearly all types,
+/// including integer types and arrays of integer types, and even if the result is unused.
 ///
 /// [uninit]: MaybeUninit::uninit
 /// [assume_init]: MaybeUninit::assume_init

From 73a30f8d8bb7a2da0e72d9c04c2cfd8db11b35f3 Mon Sep 17 00:00:00 2001
From: 5225225 <5225225@mailbox.org>
Date: Tue, 5 Jul 2022 08:50:42 +0100
Subject: [PATCH 29/30] More tests for invalid_value lint

---
 src/test/ui/lint/uninitialized-zeroed.rs     |  9 ++++
 src/test/ui/lint/uninitialized-zeroed.stderr | 47 +++++++++++++++++---
 2 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/src/test/ui/lint/uninitialized-zeroed.rs b/src/test/ui/lint/uninitialized-zeroed.rs
index 2d1ee6492af55..dae258407ebb0 100644
--- a/src/test/ui/lint/uninitialized-zeroed.rs
+++ b/src/test/ui/lint/uninitialized-zeroed.rs
@@ -103,6 +103,15 @@ fn main() {
         let _val: i32 = mem::zeroed();
         let _val: i32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
 
+        let _val: f32 = mem::zeroed();
+        let _val: f32 = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: *const () = mem::zeroed();
+        let _val: *const () = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
+        let _val: *const [()] = mem::zeroed();
+        let _val: *const [()] = mem::uninitialized(); //~ ERROR: does not permit being left uninitialized
+
         // Transmute-from-0
         let _val: &'static i32 = mem::transmute(0usize); //~ ERROR: does not permit zero-initialization
         let _val: &'static [i32] = mem::transmute((0usize, 0usize)); //~ ERROR: does not permit zero-initialization
diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr
index 69fce32153ce5..b46042e7be43f 100644
--- a/src/test/ui/lint/uninitialized-zeroed.stderr
+++ b/src/test/ui/lint/uninitialized-zeroed.stderr
@@ -425,8 +425,41 @@ LL |         let _val: i32 = mem::uninitialized();
    |
    = note: integers must not be uninitialized
 
+error: the type `f32` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:107:25
+   |
+LL |         let _val: f32 = mem::uninitialized();
+   |                         ^^^^^^^^^^^^^^^^^^^^
+   |                         |
+   |                         this code causes undefined behavior when executed
+   |                         help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+   = note: floats must not be uninitialized
+
+error: the type `*const ()` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:110:31
+   |
+LL |         let _val: *const () = mem::uninitialized();
+   |                               ^^^^^^^^^^^^^^^^^^^^
+   |                               |
+   |                               this code causes undefined behavior when executed
+   |                               help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+   = note: raw pointers must not be uninitialized
+
+error: the type `*const [()]` does not permit being left uninitialized
+  --> $DIR/uninitialized-zeroed.rs:113:33
+   |
+LL |         let _val: *const [()] = mem::uninitialized();
+   |                                 ^^^^^^^^^^^^^^^^^^^^
+   |                                 |
+   |                                 this code causes undefined behavior when executed
+   |                                 help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+   |
+   = note: raw pointers must not be uninitialized
+
 error: the type `&i32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:107:34
+  --> $DIR/uninitialized-zeroed.rs:116:34
    |
 LL |         let _val: &'static i32 = mem::transmute(0usize);
    |                                  ^^^^^^^^^^^^^^^^^^^^^^
@@ -437,7 +470,7 @@ LL |         let _val: &'static i32 = mem::transmute(0usize);
    = note: references must be non-null
 
 error: the type `&[i32]` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:108:36
+  --> $DIR/uninitialized-zeroed.rs:117:36
    |
 LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -448,7 +481,7 @@ LL |         let _val: &'static [i32] = mem::transmute((0usize, 0usize));
    = note: references must be non-null
 
 error: the type `NonZeroU32` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:109:32
+  --> $DIR/uninitialized-zeroed.rs:118:32
    |
 LL |         let _val: NonZeroU32 = mem::transmute(0);
    |                                ^^^^^^^^^^^^^^^^^
@@ -459,7 +492,7 @@ LL |         let _val: NonZeroU32 = mem::transmute(0);
    = note: `std::num::NonZeroU32` must be non-null
 
 error: the type `NonNull<i32>` does not permit zero-initialization
-  --> $DIR/uninitialized-zeroed.rs:112:34
+  --> $DIR/uninitialized-zeroed.rs:121:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -470,7 +503,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::zeroed().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `NonNull<i32>` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:113:34
+  --> $DIR/uninitialized-zeroed.rs:122:34
    |
 LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -481,7 +514,7 @@ LL |         let _val: NonNull<i32> = MaybeUninit::uninit().assume_init();
    = note: `std::ptr::NonNull<i32>` must be non-null
 
 error: the type `bool` does not permit being left uninitialized
-  --> $DIR/uninitialized-zeroed.rs:114:26
+  --> $DIR/uninitialized-zeroed.rs:123:26
    |
 LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -491,5 +524,5 @@ LL |         let _val: bool = MaybeUninit::uninit().assume_init();
    |
    = note: booleans must be either `true` or `false`
 
-error: aborting due to 40 previous errors
+error: aborting due to 43 previous errors
 

From 5e8f95ba7d53f022c658aa9094c470f57e7a6076 Mon Sep 17 00:00:00 2001
From: 5225225 <5225225@mailbox.org>
Date: Tue, 5 Jul 2022 17:50:33 +0100
Subject: [PATCH 30/30] Re-add some justification

---
 library/core/src/mem/mod.rs | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 20be406c0e5ac..d2dd2941d590f 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -666,6 +666,11 @@ pub unsafe fn zeroed<T>() -> T {
 /// As the [`assume_init` documentation][assume_init] explains,
 /// [the Rust compiler assumes][inv] that values are properly initialized.
 ///
+/// Truly uninitialized memory like what gets returned here
+/// is special in that the compiler knows that it does not have a fixed value.
+/// This makes it undefined behavior to have uninitialized data in a variable even
+/// if that variable has an integer type.
+///
 /// Therefore, it is immediate undefined behavior to call this function on nearly all types,
 /// including integer types and arrays of integer types, and even if the result is unused.
 ///