• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::{NameBinding, NameBindingKind, Resolver};
2 use rustc_ast::ast;
3 use rustc_ast::visit;
4 use rustc_ast::visit::Visitor;
5 use rustc_ast::Crate;
6 use rustc_ast::EnumDef;
7 use rustc_data_structures::fx::FxHashSet;
8 use rustc_hir::def_id::LocalDefId;
9 use rustc_hir::def_id::CRATE_DEF_ID;
10 use rustc_middle::middle::privacy::Level;
11 use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility};
12 use rustc_middle::ty::Visibility;
13 use std::mem;
14 
15 #[derive(Clone, Copy)]
16 enum ParentId<'a> {
17     Def(LocalDefId),
18     Import(NameBinding<'a>),
19 }
20 
21 impl ParentId<'_> {
level(self) -> Level22     fn level(self) -> Level {
23         match self {
24             ParentId::Def(_) => Level::Direct,
25             ParentId::Import(_) => Level::Reexported,
26         }
27     }
28 }
29 
30 pub(crate) struct EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
31     r: &'r mut Resolver<'a, 'tcx>,
32     def_effective_visibilities: EffectiveVisibilities,
33     /// While walking import chains we need to track effective visibilities per-binding, and def id
34     /// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
35     /// bindings can correspond to a single def id in imports. So we keep a separate table.
36     import_effective_visibilities: EffectiveVisibilities<NameBinding<'a>>,
37     // It's possible to recalculate this at any point, but it's relatively expensive.
38     current_private_vis: Visibility,
39     changed: bool,
40 }
41 
42 impl Resolver<'_, '_> {
nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId43     fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId {
44         self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
45     }
46 
private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility47     fn private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility {
48         let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
49         Visibility::Restricted(
50             import
51                 .id()
52                 .map(|id| self.nearest_normal_mod(self.local_def_id(id)))
53                 .unwrap_or(CRATE_DEF_ID),
54         )
55     }
56 
private_vis_def(&mut self, def_id: LocalDefId) -> Visibility57     fn private_vis_def(&mut self, def_id: LocalDefId) -> Visibility {
58         // For mod items `nearest_normal_mod` returns its argument, but we actually need its parent.
59         let normal_mod_id = self.nearest_normal_mod(def_id);
60         if normal_mod_id == def_id {
61             Visibility::Restricted(self.tcx.local_parent(def_id))
62         } else {
63             Visibility::Restricted(normal_mod_id)
64         }
65     }
66 }
67 
68 impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
69     /// Fills the `Resolver::effective_visibilities` table with public & exported items
70     /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
71     /// need access to a TyCtxt for that. Returns the set of ambiguous re-exports.
compute_effective_visibilities<'c>( r: &'r mut Resolver<'a, 'tcx>, krate: &'c Crate, ) -> FxHashSet<NameBinding<'a>>72     pub(crate) fn compute_effective_visibilities<'c>(
73         r: &'r mut Resolver<'a, 'tcx>,
74         krate: &'c Crate,
75     ) -> FxHashSet<NameBinding<'a>> {
76         let mut visitor = EffectiveVisibilitiesVisitor {
77             r,
78             def_effective_visibilities: Default::default(),
79             import_effective_visibilities: Default::default(),
80             current_private_vis: Visibility::Restricted(CRATE_DEF_ID),
81             changed: true,
82         };
83 
84         visitor.def_effective_visibilities.update_root();
85         visitor.set_bindings_effective_visibilities(CRATE_DEF_ID);
86 
87         while visitor.changed {
88             visitor.changed = false;
89             visit::walk_crate(&mut visitor, krate);
90         }
91         visitor.r.effective_visibilities = visitor.def_effective_visibilities;
92 
93         let mut exported_ambiguities = FxHashSet::default();
94 
95         // Update visibilities for import def ids. These are not used during the
96         // `EffectiveVisibilitiesVisitor` pass, because we have more detailed binding-based
97         // information, but are used by later passes. Effective visibility of an import def id
98         // is the maximum value among visibilities of bindings corresponding to that def id.
99         for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
100             let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
101             if !binding.is_ambiguity() {
102                 if let Some(node_id) = import.id() {
103                     r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
104                 }
105             } else if binding.ambiguity.is_some() && eff_vis.is_public_at_level(Level::Reexported) {
106                 exported_ambiguities.insert(*binding);
107             }
108         }
109 
110         info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities);
111 
112         exported_ambiguities
113     }
114 
115     /// Update effective visibilities of bindings in the given module,
116     /// including their whole reexport chains.
set_bindings_effective_visibilities(&mut self, module_id: LocalDefId)117     fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) {
118         assert!(self.r.module_map.contains_key(&&module_id.to_def_id()));
119         let module = self.r.get_module(module_id.to_def_id()).unwrap();
120         let resolutions = self.r.resolutions(module);
121 
122         for (_, name_resolution) in resolutions.borrow().iter() {
123             if let Some(mut binding) = name_resolution.borrow().binding() {
124                 // Set the given effective visibility level to `Level::Direct` and
125                 // sets the rest of the `use` chain to `Level::Reexported` until
126                 // we hit the actual exported item.
127                 //
128                 // If the binding is ambiguous, put the root ambiguity binding and all reexports
129                 // leading to it into the table. They are used by the `ambiguous_glob_reexports`
130                 // lint. For all bindings added to the table this way `is_ambiguity` returns true.
131                 let mut parent_id = ParentId::Def(module_id);
132                 while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind {
133                     self.update_import(binding, parent_id);
134 
135                     if binding.ambiguity.is_some() {
136                         // Stop at the root ambiguity, further bindings in the chain should not
137                         // be reexported because the root ambiguity blocks any access to them.
138                         // (Those further bindings are most likely not ambiguities themselves.)
139                         break;
140                     }
141 
142                     parent_id = ParentId::Import(binding);
143                     binding = nested_binding;
144                 }
145 
146                 if binding.ambiguity.is_none()
147                     && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
148                     self.update_def(def_id, binding.vis.expect_local(), parent_id);
149                 }
150             }
151         }
152     }
153 
effective_vis_or_private(&mut self, parent_id: ParentId<'a>) -> EffectiveVisibility154     fn effective_vis_or_private(&mut self, parent_id: ParentId<'a>) -> EffectiveVisibility {
155         // Private nodes are only added to the table for caching, they could be added or removed at
156         // any moment without consequences, so we don't set `changed` to true when adding them.
157         *match parent_id {
158             ParentId::Def(def_id) => self
159                 .def_effective_visibilities
160                 .effective_vis_or_private(def_id, || self.r.private_vis_def(def_id)),
161             ParentId::Import(binding) => self
162                 .import_effective_visibilities
163                 .effective_vis_or_private(binding, || self.r.private_vis_import(binding)),
164         }
165     }
166 
167     /// All effective visibilities for a node are larger or equal than private visibility
168     /// for that node (see `check_invariants` in middle/privacy.rs).
169     /// So if either parent or nominal visibility is the same as private visibility, then
170     /// `min(parent_vis, nominal_vis) <= private_vis`, and the update logic is guaranteed
171     /// to not update anything and we can skip it.
172     ///
173     /// We are checking this condition only if the correct value of private visibility is
174     /// cheaply available, otherwise it doesn't make sense performance-wise.
175     ///
176     /// `None` is returned if the update can be skipped,
177     /// and cheap private visibility is returned otherwise.
may_update( &self, nominal_vis: Visibility, parent_id: ParentId<'_>, ) -> Option<Option<Visibility>>178     fn may_update(
179         &self,
180         nominal_vis: Visibility,
181         parent_id: ParentId<'_>,
182     ) -> Option<Option<Visibility>> {
183         match parent_id {
184             ParentId::Def(def_id) => (nominal_vis != self.current_private_vis
185                 && self.r.visibilities[&def_id] != self.current_private_vis)
186                 .then_some(Some(self.current_private_vis)),
187             ParentId::Import(_) => Some(None),
188         }
189     }
190 
update_import(&mut self, binding: NameBinding<'a>, parent_id: ParentId<'a>)191     fn update_import(&mut self, binding: NameBinding<'a>, parent_id: ParentId<'a>) {
192         let nominal_vis = binding.vis.expect_local();
193         let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
194         let inherited_eff_vis = self.effective_vis_or_private(parent_id);
195         let tcx = self.r.tcx;
196         self.changed |= self.import_effective_visibilities.update(
197             binding,
198             Some(nominal_vis),
199             || cheap_private_vis.unwrap_or_else(|| self.r.private_vis_import(binding)),
200             inherited_eff_vis,
201             parent_id.level(),
202             tcx,
203         );
204     }
205 
update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>)206     fn update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>) {
207         let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
208         let inherited_eff_vis = self.effective_vis_or_private(parent_id);
209         let tcx = self.r.tcx;
210         self.changed |= self.def_effective_visibilities.update(
211             def_id,
212             Some(nominal_vis),
213             || cheap_private_vis.unwrap_or_else(|| self.r.private_vis_def(def_id)),
214             inherited_eff_vis,
215             parent_id.level(),
216             tcx,
217         );
218     }
219 
update_field(&mut self, def_id: LocalDefId, parent_id: LocalDefId)220     fn update_field(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
221         self.update_def(def_id, self.r.visibilities[&def_id], ParentId::Def(parent_id));
222     }
223 }
224 
225 impl<'r, 'ast, 'tcx> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r, 'tcx> {
visit_item(&mut self, item: &'ast ast::Item)226     fn visit_item(&mut self, item: &'ast ast::Item) {
227         let def_id = self.r.local_def_id(item.id);
228         // Update effective visibilities of nested items.
229         // If it's a mod, also make the visitor walk all of its items
230         match item.kind {
231             // Resolved in rustc_privacy when types are available
232             ast::ItemKind::Impl(..) => return,
233 
234             // Should be unreachable at this stage
235             ast::ItemKind::MacCall(..) => panic!(
236                 "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
237             ),
238 
239             ast::ItemKind::Mod(..) => {
240                 let prev_private_vis =
241                     mem::replace(&mut self.current_private_vis, Visibility::Restricted(def_id));
242                 self.set_bindings_effective_visibilities(def_id);
243                 visit::walk_item(self, item);
244                 self.current_private_vis = prev_private_vis;
245             }
246 
247             ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
248                 self.set_bindings_effective_visibilities(def_id);
249                 for variant in variants {
250                     let variant_def_id = self.r.local_def_id(variant.id);
251                     for field in variant.data.fields() {
252                         self.update_field(self.r.local_def_id(field.id), variant_def_id);
253                     }
254                 }
255             }
256 
257             ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
258                 for field in def.fields() {
259                     self.update_field(self.r.local_def_id(field.id), def_id);
260                 }
261             }
262 
263             ast::ItemKind::Trait(..) => {
264                 self.set_bindings_effective_visibilities(def_id);
265             }
266 
267             ast::ItemKind::ExternCrate(..)
268             | ast::ItemKind::Use(..)
269             | ast::ItemKind::Static(..)
270             | ast::ItemKind::Const(..)
271             | ast::ItemKind::GlobalAsm(..)
272             | ast::ItemKind::TyAlias(..)
273             | ast::ItemKind::TraitAlias(..)
274             | ast::ItemKind::MacroDef(..)
275             | ast::ItemKind::ForeignMod(..)
276             | ast::ItemKind::Fn(..) => return,
277         }
278     }
279 }
280