From 1e7ab0bbd772d4dab2a66a72af20f8b207c252ab Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Mon, 20 Jun 2022 16:29:05 +0900
Subject: [PATCH 1/3] point at private fields in struct literal

---
 Cargo.lock                                    |  1 +
 compiler/rustc_typeck/Cargo.toml              |  1 +
 compiler/rustc_typeck/src/check/expr.rs       | 80 ++++++++++++++++---
 src/test/ui/issues/issue-76077.rs             |  2 +-
 src/test/ui/issues/issue-76077.stderr         |  8 +-
 src/test/ui/privacy/issue-79593.rs            |  2 +-
 src/test/ui/privacy/issue-79593.stderr        |  8 +-
 ...7872-missing-inaccessible-field-literal.rs |  2 +-
 ...-missing-inaccessible-field-literal.stderr |  8 +-
 ...issing-private-fields-in-struct-literal.rs | 18 +++++
 ...ng-private-fields-in-struct-literal.stderr | 23 ++++++
 11 files changed, 134 insertions(+), 19 deletions(-)
 create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.rs
 create mode 100644 src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr

diff --git a/Cargo.lock b/Cargo.lock
index df6f46f26cf0d..cc0f4cc53e656 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4590,6 +4590,7 @@ dependencies = [
 name = "rustc_typeck"
 version = "0.0.0"
 dependencies = [
+ "itertools",
  "rustc_arena",
  "rustc_ast",
  "rustc_attr",
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml
index c08023ee6a70a..b3dd695508094 100644
--- a/compiler/rustc_typeck/Cargo.toml
+++ b/compiler/rustc_typeck/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
+itertools = "0.10.1"
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index dc9d76160c4e9..0a017de80f248 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -23,13 +23,14 @@ use crate::type_error_struct;
 
 use super::suggest_call_constructor;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
+use itertools::{Either, Itertools};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Diagnostic;
-use rustc_errors::EmissionGuarantee;
-use rustc_errors::ErrorGuaranteed;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
+    EmissionGuarantee, ErrorGuaranteed, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -1672,12 +1673,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
             self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys);
         } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() {
-            let inaccessible_remaining_fields = remaining_fields.iter().any(|(_, (_, field))| {
-                !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
-            });
+            debug!(?remaining_fields);
+            let private_fields: Vec<&ty::FieldDef> = variant
+                .fields
+                .iter()
+                .filter(|field| {
+                    !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
+                })
+                .collect();
 
-            if inaccessible_remaining_fields {
-                self.report_inaccessible_fields(adt_ty, span);
+            if !private_fields.is_empty()
+                && tcx
+                    .visibility(variant.def_id)
+                    .is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
+            {
+                self.report_private_fields(adt_ty, span, private_fields, ast_fields);
             } else {
                 self.report_missing_fields(
                     adt_ty,
@@ -1801,7 +1811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Report an error for a struct field expression when there are invisible fields.
     ///
     /// ```text
-    /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    /// error: cannot construct `Foo` with struct literal syntax due to private fields
     ///  --> src/main.rs:8:5
     ///   |
     /// 8 |     foo::Foo {};
@@ -1809,13 +1819,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// error: aborting due to previous error
     /// ```
-    fn report_inaccessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
-        self.tcx.sess.span_err(
+    fn report_private_fields(
+        &self,
+        adt_ty: Ty<'tcx>,
+        span: Span,
+        private_fields: Vec<&ty::FieldDef>,
+        used_fields: &'tcx [hir::ExprField<'tcx>],
+    ) {
+        let field_names = |fields: Vec<Symbol>, len: usize| match &fields
+            .iter()
+            .map(|field| field.to_string())
+            .collect::<Vec<_>>()[..]
+        {
+            _ if len > 6 => String::new(),
+            [name] => format!("`{name}` "),
+            [names @ .., last] => {
+                let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
+                format!("{} and `{last}` ", names.join(", "))
+            }
+            [] => unreachable!(),
+        };
+
+        let mut err = self.tcx.sess.struct_span_err(
             span,
             &format!(
-                "cannot construct `{adt_ty}` with struct literal syntax due to inaccessible fields",
+                "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
             ),
         );
+        let (used_private_fields, remaining_private_fields): (
+            Vec<(Symbol, Span)>,
+            Vec<(Symbol, Span)>,
+        ) = private_fields.iter().partition_map(|field| {
+            match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
+                Some(used_field) => Either::Left((field.name, used_field.span)),
+                None => Either::Right((field.name, self.tcx.def_span(field.did))),
+            }
+        });
+        let remaining_private_fields_len = remaining_private_fields.len();
+        err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field");
+        err.span_note(
+            MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()),
+            format!(
+                "missing field{s} {names}{are} private",
+                s = pluralize!(remaining_private_fields_len),
+                are = pluralize!("is", remaining_private_fields_len),
+                names = field_names(
+                    remaining_private_fields.iter().map(|(name, _)| *name).collect(),
+                    remaining_private_fields_len
+                )
+            ),
+        );
+        err.emit();
     }
 
     fn report_unknown_field(
diff --git a/src/test/ui/issues/issue-76077.rs b/src/test/ui/issues/issue-76077.rs
index 1ecd37de2e14a..2d29093b01b02 100644
--- a/src/test/ui/issues/issue-76077.rs
+++ b/src/test/ui/issues/issue-76077.rs
@@ -6,5 +6,5 @@ pub mod foo {
 
 fn main() {
     foo::Foo {};
-    //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
 }
diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr
index d834ec5e0edd2..c70a928f6475a 100644
--- a/src/test/ui/issues/issue-76077.stderr
+++ b/src/test/ui/issues/issue-76077.stderr
@@ -1,8 +1,14 @@
-error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+error: cannot construct `Foo` with struct literal syntax due to private fields
   --> $DIR/issue-76077.rs:8:5
    |
 LL |     foo::Foo {};
    |     ^^^^^^^^
+   |
+note: missing field `you_cant_use_this_field` is private
+  --> $DIR/issue-76077.rs:3:9
+   |
+LL |         you_cant_use_this_field: bool,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/privacy/issue-79593.rs b/src/test/ui/privacy/issue-79593.rs
index b94278bfdd221..39c222f7c3414 100644
--- a/src/test/ui/privacy/issue-79593.rs
+++ b/src/test/ui/privacy/issue-79593.rs
@@ -16,7 +16,7 @@ mod foo {
 
 fn correct() {
     foo::Pub {};
-    //~^ ERROR cannot construct `Pub` with struct literal syntax due to inaccessible fields
+    //~^ ERROR cannot construct `Pub` with struct literal syntax due to private fields
 }
 
 fn wrong() {
diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr
index b8c7d4f23a28f..435d4cbf73595 100644
--- a/src/test/ui/privacy/issue-79593.stderr
+++ b/src/test/ui/privacy/issue-79593.stderr
@@ -10,11 +10,17 @@ error[E0063]: missing field `y` in initializer of `Enum`
 LL |         Enum::Variant { x: () };
    |         ^^^^^^^^^^^^^ missing `y`
 
-error: cannot construct `Pub` with struct literal syntax due to inaccessible fields
+error: cannot construct `Pub` with struct literal syntax due to private fields
   --> $DIR/issue-79593.rs:18:5
    |
 LL |     foo::Pub {};
    |     ^^^^^^^^
+   |
+note: missing field `private` is private
+  --> $DIR/issue-79593.rs:2:22
+   |
+LL |     pub struct Pub { private: () }
+   |                      ^^^^^^^^^^^
 
 error[E0063]: missing field `y` in initializer of `Enum`
   --> $DIR/issue-79593.rs:23:5
diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs
index 3176144133760..326e958aaa94f 100644
--- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs
+++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs
@@ -7,5 +7,5 @@ pub mod foo {
 
 fn main() {
     foo::Foo {};
-    //~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
+    //~^ ERROR cannot construct `Foo` with struct literal syntax due to private fields
 }
diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
index 81b73c00e8600..2ade7aea57b3d 100644
--- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
+++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
@@ -1,8 +1,14 @@
-error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
+error: cannot construct `Foo` with struct literal syntax due to private fields
   --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:9:5
    |
 LL |     foo::Foo {};
    |     ^^^^^^^^
+   |
+note: missing field `you_cant_use_this_field` is private
+  --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9
+   |
+LL |         you_cant_use_this_field: bool,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs
new file mode 100644
index 0000000000000..9f1560bfb8dfe
--- /dev/null
+++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.rs
@@ -0,0 +1,18 @@
+pub mod m {
+    pub struct S {
+        pub visible: bool,
+        a: (),
+        b: (),
+        c: (),
+        d: (),
+        e: (),
+    }
+}
+
+fn main() {
+    let _ = m::S { //~ ERROR cannot construct `S` with struct literal syntax due to private fields
+        visible: true,
+        a: (),
+        b: (),
+    };
+}
diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
new file mode 100644
index 0000000000000..eb5f460f868e1
--- /dev/null
+++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
@@ -0,0 +1,23 @@
+error: cannot construct `S` with struct literal syntax due to private fields
+  --> $DIR/missing-private-fields-in-struct-literal.rs:13:13
+   |
+LL |     let _ = m::S {
+   |             ^^^^
+LL |         visible: true,
+LL |         a: (),
+   |         ----- private field
+LL |         b: (),
+   |         ----- private field
+   |
+note: missing fields `c`, `d` and `e` are private
+  --> $DIR/missing-private-fields-in-struct-literal.rs:6:9
+   |
+LL |         c: (),
+   |         ^^^^^
+LL |         d: (),
+   |         ^^^^^
+LL |         e: (),
+   |         ^^^^^
+
+error: aborting due to previous error
+

From f847261478de5fa72d7e1d2ec3341e31a794fcaf Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Wed, 22 Jun 2022 12:01:41 +0900
Subject: [PATCH 2/3] stop pointing at definitions of missing fields

---
 Cargo.lock                                    |  1 -
 compiler/rustc_typeck/Cargo.toml              |  1 -
 compiler/rustc_typeck/src/check/expr.rs       | 69 ++++++++-----------
 src/test/ui/issues/issue-76077.stderr         |  6 +-
 src/test/ui/privacy/issue-79593.stderr        |  6 +-
 ...-missing-inaccessible-field-literal.stderr |  6 +-
 ...ng-private-fields-in-struct-literal.stderr | 10 +--
 7 files changed, 33 insertions(+), 66 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index cc0f4cc53e656..df6f46f26cf0d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4590,7 +4590,6 @@ dependencies = [
 name = "rustc_typeck"
 version = "0.0.0"
 dependencies = [
- "itertools",
  "rustc_arena",
  "rustc_ast",
  "rustc_attr",
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml
index b3dd695508094..c08023ee6a70a 100644
--- a/compiler/rustc_typeck/Cargo.toml
+++ b/compiler/rustc_typeck/Cargo.toml
@@ -10,7 +10,6 @@ doctest = false
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
-itertools = "0.10.1"
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 0a017de80f248..e5048fc513217 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -23,13 +23,12 @@ use crate::type_error_struct;
 
 use super::suggest_call_constructor;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
-use itertools::{Either, Itertools};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
-    EmissionGuarantee, ErrorGuaranteed, MultiSpan,
+    EmissionGuarantee, ErrorGuaranteed,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -1682,11 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })
                 .collect();
 
-            if !private_fields.is_empty()
-                && tcx
-                    .visibility(variant.def_id)
-                    .is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
-            {
+            if !private_fields.is_empty() {
                 self.report_private_fields(adt_ty, span, private_fields, ast_fields);
             } else {
                 self.report_missing_fields(
@@ -1826,12 +1821,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         private_fields: Vec<&ty::FieldDef>,
         used_fields: &'tcx [hir::ExprField<'tcx>],
     ) {
-        let field_names = |fields: Vec<Symbol>, len: usize| match &fields
+        let mut err = self.tcx.sess.struct_span_err(
+            span,
+            &format!(
+                "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
+            ),
+        );
+        let (used_private_fields, remaining_private_fields): (
+            Vec<(Symbol, Span, bool)>,
+            Vec<(Symbol, Span, bool)>,
+        ) = private_fields
+            .iter()
+            .map(|field| {
+                match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
+                    Some(used_field) => (field.name, used_field.span, true),
+                    None => (field.name, self.tcx.def_span(field.did), false),
+                }
+            })
+            .partition(|field| field.2);
+        let remaining_private_fields_len = remaining_private_fields.len();
+        let names = match &remaining_private_fields
             .iter()
-            .map(|field| field.to_string())
+            .map(|(name, _, _)| name.to_string())
             .collect::<Vec<_>>()[..]
         {
-            _ if len > 6 => String::new(),
+            _ if remaining_private_fields_len > 6 => String::new(),
             [name] => format!("`{name}` "),
             [names @ .., last] => {
                 let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
@@ -1839,36 +1853,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             [] => unreachable!(),
         };
-
-        let mut err = self.tcx.sess.struct_span_err(
-            span,
-            &format!(
-                "cannot construct `{adt_ty}` with struct literal syntax due to private fields",
-            ),
-        );
-        let (used_private_fields, remaining_private_fields): (
-            Vec<(Symbol, Span)>,
-            Vec<(Symbol, Span)>,
-        ) = private_fields.iter().partition_map(|field| {
-            match used_fields.iter().find(|used_field| field.name == used_field.ident.name) {
-                Some(used_field) => Either::Left((field.name, used_field.span)),
-                None => Either::Right((field.name, self.tcx.def_span(field.did))),
-            }
-        });
-        let remaining_private_fields_len = remaining_private_fields.len();
-        err.span_labels(used_private_fields.iter().map(|(_, span)| *span), "private field");
-        err.span_note(
-            MultiSpan::from_spans(remaining_private_fields.iter().map(|(_, span)| *span).collect()),
-            format!(
-                "missing field{s} {names}{are} private",
-                s = pluralize!(remaining_private_fields_len),
-                are = pluralize!("is", remaining_private_fields_len),
-                names = field_names(
-                    remaining_private_fields.iter().map(|(name, _)| *name).collect(),
-                    remaining_private_fields_len
-                )
-            ),
-        );
+        err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
+        err.note(format!(
+            "... and other private field{s} {names}that were not provided",
+            s = pluralize!(remaining_private_fields_len),
+        ));
         err.emit();
     }
 
diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr
index c70a928f6475a..57f7abe3931a4 100644
--- a/src/test/ui/issues/issue-76077.stderr
+++ b/src/test/ui/issues/issue-76077.stderr
@@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields
 LL |     foo::Foo {};
    |     ^^^^^^^^
    |
-note: missing field `you_cant_use_this_field` is private
-  --> $DIR/issue-76077.rs:3:9
-   |
-LL |         you_cant_use_this_field: bool,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: ... and other private field `you_cant_use_this_field` that were not provided
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr
index 435d4cbf73595..d878e1c023ff1 100644
--- a/src/test/ui/privacy/issue-79593.stderr
+++ b/src/test/ui/privacy/issue-79593.stderr
@@ -16,11 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields
 LL |     foo::Pub {};
    |     ^^^^^^^^
    |
-note: missing field `private` is private
-  --> $DIR/issue-79593.rs:2:22
-   |
-LL |     pub struct Pub { private: () }
-   |                      ^^^^^^^^^^^
+   = note: ... and other private field `private` that were not provided
 
 error[E0063]: missing field `y` in initializer of `Enum`
   --> $DIR/issue-79593.rs:23:5
diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
index 2ade7aea57b3d..fa1c661ef244e 100644
--- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
+++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
@@ -4,11 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields
 LL |     foo::Foo {};
    |     ^^^^^^^^
    |
-note: missing field `you_cant_use_this_field` is private
-  --> $DIR/issue-87872-missing-inaccessible-field-literal.rs:4:9
-   |
-LL |         you_cant_use_this_field: bool,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: ... and other private field `you_cant_use_this_field` that were not provided
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
index eb5f460f868e1..234110f31f79c 100644
--- a/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
+++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
@@ -9,15 +9,7 @@ LL |         a: (),
 LL |         b: (),
    |         ----- private field
    |
-note: missing fields `c`, `d` and `e` are private
-  --> $DIR/missing-private-fields-in-struct-literal.rs:6:9
-   |
-LL |         c: (),
-   |         ^^^^^
-LL |         d: (),
-   |         ^^^^^
-LL |         e: (),
-   |         ^^^^^
+   = note: ... and other private fields `c`, `d` and `e` that were not provided
 
 error: aborting due to previous error
 

From eb86daa1383d5330a18aa4e78270a6ca5b4ea469 Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <takoyaki0316@gmail.com>
Date: Wed, 22 Jun 2022 14:56:40 +0900
Subject: [PATCH 3/3] add "was" to pluralize macro and use it

---
 compiler/rustc_lint_defs/src/lib.rs           |  3 ++
 compiler/rustc_typeck/src/check/expr.rs       | 39 ++++++++++---------
 src/test/ui/issues/issue-76077.stderr         |  2 +-
 src/test/ui/privacy/issue-79593.stderr        |  2 +-
 ...-missing-inaccessible-field-literal.stderr |  2 +-
 5 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index cb1c6f4098767..1cd19c7eaab35 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -26,6 +26,9 @@ macro_rules! pluralize {
     ("is", $x:expr) => {
         if $x == 1 { "is" } else { "are" }
     };
+    ("was", $x:expr) => {
+        if $x == 1 { "was" } else { "were" }
+    };
     ("this", $x:expr) => {
         if $x == 1 { "this" } else { "these" }
     };
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index e5048fc513217..c69de6434539a 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -1839,25 +1839,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             })
             .partition(|field| field.2);
-        let remaining_private_fields_len = remaining_private_fields.len();
-        let names = match &remaining_private_fields
-            .iter()
-            .map(|(name, _, _)| name.to_string())
-            .collect::<Vec<_>>()[..]
-        {
-            _ if remaining_private_fields_len > 6 => String::new(),
-            [name] => format!("`{name}` "),
-            [names @ .., last] => {
-                let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
-                format!("{} and `{last}` ", names.join(", "))
-            }
-            [] => unreachable!(),
-        };
         err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
-        err.note(format!(
-            "... and other private field{s} {names}that were not provided",
-            s = pluralize!(remaining_private_fields_len),
-        ));
+        if !remaining_private_fields.is_empty() {
+            let remaining_private_fields_len = remaining_private_fields.len();
+            let names = match &remaining_private_fields
+                .iter()
+                .map(|(name, _, _)| name.to_string())
+                .collect::<Vec<_>>()[..]
+            {
+                _ if remaining_private_fields_len > 6 => String::new(),
+                [name] => format!("`{name}` "),
+                [names @ .., last] => {
+                    let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
+                    format!("{} and `{last}` ", names.join(", "))
+                }
+                [] => unreachable!(),
+            };
+            err.note(format!(
+                "... and other private field{s} {names}that {were} not provided",
+                s = pluralize!(remaining_private_fields_len),
+                were = pluralize!("was", remaining_private_fields_len),
+            ));
+        }
         err.emit();
     }
 
diff --git a/src/test/ui/issues/issue-76077.stderr b/src/test/ui/issues/issue-76077.stderr
index 57f7abe3931a4..197ca8d5a7b25 100644
--- a/src/test/ui/issues/issue-76077.stderr
+++ b/src/test/ui/issues/issue-76077.stderr
@@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields
 LL |     foo::Foo {};
    |     ^^^^^^^^
    |
-   = note: ... and other private field `you_cant_use_this_field` that were not provided
+   = note: ... and other private field `you_cant_use_this_field` that was not provided
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/privacy/issue-79593.stderr b/src/test/ui/privacy/issue-79593.stderr
index d878e1c023ff1..21ba760ad0bcc 100644
--- a/src/test/ui/privacy/issue-79593.stderr
+++ b/src/test/ui/privacy/issue-79593.stderr
@@ -16,7 +16,7 @@ error: cannot construct `Pub` with struct literal syntax due to private fields
 LL |     foo::Pub {};
    |     ^^^^^^^^
    |
-   = note: ... and other private field `private` that were not provided
+   = note: ... and other private field `private` that was not provided
 
 error[E0063]: missing field `y` in initializer of `Enum`
   --> $DIR/issue-79593.rs:23:5
diff --git a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
index fa1c661ef244e..f0bd3e0ddf768 100644
--- a/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
+++ b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr
@@ -4,7 +4,7 @@ error: cannot construct `Foo` with struct literal syntax due to private fields
 LL |     foo::Foo {};
    |     ^^^^^^^^
    |
-   = note: ... and other private field `you_cant_use_this_field` that were not provided
+   = note: ... and other private field `you_cant_use_this_field` that was not provided
 
 error: aborting due to previous error