about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-06-23 14:39:08 -0700
committerGitHub <noreply@github.com>2022-06-23 14:39:08 -0700
commit41cb5e9439d0e14d1eaa5bee29f7d7faa0e116fb (patch)
tree93de7fad0460bb0c0be502f3389c91b048784306
parent3b68700d0ce297e48c6448f27d156d5f461d64e9 (diff)
parenteb86daa1383d5330a18aa4e78270a6ca5b4ea469 (diff)
downloadrust-41cb5e9439d0e14d1eaa5bee29f7d7faa0e116fb.tar.gz
rust-41cb5e9439d0e14d1eaa5bee29f7d7faa0e116fb.zip
Rollup merge of #98283 - TaKO8Ki:point-at-private-fields-in-struct-literal, r=compiler-errors
Point at private fields in struct literal

closes #95872
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs3
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs72
-rw-r--r--src/test/ui/issues/issue-76077.rs2
-rw-r--r--src/test/ui/issues/issue-76077.stderr4
-rw-r--r--src/test/ui/privacy/issue-79593.rs2
-rw-r--r--src/test/ui/privacy/issue-79593.stderr4
-rw-r--r--src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs2
-rw-r--r--src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.stderr4
-rw-r--r--src/test/ui/typeck/missing-private-fields-in-struct-literal.rs18
-rw-r--r--src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr15
10 files changed, 107 insertions, 19 deletions
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index cb1c6f40987..1cd19c7eaab 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 77d6495f38c..b4476d5c59b 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -26,10 +26,10 @@ use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructEx
 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,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -1701,12 +1701,17 @@ 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() {
+                self.report_private_fields(adt_ty, span, private_fields, ast_fields);
             } else {
                 self.report_missing_fields(
                     adt_ty,
@@ -1830,7 +1835,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 {};
@@ -1838,13 +1843,54 @@ 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 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, 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);
+        err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field");
+        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();
     }
 
     fn report_unknown_field(
diff --git a/src/test/ui/issues/issue-76077.rs b/src/test/ui/issues/issue-76077.rs
index 1ecd37de2e1..2d29093b01b 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 d834ec5e0ed..197ca8d5a7b 100644
--- a/src/test/ui/issues/issue-76077.stderr
+++ b/src/test/ui/issues/issue-76077.stderr
@@ -1,8 +1,10 @@
-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: ... 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.rs b/src/test/ui/privacy/issue-79593.rs
index b94278bfdd2..39c222f7c34 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 b8c7d4f23a2..21ba760ad0b 100644
--- a/src/test/ui/privacy/issue-79593.stderr
+++ b/src/test/ui/privacy/issue-79593.stderr
@@ -10,11 +10,13 @@ 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: ... 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.rs b/src/test/ui/typeck/issue-87872-missing-inaccessible-field-literal.rs
index 31761441337..326e958aaa9 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 81b73c00e86..f0bd3e0ddf7 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,10 @@
-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: ... 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/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 00000000000..9f1560bfb8d
--- /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 00000000000..234110f31f7
--- /dev/null
+++ b/src/test/ui/typeck/missing-private-fields-in-struct-literal.stderr
@@ -0,0 +1,15 @@
+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: ... and other private fields `c`, `d` and `e` that were not provided
+
+error: aborting due to previous error
+