about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTakayuki Maeda <takoyaki0316@gmail.com>2022-06-20 16:29:05 +0900
committerTakayuki Maeda <takoyaki0316@gmail.com>2022-06-20 16:29:05 +0900
commit1e7ab0bbd772d4dab2a66a72af20f8b207c252ab (patch)
tree268838053874c12066f8386629a257ed2c848787
parent349bda2051e94b7aefb33d6541f48f561bf06dbc (diff)
downloadrust-1e7ab0bbd772d4dab2a66a72af20f8b207c252ab.tar.gz
rust-1e7ab0bbd772d4dab2a66a72af20f8b207c252ab.zip
point at private fields in struct literal
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_typeck/Cargo.toml1
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs80
-rw-r--r--src/test/ui/issues/issue-76077.rs2
-rw-r--r--src/test/ui/issues/issue-76077.stderr8
-rw-r--r--src/test/ui/privacy/issue-79593.rs2
-rw-r--r--src/test/ui/privacy/issue-79593.stderr8
-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.stderr8
-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.stderr23
11 files changed, 134 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index df6f46f26cf..cc0f4cc53e6 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 c08023ee6a7..b3dd6955080 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 dc9d76160c4..0a017de80f2 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 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..c70a928f647 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 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..435d4cbf735 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 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..2ade7aea57b 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 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..eb5f460f868
--- /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
+