Skip to content

Commit b39d4c6

Browse files
authored
Rollup merge of rust-lang#100147 - Bryanskiy:private-in-public, r=petrochenkov
optimization of access level table construction Refactoring which was mentioned in rust-lang#87487
2 parents 83d2870 + 0111fb0 commit b39d4c6

File tree

10 files changed

+326
-125
lines changed

10 files changed

+326
-125
lines changed

compiler/rustc_error_messages/locales/en-US/privacy.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ privacy_in_public_interface = {$vis_descr} {$kind} `{$descr}` in public interfac
1111
.label = can't leak {$vis_descr} {$kind}
1212
.visibility_label = `{$descr}` declared as {$vis_descr}
1313
14+
privacy_report_access_level = {$descr}
15+
1416
privacy_from_private_dep_in_public_interface =
1517
{$kind} `{$descr}` from private dependency '{$krate}' in public interface
1618

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
758758
// Internal attributes, Testing:
759759
// ==========================================================================
760760

761+
rustc_attr!(TEST, rustc_access_level, Normal, template!(Word), WarnFollowing),
761762
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
762763
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
763764
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),

compiler/rustc_privacy/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ pub struct InPublicInterface<'a> {
7575
pub vis_span: Span,
7676
}
7777

78+
#[derive(SessionDiagnostic)]
79+
#[diag(privacy::report_access_level)]
80+
pub struct ReportAccessLevel {
81+
#[primary_span]
82+
pub span: Span,
83+
pub descr: String,
84+
}
85+
7886
#[derive(LintDiagnostic)]
7987
#[diag(privacy::from_private_dep_in_public_interface)]
8088
pub struct FromPrivateDependencyInPublicInterface<'a> {

compiler/rustc_privacy/src/lib.rs

+60-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
3333
use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
3434
use rustc_session::lint;
3535
use rustc_span::hygiene::Transparency;
36-
use rustc_span::symbol::{kw, Ident};
36+
use rustc_span::symbol::{kw, sym, Ident};
3737
use rustc_span::Span;
3838

3939
use std::marker::PhantomData;
@@ -42,7 +42,8 @@ use std::{cmp, fmt, mem};
4242

4343
use errors::{
4444
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
45-
InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate,
45+
InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, ReportAccessLevel,
46+
UnnamedItemIsPrivate,
4647
};
4748

4849
////////////////////////////////////////////////////////////////////////////////
@@ -907,6 +908,60 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
907908
}
908909
}
909910

911+
////////////////////////////////////////////////////////////////////////////////
912+
/// Visitor, used for AccessLevels table checking
913+
////////////////////////////////////////////////////////////////////////////////
914+
pub struct TestReachabilityVisitor<'tcx, 'a> {
915+
tcx: TyCtxt<'tcx>,
916+
access_levels: &'a AccessLevels,
917+
}
918+
919+
impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
920+
fn access_level_diagnostic(&mut self, def_id: LocalDefId) {
921+
if self.tcx.has_attr(def_id.to_def_id(), sym::rustc_access_level) {
922+
let access_level = format!("{:?}", self.access_levels.map.get(&def_id));
923+
let span = self.tcx.def_span(def_id.to_def_id());
924+
self.tcx.sess.emit_err(ReportAccessLevel { span, descr: access_level });
925+
}
926+
}
927+
}
928+
929+
impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
930+
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
931+
self.access_level_diagnostic(item.def_id);
932+
933+
match item.kind {
934+
hir::ItemKind::Enum(ref def, _) => {
935+
for variant in def.variants.iter() {
936+
let variant_id = self.tcx.hir().local_def_id(variant.id);
937+
self.access_level_diagnostic(variant_id);
938+
for field in variant.data.fields() {
939+
let def_id = self.tcx.hir().local_def_id(field.hir_id);
940+
self.access_level_diagnostic(def_id);
941+
}
942+
}
943+
}
944+
hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
945+
for field in def.fields() {
946+
let def_id = self.tcx.hir().local_def_id(field.hir_id);
947+
self.access_level_diagnostic(def_id);
948+
}
949+
}
950+
_ => {}
951+
}
952+
}
953+
954+
fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
955+
self.access_level_diagnostic(item.def_id);
956+
}
957+
fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
958+
self.access_level_diagnostic(item.def_id);
959+
}
960+
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
961+
self.access_level_diagnostic(item.def_id);
962+
}
963+
}
964+
910965
//////////////////////////////////////////////////////////////////////////////////////
911966
/// Name privacy visitor, checks privacy and reports violations.
912967
/// Most of name privacy checks are performed during the main resolution phase,
@@ -2045,6 +2100,9 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, (): ()) -> &AccessLevels {
20452100
}
20462101
}
20472102

2103+
let mut check_visitor = TestReachabilityVisitor { tcx, access_levels: &visitor.access_levels };
2104+
tcx.hir().visit_all_item_likes_in_crate(&mut check_visitor);
2105+
20482106
tcx.arena.alloc(visitor.access_levels)
20492107
}
20502108

+53-105
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
1+
use crate::imports::ImportKind;
2+
use crate::NameBinding;
3+
use crate::NameBindingKind;
4+
use crate::Resolver;
15
use rustc_ast::ast;
26
use rustc_ast::visit;
37
use rustc_ast::visit::Visitor;
48
use rustc_ast::Crate;
59
use rustc_ast::EnumDef;
6-
use rustc_ast::ForeignMod;
710
use rustc_ast::NodeId;
811
use rustc_hir::def_id::LocalDefId;
912
use rustc_hir::def_id::CRATE_DEF_ID;
1013
use rustc_middle::middle::privacy::AccessLevel;
11-
use rustc_middle::ty::Visibility;
14+
use rustc_middle::ty::DefIdTree;
1215
use rustc_span::sym;
1316

14-
use crate::imports::ImportKind;
15-
use crate::BindingKey;
16-
use crate::NameBinding;
17-
use crate::NameBindingKind;
18-
use crate::Resolver;
19-
2017
pub struct AccessLevelsVisitor<'r, 'a> {
2118
r: &'r mut Resolver<'a>,
22-
prev_level: Option<AccessLevel>,
2319
changed: bool,
2420
}
2521

@@ -28,11 +24,10 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
2824
/// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
2925
/// need access to a TyCtxt for that.
3026
pub fn compute_access_levels<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
31-
let mut visitor =
32-
AccessLevelsVisitor { r, changed: false, prev_level: Some(AccessLevel::Public) };
27+
let mut visitor = AccessLevelsVisitor { r, changed: false };
3328

3429
visitor.set_access_level_def_id(CRATE_DEF_ID, Some(AccessLevel::Public));
35-
visitor.set_exports_access_level(CRATE_DEF_ID);
30+
visitor.set_bindings_access_level(CRATE_DEF_ID);
3631

3732
while visitor.changed {
3833
visitor.reset();
@@ -44,15 +39,17 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
4439

4540
fn reset(&mut self) {
4641
self.changed = false;
47-
self.prev_level = Some(AccessLevel::Public);
4842
}
4943

50-
/// Update the access level of the exports of the given module accordingly. The module access
44+
/// Update the access level of the bindings in the given module accordingly. The module access
5145
/// level has to be Exported or Public.
5246
/// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level).
53-
fn set_exports_access_level(&mut self, module_id: LocalDefId) {
47+
fn set_bindings_access_level(&mut self, module_id: LocalDefId) {
5448
assert!(self.r.module_map.contains_key(&&module_id.to_def_id()));
55-
49+
let module_level = self.r.access_levels.map.get(&module_id).copied();
50+
if !module_level.is_some() {
51+
return;
52+
}
5653
// Set the given binding access level to `AccessLevel::Public` and
5754
// sets the rest of the `use` chain to `AccessLevel::Exported` until
5855
// we hit the actual exported item.
@@ -72,28 +69,20 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
7269
}
7370
};
7471

75-
let module_level = self.r.access_levels.map.get(&module_id).copied();
76-
assert!(module_level >= Some(AccessLevel::Exported));
77-
78-
if let Some(exports) = self.r.reexport_map.get(&module_id) {
79-
let pub_exports = exports
80-
.iter()
81-
.filter(|ex| ex.vis == Visibility::Public)
82-
.cloned()
83-
.collect::<Vec<_>>();
84-
85-
let module = self.r.get_module(module_id.to_def_id()).unwrap();
86-
for export in pub_exports.into_iter() {
87-
if let Some(export_def_id) = export.res.opt_def_id().and_then(|id| id.as_local()) {
88-
self.set_access_level_def_id(export_def_id, Some(AccessLevel::Exported));
89-
}
90-
91-
if let Some(ns) = export.res.ns() {
92-
let key = BindingKey { ident: export.ident, ns, disambiguator: 0 };
93-
let name_res = self.r.resolution(module, key);
94-
if let Some(binding) = name_res.borrow().binding() {
95-
set_import_binding_access_level(self, binding, module_level)
96-
}
72+
let module = self.r.get_module(module_id.to_def_id()).unwrap();
73+
let resolutions = self.r.resolutions(module);
74+
75+
for (.., name_resolution) in resolutions.borrow().iter() {
76+
if let Some(binding) = name_resolution.borrow().binding() && binding.vis.is_public() && !binding.is_ambiguity() {
77+
let access_level = match binding.is_import() {
78+
true => {
79+
set_import_binding_access_level(self, binding, module_level);
80+
Some(AccessLevel::Exported)
81+
},
82+
false => module_level,
83+
};
84+
if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
85+
self.set_access_level_def_id(def_id, access_level);
9786
}
9887
}
9988
}
@@ -127,97 +116,59 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
127116

128117
impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
129118
fn visit_item(&mut self, item: &'ast ast::Item) {
130-
let inherited_item_level = match item.kind {
119+
let def_id = self.r.local_def_id(item.id);
120+
// Set access level of nested items.
121+
// If it's a mod, also make the visitor walk all of its items
122+
match item.kind {
131123
// Resolved in rustc_privacy when types are available
132124
ast::ItemKind::Impl(..) => return,
133125

134-
// Only exported `macro_rules!` items are public, but they always are
135-
ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
136-
let is_macro_export =
137-
item.attrs.iter().any(|attr| attr.has_name(sym::macro_export));
138-
if is_macro_export { Some(AccessLevel::Public) } else { None }
139-
}
140-
141-
// Foreign modules inherit level from parents.
142-
ast::ItemKind::ForeignMod(..) => self.prev_level,
143-
144-
// Other `pub` items inherit levels from parents.
145-
ast::ItemKind::ExternCrate(..)
146-
| ast::ItemKind::Use(..)
147-
| ast::ItemKind::Static(..)
148-
| ast::ItemKind::Const(..)
149-
| ast::ItemKind::Fn(..)
150-
| ast::ItemKind::Mod(..)
151-
| ast::ItemKind::GlobalAsm(..)
152-
| ast::ItemKind::TyAlias(..)
153-
| ast::ItemKind::Enum(..)
154-
| ast::ItemKind::Struct(..)
155-
| ast::ItemKind::Union(..)
156-
| ast::ItemKind::Trait(..)
157-
| ast::ItemKind::TraitAlias(..)
158-
| ast::ItemKind::MacroDef(..) => {
159-
if item.vis.kind.is_pub() {
160-
self.prev_level
161-
} else {
162-
None
163-
}
164-
}
165-
166126
// Should be unreachable at this stage
167127
ast::ItemKind::MacCall(..) => panic!(
168128
"ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
169129
),
170-
};
171130

172-
let access_level = self.set_access_level(item.id, inherited_item_level);
131+
// Foreign modules inherit level from parents.
132+
ast::ItemKind::ForeignMod(..) => {
133+
let parent_level =
134+
self.r.access_levels.map.get(&self.r.local_parent(def_id)).copied();
135+
self.set_access_level(item.id, parent_level);
136+
}
173137

174-
// Set access level of nested items.
175-
// If it's a mod, also make the visitor walk all of its items
176-
match item.kind {
177-
ast::ItemKind::Mod(..) => {
178-
if access_level.is_some() {
179-
self.set_exports_access_level(self.r.local_def_id(item.id));
138+
// Only exported `macro_rules!` items are public, but they always are
139+
ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
140+
if item.attrs.iter().any(|attr| attr.has_name(sym::macro_export)) {
141+
self.set_access_level(item.id, Some(AccessLevel::Public));
180142
}
143+
}
181144

182-
let orig_level = std::mem::replace(&mut self.prev_level, access_level);
145+
ast::ItemKind::Mod(..) => {
146+
self.set_bindings_access_level(def_id);
183147
visit::walk_item(self, item);
184-
self.prev_level = orig_level;
185148
}
186149

187-
ast::ItemKind::ForeignMod(ForeignMod { ref items, .. }) => {
188-
for nested in items {
189-
if nested.vis.kind.is_pub() {
190-
self.set_access_level(nested.id, access_level);
191-
}
192-
}
193-
}
194150
ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
151+
self.set_bindings_access_level(def_id);
195152
for variant in variants {
196-
let variant_level = self.set_access_level(variant.id, access_level);
197-
if let Some(ctor_id) = variant.data.ctor_id() {
198-
self.set_access_level(ctor_id, access_level);
199-
}
200-
153+
let variant_def_id = self.r.local_def_id(variant.id);
154+
let variant_level = self.r.access_levels.map.get(&variant_def_id).copied();
201155
for field in variant.data.fields() {
202156
self.set_access_level(field.id, variant_level);
203157
}
204158
}
205159
}
206-
ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
207-
if let Some(ctor_id) = def.ctor_id() {
208-
self.set_access_level(ctor_id, access_level);
209-
}
210160

161+
ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
162+
let inherited_level = self.r.access_levels.map.get(&def_id).copied();
211163
for field in def.fields() {
212164
if field.vis.kind.is_pub() {
213-
self.set_access_level(field.id, access_level);
165+
self.set_access_level(field.id, inherited_level);
214166
}
215167
}
216168
}
217-
ast::ItemKind::Trait(ref trait_kind) => {
218-
for nested in trait_kind.items.iter() {
219-
self.set_access_level(nested.id, access_level);
220-
}
169+
170+
ast::ItemKind::Trait(..) => {
171+
self.set_bindings_access_level(def_id);
221172
}
222173

223174
ast::ItemKind::ExternCrate(..)
@@ -229,9 +180,6 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
229180
| ast::ItemKind::TraitAlias(..)
230181
| ast::ItemKind::MacroDef(..)
231182
| ast::ItemKind::Fn(..) => return,
232-
233-
// Unreachable kinds
234-
ast::ItemKind::Impl(..) | ast::ItemKind::MacCall(..) => unreachable!(),
235183
}
236184
}
237185
}

compiler/rustc_resolve/src/imports.rs

+9-18
Original file line numberDiff line numberDiff line change
@@ -1133,24 +1133,15 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
11331133
if let Some(def_id) = module.opt_def_id() {
11341134
let mut reexports = Vec::new();
11351135

1136-
module.for_each_child(self.r, |_, ident, _, binding| {
1137-
// FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules`
1138-
// into the crate root to actual `NameBindingKind::Import`.
1139-
if binding.is_import()
1140-
|| matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
1141-
{
1142-
let res = binding.res().expect_non_local();
1143-
// Ambiguous imports are treated as errors at this point and are
1144-
// not exposed to other crates (see #36837 for more details).
1145-
if res != def::Res::Err && !binding.is_ambiguity() {
1146-
reexports.push(ModChild {
1147-
ident,
1148-
res,
1149-
vis: binding.vis,
1150-
span: binding.span,
1151-
macro_rules: false,
1152-
});
1153-
}
1136+
module.for_each_child(self.r, |this, ident, _, binding| {
1137+
if let Some(res) = this.is_reexport(binding) {
1138+
reexports.push(ModChild {
1139+
ident,
1140+
res,
1141+
vis: binding.vis,
1142+
span: binding.span,
1143+
macro_rules: false,
1144+
});
11541145
}
11551146
});
11561147

0 commit comments

Comments
 (0)