Skip to content

Commit 7ce29fa

Browse files
committed
Change privacy checks, particularly for tuple structs
Move tuple struct privacy checks away from `resolve` in order to evaluate more explicitely point at private tuple struct fields when they are the cause of the tuple struct being inaccessible. For structs that are inaccessible, point at the definition span. Reword privacy messages to be more specific about the ADT kind name. Group private field errors per struct.
1 parent 710ddc1 commit 7ce29fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1598
-885
lines changed

src/librustc/ty/mod.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -2242,13 +2242,20 @@ impl<'a, 'gcx, 'tcx> AdtDef {
22422242
.0
22432243
}
22442244

2245-
pub fn variant_of_def(&self, def: Def) -> &VariantDef {
2245+
pub fn opt_variant_of_def(&self, def: Def) -> Option<&VariantDef> {
22462246
match def {
2247-
Def::Variant(vid) | Def::VariantCtor(vid, ..) => self.variant_with_id(vid),
2247+
Def::Variant(vid) | Def::VariantCtor(vid, ..) => Some(self.variant_with_id(vid)),
22482248
Def::Struct(..) | Def::StructCtor(..) | Def::Union(..) |
22492249
Def::TyAlias(..) | Def::AssociatedTy(..) | Def::SelfTy(..) |
2250-
Def::SelfCtor(..) => self.non_enum_variant(),
2251-
_ => bug!("unexpected def {:?} in variant_of_def", def)
2250+
Def::SelfCtor(..) => Some(self.non_enum_variant()),
2251+
_ => None,
2252+
}
2253+
}
2254+
2255+
pub fn variant_of_def(&self, def: Def) -> &VariantDef {
2256+
match self.opt_variant_of_def(def) {
2257+
Some(vd) => vd,
2258+
None => bug!("unexpected def {:?} in variant_of_def", def),
22522259
}
22532260
}
22542261

src/librustc_privacy/lib.rs

+184-20
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,10 @@ impl<'a, 'tcx, V> TypeVisitor<'tcx> for DefIdVisitorSkeleton<'_, 'a, 'tcx, V>
222222
}
223223
}
224224

225-
fn def_id_visibility<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
226-
-> (ty::Visibility, Span, &'static str) {
225+
fn def_id_visibility<'a, 'tcx>(
226+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
227+
def_id: DefId,
228+
) -> (ty::Visibility, Span, &'static str) {
227229
match tcx.hir().as_local_node_id(def_id) {
228230
Some(node_id) => {
229231
let vis = match tcx.hir().get(node_id) {
@@ -799,22 +801,71 @@ struct NamePrivacyVisitor<'a, 'tcx: 'a> {
799801
tables: &'a ty::TypeckTables<'tcx>,
800802
current_item: ast::NodeId,
801803
empty_tables: &'a ty::TypeckTables<'tcx>,
804+
reported_tuple_structs: FxHashSet<Span>,
802805
}
803806

804807
impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
805808
// Checks that a field in a struct constructor (expression or pattern) is accessible.
806-
fn check_field(&mut self,
807-
use_ctxt: Span, // syntax context of the field name at the use site
808-
span: Span, // span of the field pattern, e.g., `x: 0`
809-
def: &'tcx ty::AdtDef, // definition of the struct or enum
810-
field: &'tcx ty::FieldDef) { // definition of the field
809+
fn check_field(
810+
&mut self,
811+
use_ctxt: Span, // syntax context of the field name at the use site
812+
span: Span, // span of the field pattern, e.g., `x: 0`
813+
def: &'tcx ty::AdtDef, // definition of the struct or enum
814+
field: &'tcx ty::FieldDef, // definition of the field
815+
) -> Option<(String /* field name */, Span)> {
811816
let ident = Ident::new(keywords::Invalid.name(), use_ctxt);
812817
let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1;
813818
if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) {
814-
struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private",
815-
field.ident, def.variant_descr(), self.tcx.item_path_str(def.did))
816-
.span_label(span, format!("field `{}` is private", field.ident))
817-
.emit();
819+
return Some((field.ident.to_string(), span));
820+
}
821+
None
822+
}
823+
824+
/// If appropriate, construct a privacy error pointing at all the fields of a literal struct
825+
/// that are private both when constructing an instance or destructuring a pattern.
826+
fn emit_field_checks(
827+
&mut self,
828+
// d: Def,
829+
def: &'tcx ty::AdtDef, // definition of the struct or enum
830+
span: Span, // struct span at use site
831+
fields: Vec<(String, Span)>, // inaccessible ADT fields
832+
action: &str, // "built" or "destructured" depending of where this happened
833+
) {
834+
let item_path = self.tcx.item_path_str(def.did);
835+
836+
if !fields.is_empty() {
837+
self.reported_tuple_structs.insert(span);
838+
let mut err = struct_span_err!(
839+
self.tcx.sess,
840+
fields.iter().map(|(_, sp)| *sp).collect::<Vec<Span>>(),
841+
E0451,
842+
"field{} of {} `{}` {} private",
843+
if fields.len() == 1 {
844+
format!(" `{}`", fields[0].0)
845+
} else {
846+
"s".to_owned()
847+
},
848+
def.variant_descr(),
849+
item_path,
850+
if fields.len() == 1 {
851+
"is"
852+
} else {
853+
"are"
854+
},
855+
);
856+
err.span_label(span, format!(
857+
"`{}` cannot be {} due to private field{}",
858+
item_path,
859+
action,
860+
if fields.len() == 1 { "" } else { "s" },
861+
));
862+
for (_field_name, field) in fields {
863+
err.span_label(field, "private field");
864+
}
865+
866+
// Point at definition
867+
err.span_label(self.tcx.def_span(def.did), format!("`{}` defined here", item_path));
868+
err.emit();
818869
}
819870
}
820871
}
@@ -867,6 +918,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
867918
let def = self.tables.qpath_def(qpath, expr.hir_id);
868919
let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap();
869920
let variant = adt.variant_of_def(def);
921+
let mut field_errors = vec![];
870922
if let Some(ref base) = *base {
871923
// If the expression uses FRU we need to make sure all the unmentioned fields
872924
// are checked for privacy (RFC 736). Rather than computing the set of
@@ -879,13 +931,48 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
879931
Some(field) => (field.ident.span, field.span),
880932
None => (base.span, base.span),
881933
};
882-
self.check_field(use_ctxt, span, adt, variant_field);
934+
if let Some(err) = self.check_field(use_ctxt, span, adt, variant_field) {
935+
field_errors.push(err);
936+
}
883937
}
884938
} else {
885939
for field in fields {
886940
let use_ctxt = field.ident.span;
887941
let index = self.tcx.field_index(field.id, self.tables);
888-
self.check_field(use_ctxt, field.span, adt, &variant.fields[index]);
942+
if let Some(err) = self.check_field(
943+
use_ctxt,
944+
field.span,
945+
adt,
946+
&variant.fields[index],
947+
) {
948+
field_errors.push(err);
949+
}
950+
}
951+
}
952+
self.emit_field_checks(adt, expr.span, field_errors, "built");
953+
}
954+
hir::ExprKind::Call(ref path, ref fields) => {
955+
if let hir::ExprKind::Path(qpath) = &path.node {
956+
let def = self.tables.qpath_def(qpath, path.hir_id);
957+
if let Some(_) = def.opt_def_id() {
958+
if let Some(adt) = self.tables.expr_ty(expr).ty_adt_def() {
959+
if let Some(variant) = adt.opt_variant_of_def(def) {
960+
let mut field_errors = vec![];
961+
for (idx, field) in variant.fields.iter().enumerate() {
962+
let use_ctxt = fields.get(idx).map(|f| f.span)
963+
.unwrap_or(path.span);
964+
if let Some(err) = self.check_field(
965+
use_ctxt,
966+
use_ctxt,
967+
adt,
968+
&field,
969+
) {
970+
field_errors.push(err);
971+
}
972+
}
973+
self.emit_field_checks(adt, path.span, field_errors, "built");
974+
}
975+
}
889976
}
890977
}
891978
}
@@ -901,11 +988,39 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
901988
let def = self.tables.qpath_def(qpath, pat.hir_id);
902989
let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
903990
let variant = adt.variant_of_def(def);
991+
let mut field_errors = vec![];
904992
for field in fields {
905993
let use_ctxt = field.node.ident.span;
906994
let index = self.tcx.field_index(field.node.id, self.tables);
907-
self.check_field(use_ctxt, field.span, adt, &variant.fields[index]);
995+
if let Some(err) = self.check_field(
996+
use_ctxt,
997+
field.span,
998+
adt,
999+
&variant.fields[index],
1000+
) {
1001+
field_errors.push(err);
1002+
}
1003+
}
1004+
self.emit_field_checks(adt, pat.span, field_errors, "destructured");
1005+
}
1006+
PatKind::TupleStruct(ref qpath, ref patterns, ..) => {
1007+
let def = self.tables.qpath_def(qpath, pat.hir_id);
1008+
let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
1009+
let variant = adt.variant_of_def(def);
1010+
let mut field_errors = vec![];
1011+
for (vf_index, variant_field) in variant.fields.iter().enumerate() {
1012+
if let Some(pat) = patterns.get(vf_index) {
1013+
if let Some(err) = self.check_field(
1014+
pat.span,
1015+
pat.span,
1016+
adt,
1017+
variant_field,
1018+
) {
1019+
field_errors.push(err);
1020+
}
1021+
}
9081022
}
1023+
self.emit_field_checks(adt, pat.span, field_errors, "destructured");
9091024
}
9101025
_ => {}
9111026
}
@@ -927,11 +1042,13 @@ struct TypePrivacyVisitor<'a, 'tcx: 'a> {
9271042
in_body: bool,
9281043
span: Span,
9291044
empty_tables: &'a ty::TypeckTables<'tcx>,
1045+
reported_tuple_structs: FxHashSet<Span>,
9301046
}
9311047

9321048
impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
9331049
fn item_is_accessible(&self, did: DefId) -> bool {
934-
def_id_visibility(self.tcx, did).0.is_accessible_from(self.current_item, self.tcx)
1050+
let (a, ..) = def_id_visibility(self.tcx, did);
1051+
a.is_accessible_from(self.current_item, self.tcx)
9351052
}
9361053

9371054
// Take node-id of an expression or pattern and check its type for privacy.
@@ -951,11 +1068,33 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
9511068
}
9521069

9531070
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
954-
let is_error = !self.item_is_accessible(def_id);
955-
if is_error {
956-
self.tcx.sess.span_err(self.span, &format!("{} `{}` is private", kind, descr));
1071+
let is_ok = self.item_is_accessible(def_id);
1072+
if !is_ok {
1073+
match self.tcx.hir().as_local_node_id(def_id) {
1074+
Some(node_id) => {
1075+
match self.tcx.hir().get(node_id) {
1076+
Node::StructCtor(hir::VariantData::Tuple(..)) => {
1077+
// Ignore tuple structs, as they are handled in `visit_path`
1078+
return false;
1079+
}
1080+
_ => {}
1081+
}
1082+
}
1083+
_ => {}
1084+
}
1085+
let msg = if let Some(def) = self.tcx.describe_def(def_id) {
1086+
format!("{} `{}` is private", def.kind_name(), self.tcx.item_path_str(def_id))
1087+
} else {
1088+
format!("{} `{}` is private", kind, descr)
1089+
};
1090+
if !self.reported_tuple_structs.iter().any(|sp| sp.overlaps(self.span)) {
1091+
self.tcx.sess
1092+
.struct_span_err(self.span, &msg)
1093+
.span_label(self.span, "private")
1094+
.emit();
1095+
}
9571096
}
958-
is_error
1097+
!is_ok
9591098
}
9601099
}
9611100

@@ -1079,14 +1218,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
10791218
hir::QPath::TypeRelative(_, ref segment) => segment.ident.to_string(),
10801219
};
10811220
let msg = format!("{} `{}` is private", def.kind_name(), name);
1082-
self.tcx.sess.span_err(span, &msg);
1221+
let label = format!("{} not accessible from here", def.kind_name());
1222+
self.tcx.sess.struct_span_err(span, &msg)
1223+
.span_label(span, label)
1224+
.emit();
10831225
return;
10841226
}
10851227
}
10861228

10871229
intravisit::walk_qpath(self, qpath, id, span);
10881230
}
10891231

1232+
// Prohibit access to tuple structs that are either unreachable *or* have private fields.
1233+
fn visit_path(&mut self, path: &'tcx hir::Path, _id: hir::HirId) {
1234+
// We handle tuple struct visibility here to only complain about bare paths referencing an
1235+
// unreachable tuple struct or one that has private fields.
1236+
if let Def::StructCtor(def_id, hir::def::CtorKind::Fn) = path.def {
1237+
if !self.item_is_accessible(def_id) &&
1238+
// only report if this is a bare path, not part of a tuple struct literal
1239+
!self.reported_tuple_structs.iter().any(|sp| sp.overlaps(path.span))
1240+
{
1241+
let kind_name = path.def.kind_name();
1242+
let sp = path.span;
1243+
let msg = format!("{} `{}` is private", kind_name, path);
1244+
let label = format!("{} not accesssible from here", kind_name);
1245+
self.tcx.sess.struct_span_err(sp, &msg)
1246+
.span_label(sp, label)
1247+
.emit();
1248+
}
1249+
}
1250+
}
1251+
10901252
// Check types of patterns.
10911253
fn visit_pat(&mut self, pattern: &'tcx hir::Pat) {
10921254
if self.check_expr_pat_type(pattern.hir_id, pattern.span) {
@@ -1770,6 +1932,7 @@ fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
17701932
tables: &empty_tables,
17711933
current_item: DUMMY_NODE_ID,
17721934
empty_tables: &empty_tables,
1935+
reported_tuple_structs: FxHashSet::default(),
17731936
};
17741937
let (module, span, node_id) = tcx.hir().get_module(module_def_id);
17751938
intravisit::walk_mod(&mut visitor, module, node_id);
@@ -1783,6 +1946,7 @@ fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
17831946
in_body: false,
17841947
span,
17851948
empty_tables: &empty_tables,
1949+
reported_tuple_structs: visitor.reported_tuple_structs,
17861950
};
17871951
intravisit::walk_mod(&mut visitor, module, node_id);
17881952
}

src/librustc_resolve/lib.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -5046,8 +5046,24 @@ impl<'a> Resolver<'a> {
50465046
let mut reported_spans = FxHashSet::default();
50475047
for &PrivacyError(dedup_span, ident, binding) in &self.privacy_errors {
50485048
if reported_spans.insert(dedup_span) {
5049-
span_err!(self.session, ident.span, E0603, "{} `{}` is private",
5050-
binding.descr(), ident.name);
5049+
if let NameBindingKind::Def(
5050+
Def::StructCtor(_def_id, CtorKind::Fn), false,
5051+
) = binding.kind {
5052+
// For tuple structs we want to be clearer about the reason for the ctor being
5053+
// private, as we'd want to identify whether the visibility failure is due to a
5054+
// non-accessible field. Because of this, ignore them at the resolve time and
5055+
// defer to privacy checking step.
5056+
} else {
5057+
let mut err = struct_span_err!(
5058+
self.session,
5059+
ident.span,
5060+
E0603,
5061+
"{} `{}` is private",
5062+
binding.descr(),
5063+
ident.name,
5064+
);
5065+
err.emit();
5066+
}
50515067
}
50525068
}
50535069
}

src/librustc_resolve/resolve_imports.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,11 @@ impl<'a> Resolver<'a> {
268268
}
269269
}
270270

271-
if !self.is_accessible(binding.vis) &&
271+
let is_accessible = self.is_accessible(binding.vis);
272+
if !is_accessible &&
272273
// Remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
273-
!(self.last_import_segment && binding.is_extern_crate()) {
274+
!(self.last_import_segment && binding.is_extern_crate())
275+
{
274276
self.privacy_errors.push(PrivacyError(path_span, ident, binding));
275277
}
276278

0 commit comments

Comments
 (0)