Skip to content

Commit c0894e7

Browse files
committed
typeck/expr: inaccessible private fields
This commit adjusts the missing field diagnostic logic for struct expressions in typeck to improve the diagnostic when the missing fields are inaccessible. Signed-off-by: David Wood <david@davidtw.co>
1 parent a18b34d commit c0894e7

File tree

3 files changed

+103
-33
lines changed

3 files changed

+103
-33
lines changed

compiler/rustc_typeck/src/check/expr.rs

Lines changed: 85 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,42 +1241,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12411241
tcx.sess.span_err(span, "union expressions should have exactly one field");
12421242
}
12431243
} else if check_completeness && !error_happened && !remaining_fields.is_empty() {
1244-
let len = remaining_fields.len();
1245-
1246-
let mut displayable_field_names =
1247-
remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
1248-
1249-
displayable_field_names.sort();
1244+
let no_accessible_remaining_fields = remaining_fields
1245+
.iter()
1246+
.filter(|(_, (_, field))| {
1247+
field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx)
1248+
})
1249+
.next()
1250+
.is_none();
12501251

1251-
let truncated_fields_error = if len <= 3 {
1252-
String::new()
1252+
if no_accessible_remaining_fields {
1253+
self.report_no_accessible_fields(adt_ty, span);
12531254
} else {
1254-
format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
1255-
};
1256-
1257-
let remaining_fields_names = displayable_field_names
1258-
.iter()
1259-
.take(3)
1260-
.map(|n| format!("`{}`", n))
1261-
.collect::<Vec<_>>()
1262-
.join(", ");
1263-
1264-
struct_span_err!(
1265-
tcx.sess,
1266-
span,
1267-
E0063,
1268-
"missing field{} {}{} in initializer of `{}`",
1269-
pluralize!(remaining_fields.len()),
1270-
remaining_fields_names,
1271-
truncated_fields_error,
1272-
adt_ty
1273-
)
1274-
.span_label(
1275-
span,
1276-
format!("missing {}{}", remaining_fields_names, truncated_fields_error),
1277-
)
1278-
.emit();
1255+
self.report_missing_field(adt_ty, span, remaining_fields);
1256+
}
12791257
}
1258+
12801259
error_happened
12811260
}
12821261

@@ -1293,6 +1272,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12931272
}
12941273
}
12951274

1275+
/// Report an error for a struct field expression when there are fields which aren't provided.
1276+
///
1277+
/// ```ignore (diagnostic)
1278+
/// error: missing field `you_can_use_this_field` in initializer of `foo::Foo`
1279+
/// --> src/main.rs:8:5
1280+
/// |
1281+
/// 8 | foo::Foo {};
1282+
/// | ^^^^^^^^ missing `you_can_use_this_field`
1283+
///
1284+
/// error: aborting due to previous error
1285+
/// ```
1286+
fn report_missing_field(
1287+
&self,
1288+
adt_ty: Ty<'tcx>,
1289+
span: Span,
1290+
remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>,
1291+
) {
1292+
let tcx = self.tcx;
1293+
let len = remaining_fields.len();
1294+
1295+
let mut displayable_field_names =
1296+
remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
1297+
1298+
displayable_field_names.sort();
1299+
1300+
let truncated_fields_error = if len <= 3 {
1301+
String::new()
1302+
} else {
1303+
format!(" and {} other field{}", (len - 3), if len - 3 == 1 { "" } else { "s" })
1304+
};
1305+
1306+
let remaining_fields_names = displayable_field_names
1307+
.iter()
1308+
.take(3)
1309+
.map(|n| format!("`{}`", n))
1310+
.collect::<Vec<_>>()
1311+
.join(", ");
1312+
1313+
struct_span_err!(
1314+
tcx.sess,
1315+
span,
1316+
E0063,
1317+
"missing field{} {}{} in initializer of `{}`",
1318+
pluralize!(remaining_fields.len()),
1319+
remaining_fields_names,
1320+
truncated_fields_error,
1321+
adt_ty
1322+
)
1323+
.span_label(span, format!("missing {}{}", remaining_fields_names, truncated_fields_error))
1324+
.emit();
1325+
}
1326+
1327+
/// Report an error for a struct field expression when there are no visible fields.
1328+
///
1329+
/// ```ignore (diagnostic)
1330+
/// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
1331+
/// --> src/main.rs:8:5
1332+
/// |
1333+
/// 8 | foo::Foo {};
1334+
/// | ^^^^^^^^
1335+
///
1336+
/// error: aborting due to previous error
1337+
/// ```
1338+
fn report_no_accessible_fields(&self, adt_ty: Ty<'tcx>, span: Span) {
1339+
self.tcx.sess.span_err(
1340+
span,
1341+
&format!(
1342+
"cannot construct `{}` with struct literal syntax due to inaccessible fields",
1343+
adt_ty,
1344+
),
1345+
);
1346+
}
1347+
12961348
fn report_unknown_field(
12971349
&self,
12981350
ty: Ty<'tcx>,

src/test/ui/issues/issue-76077.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pub mod foo {
2+
pub struct Foo {
3+
you_cant_use_this_field: bool,
4+
}
5+
}
6+
7+
fn main() {
8+
foo::Foo {};
9+
//~^ ERROR cannot construct `Foo` with struct literal syntax due to inaccessible fields
10+
}

src/test/ui/issues/issue-76077.stderr

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: cannot construct `Foo` with struct literal syntax due to inaccessible fields
2+
--> $DIR/issue-76077.rs:8:5
3+
|
4+
LL | foo::Foo {};
5+
| ^^^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)