]>
Commit | Line | Data |
---|---|---|
1 | #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] | |
2 | ||
3 | #![deny(rust_2018_idioms)] | |
4 | ||
5 | #![feature(nll)] | |
6 | #![feature(rustc_diagnostic_macros)] | |
7 | ||
8 | #![recursion_limit="256"] | |
9 | ||
10 | #[macro_use] extern crate syntax; | |
11 | ||
12 | use rustc::bug; | |
13 | use rustc::hir::{self, Node, PatKind, AssociatedItemKind}; | |
14 | use rustc::hir::def::Def; | |
15 | use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId}; | |
16 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; | |
17 | use rustc::hir::itemlikevisit::DeepVisitor; | |
18 | use rustc::lint; | |
19 | use rustc::middle::privacy::{AccessLevel, AccessLevels}; | |
20 | use rustc::ty::{self, TyCtxt, Ty, TraitRef, TypeFoldable, GenericParamDefKind}; | |
21 | use rustc::ty::fold::TypeVisitor; | |
22 | use rustc::ty::query::Providers; | |
23 | use rustc::ty::subst::Substs; | |
24 | use rustc::util::nodemap::NodeSet; | |
25 | use rustc_data_structures::fx::FxHashSet; | |
26 | use rustc_data_structures::sync::Lrc; | |
27 | use syntax::ast::{self, DUMMY_NODE_ID, Ident}; | |
28 | use syntax::attr; | |
29 | use syntax::symbol::keywords; | |
30 | use syntax_pos::Span; | |
31 | ||
32 | use std::{cmp, fmt, mem}; | |
33 | use std::marker::PhantomData; | |
34 | ||
35 | mod diagnostics; | |
36 | ||
37 | //////////////////////////////////////////////////////////////////////////////// | |
38 | /// Generic infrastructure used to implement specific visitors below. | |
39 | //////////////////////////////////////////////////////////////////////////////// | |
40 | ||
41 | /// Implemented to visit all `DefId`s in a type. | |
42 | /// Visiting `DefId`s is useful because visibilities and reachabilities are attached to them. | |
43 | /// The idea is to visit "all components of a type", as documented in | |
44 | /// https://github.com/rust-lang/rfcs/blob/master/text/2145-type-privacy.md#how-to-determine-visibility-of-a-type. | |
45 | /// The default type visitor (`TypeVisitor`) does most of the job, but it has some shortcomings. | |
46 | /// First, it doesn't have overridable `fn visit_trait_ref`, so we have to catch trait `DefId`s | |
47 | /// manually. Second, it doesn't visit some type components like signatures of fn types, or traits | |
48 | /// in `impl Trait`, see individual comments in `DefIdVisitorSkeleton::visit_ty`. | |
49 | trait DefIdVisitor<'a, 'tcx: 'a> { | |
50 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx>; | |
51 | fn shallow(&self) -> bool { false } | |
52 | fn skip_assoc_tys(&self) -> bool { false } | |
53 | fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool; | |
54 | ||
55 | /// Not overridden, but used to actually visit types and traits. | |
56 | fn skeleton(&mut self) -> DefIdVisitorSkeleton<'_, 'a, 'tcx, Self> { | |
57 | DefIdVisitorSkeleton { | |
58 | def_id_visitor: self, | |
59 | visited_opaque_tys: Default::default(), | |
60 | dummy: Default::default(), | |
61 | } | |
62 | } | |
63 | fn visit(&mut self, ty_fragment: impl TypeFoldable<'tcx>) -> bool { | |
64 | ty_fragment.visit_with(&mut self.skeleton()) | |
65 | } | |
66 | fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> bool { | |
67 | self.skeleton().visit_trait(trait_ref) | |
68 | } | |
69 | fn visit_predicates(&mut self, predicates: Lrc<ty::GenericPredicates<'tcx>>) -> bool { | |
70 | self.skeleton().visit_predicates(predicates) | |
71 | } | |
72 | } | |
73 | ||
74 | struct DefIdVisitorSkeleton<'v, 'a, 'tcx, V> | |
75 | where V: DefIdVisitor<'a, 'tcx> + ?Sized | |
76 | { | |
77 | def_id_visitor: &'v mut V, | |
78 | visited_opaque_tys: FxHashSet<DefId>, | |
79 | dummy: PhantomData<TyCtxt<'a, 'tcx, 'tcx>>, | |
80 | } | |
81 | ||
82 | impl<'a, 'tcx, V> DefIdVisitorSkeleton<'_, 'a, 'tcx, V> | |
83 | where V: DefIdVisitor<'a, 'tcx> + ?Sized | |
84 | { | |
85 | fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> bool { | |
86 | let TraitRef { def_id, substs } = trait_ref; | |
87 | self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref) || | |
88 | (!self.def_id_visitor.shallow() && substs.visit_with(self)) | |
89 | } | |
90 | ||
91 | fn visit_predicates(&mut self, predicates: Lrc<ty::GenericPredicates<'tcx>>) -> bool { | |
92 | let ty::GenericPredicates { parent: _, predicates } = &*predicates; | |
93 | for (predicate, _span) in predicates { | |
94 | match predicate { | |
95 | ty::Predicate::Trait(poly_predicate) => { | |
96 | let ty::TraitPredicate { trait_ref } = *poly_predicate.skip_binder(); | |
97 | if self.visit_trait(trait_ref) { | |
98 | return true; | |
99 | } | |
100 | } | |
101 | ty::Predicate::Projection(poly_predicate) => { | |
102 | let ty::ProjectionPredicate { projection_ty, ty } = | |
103 | *poly_predicate.skip_binder(); | |
104 | if ty.visit_with(self) { | |
105 | return true; | |
106 | } | |
107 | if self.visit_trait(projection_ty.trait_ref(self.def_id_visitor.tcx())) { | |
108 | return true; | |
109 | } | |
110 | } | |
111 | ty::Predicate::TypeOutlives(poly_predicate) => { | |
112 | let ty::OutlivesPredicate(ty, _region) = *poly_predicate.skip_binder(); | |
113 | if ty.visit_with(self) { | |
114 | return true; | |
115 | } | |
116 | } | |
117 | ty::Predicate::RegionOutlives(..) => {}, | |
118 | _ => bug!("unexpected predicate: {:?}", predicate), | |
119 | } | |
120 | } | |
121 | false | |
122 | } | |
123 | } | |
124 | ||
125 | impl<'a, 'tcx, V> TypeVisitor<'tcx> for DefIdVisitorSkeleton<'_, 'a, 'tcx, V> | |
126 | where V: DefIdVisitor<'a, 'tcx> + ?Sized | |
127 | { | |
128 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
129 | let tcx = self.def_id_visitor.tcx(); | |
130 | // Substs are not visited here because they are visited below in `super_visit_with`. | |
131 | match ty.sty { | |
132 | ty::Adt(&ty::AdtDef { did: def_id, .. }, ..) | | |
133 | ty::Foreign(def_id) | | |
134 | ty::FnDef(def_id, ..) | | |
135 | ty::Closure(def_id, ..) | | |
136 | ty::Generator(def_id, ..) => { | |
137 | if self.def_id_visitor.visit_def_id(def_id, "type", ty) { | |
138 | return true; | |
139 | } | |
140 | if self.def_id_visitor.shallow() { | |
141 | return false; | |
142 | } | |
143 | // Default type visitor doesn't visit signatures of fn types. | |
144 | // Something like `fn() -> Priv {my_func}` is considered a private type even if | |
145 | // `my_func` is public, so we need to visit signatures. | |
146 | if let ty::FnDef(..) = ty.sty { | |
147 | if tcx.fn_sig(def_id).visit_with(self) { | |
148 | return true; | |
149 | } | |
150 | } | |
151 | // Inherent static methods don't have self type in substs. | |
152 | // Something like `fn() {my_method}` type of the method | |
153 | // `impl Pub<Priv> { pub fn my_method() {} }` is considered a private type, | |
154 | // so we need to visit the self type additionally. | |
155 | if let Some(assoc_item) = tcx.opt_associated_item(def_id) { | |
156 | if let ty::ImplContainer(impl_def_id) = assoc_item.container { | |
157 | if tcx.type_of(impl_def_id).visit_with(self) { | |
158 | return true; | |
159 | } | |
160 | } | |
161 | } | |
162 | } | |
163 | ty::Projection(proj) | ty::UnnormalizedProjection(proj) => { | |
164 | if self.def_id_visitor.skip_assoc_tys() { | |
165 | // Visitors searching for minimal visibility/reachability want to | |
166 | // conservatively approximate associated types like `<Type as Trait>::Alias` | |
167 | // as visible/reachable even if both `Type` and `Trait` are private. | |
168 | // Ideally, associated types should be substituted in the same way as | |
169 | // free type aliases, but this isn't done yet. | |
170 | return false; | |
171 | } | |
172 | // This will also visit substs if necessary, so we don't need to recurse. | |
173 | return self.visit_trait(proj.trait_ref(tcx)); | |
174 | } | |
175 | ty::Dynamic(predicates, ..) => { | |
176 | // All traits in the list are considered the "primary" part of the type | |
177 | // and are visited by shallow visitors. | |
178 | for predicate in *predicates.skip_binder() { | |
179 | let trait_ref = match *predicate { | |
180 | ty::ExistentialPredicate::Trait(trait_ref) => trait_ref, | |
181 | ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx), | |
182 | ty::ExistentialPredicate::AutoTrait(def_id) => | |
183 | ty::ExistentialTraitRef { def_id, substs: Substs::empty() }, | |
184 | }; | |
185 | let ty::ExistentialTraitRef { def_id, substs: _ } = trait_ref; | |
186 | if self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref) { | |
187 | return true; | |
188 | } | |
189 | } | |
190 | } | |
191 | ty::Opaque(def_id, ..) => { | |
192 | // Skip repeated `Opaque`s to avoid infinite recursion. | |
193 | if self.visited_opaque_tys.insert(def_id) { | |
194 | // The intent is to treat `impl Trait1 + Trait2` identically to | |
195 | // `dyn Trait1 + Trait2`. Therefore we ignore def-id of the opaque type itself | |
196 | // (it either has no visibility, or its visibility is insignificant, like | |
197 | // visibilities of type aliases) and recurse into predicates instead to go | |
198 | // through the trait list (default type visitor doesn't visit those traits). | |
199 | // All traits in the list are considered the "primary" part of the type | |
200 | // and are visited by shallow visitors. | |
201 | if self.visit_predicates(tcx.predicates_of(def_id)) { | |
202 | return true; | |
203 | } | |
204 | } | |
205 | } | |
206 | // These types don't have their own def-ids (but may have subcomponents | |
207 | // with def-ids that should be visited recursively). | |
208 | ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | | |
209 | ty::Float(..) | ty::Str | ty::Never | | |
210 | ty::Array(..) | ty::Slice(..) | ty::Tuple(..) | | |
211 | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) | | |
212 | ty::Param(..) | ty::Error | ty::GeneratorWitness(..) => {} | |
213 | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => | |
214 | bug!("unexpected type: {:?}", ty), | |
215 | } | |
216 | ||
217 | !self.def_id_visitor.shallow() && ty.super_visit_with(self) | |
218 | } | |
219 | } | |
220 | ||
221 | fn def_id_visibility<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) | |
222 | -> (ty::Visibility, Span, &'static str) { | |
223 | match tcx.hir().as_local_node_id(def_id) { | |
224 | Some(node_id) => { | |
225 | let vis = match tcx.hir().get(node_id) { | |
226 | Node::Item(item) => &item.vis, | |
227 | Node::ForeignItem(foreign_item) => &foreign_item.vis, | |
228 | Node::TraitItem(..) | Node::Variant(..) => { | |
229 | return def_id_visibility(tcx, tcx.hir().get_parent_did(node_id)); | |
230 | } | |
231 | Node::ImplItem(impl_item) => { | |
232 | match tcx.hir().get(tcx.hir().get_parent(node_id)) { | |
233 | Node::Item(item) => match &item.node { | |
234 | hir::ItemKind::Impl(.., None, _, _) => &impl_item.vis, | |
235 | hir::ItemKind::Impl(.., Some(trait_ref), _, _) | |
236 | => return def_id_visibility(tcx, trait_ref.path.def.def_id()), | |
237 | kind => bug!("unexpected item kind: {:?}", kind), | |
238 | } | |
239 | node => bug!("unexpected node kind: {:?}", node), | |
240 | } | |
241 | } | |
242 | Node::StructCtor(vdata) => { | |
243 | let struct_node_id = tcx.hir().get_parent(node_id); | |
244 | let item = match tcx.hir().get(struct_node_id) { | |
245 | Node::Item(item) => item, | |
246 | node => bug!("unexpected node kind: {:?}", node), | |
247 | }; | |
248 | let (mut ctor_vis, mut span, mut descr) = | |
249 | (ty::Visibility::from_hir(&item.vis, struct_node_id, tcx), | |
250 | item.vis.span, item.vis.node.descr()); | |
251 | for field in vdata.fields() { | |
252 | let field_vis = ty::Visibility::from_hir(&field.vis, node_id, tcx); | |
253 | if ctor_vis.is_at_least(field_vis, tcx) { | |
254 | ctor_vis = field_vis; | |
255 | span = field.vis.span; | |
256 | descr = field.vis.node.descr(); | |
257 | } | |
258 | } | |
259 | ||
260 | // If the structure is marked as non_exhaustive then lower the | |
261 | // visibility to within the crate. | |
262 | if ctor_vis == ty::Visibility::Public { | |
263 | let adt_def = tcx.adt_def(tcx.hir().get_parent_did(node_id)); | |
264 | if adt_def.non_enum_variant().is_field_list_non_exhaustive() { | |
265 | ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); | |
266 | span = attr::find_by_name(&item.attrs, "non_exhaustive").unwrap().span; | |
267 | descr = "crate-visible"; | |
268 | } | |
269 | } | |
270 | ||
271 | return (ctor_vis, span, descr); | |
272 | } | |
273 | Node::Expr(expr) => { | |
274 | return (ty::Visibility::Restricted(tcx.hir().get_module_parent(expr.id)), | |
275 | expr.span, "private") | |
276 | } | |
277 | node => bug!("unexpected node kind: {:?}", node) | |
278 | }; | |
279 | (ty::Visibility::from_hir(vis, node_id, tcx), vis.span, vis.node.descr()) | |
280 | } | |
281 | None => { | |
282 | let vis = tcx.visibility(def_id); | |
283 | let descr = if vis == ty::Visibility::Public { "public" } else { "private" }; | |
284 | (vis, tcx.def_span(def_id), descr) | |
285 | } | |
286 | } | |
287 | } | |
288 | ||
289 | // Set the correct `TypeckTables` for the given `item_id` (or an empty table if | |
290 | // there is no `TypeckTables` for the item). | |
291 | fn item_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
292 | node_id: ast::NodeId, | |
293 | empty_tables: &'a ty::TypeckTables<'tcx>) | |
294 | -> &'a ty::TypeckTables<'tcx> { | |
295 | let def_id = tcx.hir().local_def_id(node_id); | |
296 | if tcx.has_typeck_tables(def_id) { tcx.typeck_tables_of(def_id) } else { empty_tables } | |
297 | } | |
298 | ||
299 | fn min<'a, 'tcx>(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'a, 'tcx, 'tcx>) | |
300 | -> ty::Visibility { | |
301 | if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } | |
302 | } | |
303 | ||
304 | //////////////////////////////////////////////////////////////////////////////// | |
305 | /// Visitor used to determine if pub(restricted) is used anywhere in the crate. | |
306 | /// | |
307 | /// This is done so that `private_in_public` warnings can be turned into hard errors | |
308 | /// in crates that have been updated to use pub(restricted). | |
309 | //////////////////////////////////////////////////////////////////////////////// | |
310 | struct PubRestrictedVisitor<'a, 'tcx: 'a> { | |
311 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
312 | has_pub_restricted: bool, | |
313 | } | |
314 | ||
315 | impl<'a, 'tcx> Visitor<'tcx> for PubRestrictedVisitor<'a, 'tcx> { | |
316 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
317 | NestedVisitorMap::All(&self.tcx.hir()) | |
318 | } | |
319 | fn visit_vis(&mut self, vis: &'tcx hir::Visibility) { | |
320 | self.has_pub_restricted = self.has_pub_restricted || vis.node.is_pub_restricted(); | |
321 | } | |
322 | } | |
323 | ||
324 | //////////////////////////////////////////////////////////////////////////////// | |
325 | /// Visitor used to determine impl visibility and reachability. | |
326 | //////////////////////////////////////////////////////////////////////////////// | |
327 | ||
328 | struct FindMin<'a, 'tcx, VL: VisibilityLike> { | |
329 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
330 | access_levels: &'a AccessLevels, | |
331 | min: VL, | |
332 | } | |
333 | ||
334 | impl<'a, 'tcx, VL: VisibilityLike> DefIdVisitor<'a, 'tcx> for FindMin<'a, 'tcx, VL> { | |
335 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx } | |
336 | fn shallow(&self) -> bool { VL::SHALLOW } | |
337 | fn skip_assoc_tys(&self) -> bool { true } | |
338 | fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) -> bool { | |
339 | self.min = VL::new_min(self, def_id); | |
340 | false | |
341 | } | |
342 | } | |
343 | ||
344 | trait VisibilityLike: Sized { | |
345 | const MAX: Self; | |
346 | const SHALLOW: bool = false; | |
347 | fn new_min<'a, 'tcx>(find: &FindMin<'a, 'tcx, Self>, def_id: DefId) -> Self; | |
348 | ||
349 | // Returns an over-approximation (`skip_assoc_tys` = true) of visibility due to | |
350 | // associated types for which we can't determine visibility precisely. | |
351 | fn of_impl<'a, 'tcx>(node_id: ast::NodeId, tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
352 | access_levels: &'a AccessLevels) -> Self { | |
353 | let mut find = FindMin { tcx, access_levels, min: Self::MAX }; | |
354 | let def_id = tcx.hir().local_def_id(node_id); | |
355 | find.visit(tcx.type_of(def_id)); | |
356 | if let Some(trait_ref) = tcx.impl_trait_ref(def_id) { | |
357 | find.visit_trait(trait_ref); | |
358 | } | |
359 | find.min | |
360 | } | |
361 | } | |
362 | impl VisibilityLike for ty::Visibility { | |
363 | const MAX: Self = ty::Visibility::Public; | |
364 | fn new_min<'a, 'tcx>(find: &FindMin<'a, 'tcx, Self>, def_id: DefId) -> Self { | |
365 | min(def_id_visibility(find.tcx, def_id).0, find.min, find.tcx) | |
366 | } | |
367 | } | |
368 | impl VisibilityLike for Option<AccessLevel> { | |
369 | const MAX: Self = Some(AccessLevel::Public); | |
370 | // Type inference is very smart sometimes. | |
371 | // It can make an impl reachable even some components of its type or trait are unreachable. | |
372 | // E.g. methods of `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }` | |
373 | // can be usable from other crates (#57264). So we skip substs when calculating reachability | |
374 | // and consider an impl reachable if its "shallow" type and trait are reachable. | |
375 | // | |
376 | // The assumption we make here is that type-inference won't let you use an impl without knowing | |
377 | // both "shallow" version of its self type and "shallow" version of its trait if it exists | |
378 | // (which require reaching the `DefId`s in them). | |
379 | const SHALLOW: bool = true; | |
380 | fn new_min<'a, 'tcx>(find: &FindMin<'a, 'tcx, Self>, def_id: DefId) -> Self { | |
381 | cmp::min(if let Some(node_id) = find.tcx.hir().as_local_node_id(def_id) { | |
382 | find.access_levels.map.get(&node_id).cloned() | |
383 | } else { | |
384 | Self::MAX | |
385 | }, find.min) | |
386 | } | |
387 | } | |
388 | ||
389 | //////////////////////////////////////////////////////////////////////////////// | |
390 | /// The embargo visitor, used to determine the exports of the AST. | |
391 | //////////////////////////////////////////////////////////////////////////////// | |
392 | ||
393 | struct EmbargoVisitor<'a, 'tcx: 'a> { | |
394 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
395 | ||
396 | // Accessibility levels for reachable nodes. | |
397 | access_levels: AccessLevels, | |
398 | // Previous accessibility level; `None` means unreachable. | |
399 | prev_level: Option<AccessLevel>, | |
400 | // Has something changed in the level map? | |
401 | changed: bool, | |
402 | } | |
403 | ||
404 | struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> { | |
405 | access_level: Option<AccessLevel>, | |
406 | item_def_id: DefId, | |
407 | ev: &'b mut EmbargoVisitor<'a, 'tcx>, | |
408 | } | |
409 | ||
410 | impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { | |
411 | fn get(&self, id: ast::NodeId) -> Option<AccessLevel> { | |
412 | self.access_levels.map.get(&id).cloned() | |
413 | } | |
414 | ||
415 | // Updates node level and returns the updated level. | |
416 | fn update(&mut self, id: ast::NodeId, level: Option<AccessLevel>) -> Option<AccessLevel> { | |
417 | let old_level = self.get(id); | |
418 | // Accessibility levels can only grow. | |
419 | if level > old_level { | |
420 | self.access_levels.map.insert(id, level.unwrap()); | |
421 | self.changed = true; | |
422 | level | |
423 | } else { | |
424 | old_level | |
425 | } | |
426 | } | |
427 | ||
428 | fn reach(&mut self, item_id: ast::NodeId, access_level: Option<AccessLevel>) | |
429 | -> ReachEverythingInTheInterfaceVisitor<'_, 'a, 'tcx> { | |
430 | ReachEverythingInTheInterfaceVisitor { | |
431 | access_level: cmp::min(access_level, Some(AccessLevel::Reachable)), | |
432 | item_def_id: self.tcx.hir().local_def_id(item_id), | |
433 | ev: self, | |
434 | } | |
435 | } | |
436 | ||
437 | ||
438 | /// Given the path segments of a `ItemKind::Use`, then we need | |
439 | /// to update the visibility of the intermediate use so that it isn't linted | |
440 | /// by `unreachable_pub`. | |
441 | /// | |
442 | /// This isn't trivial as `path.def` has the `DefId` of the eventual target | |
443 | /// of the use statement not of the next intermediate use statement. | |
444 | /// | |
445 | /// To do this, consider the last two segments of the path to our intermediate | |
446 | /// use statement. We expect the penultimate segment to be a module and the | |
447 | /// last segment to be the name of the item we are exporting. We can then | |
448 | /// look at the items contained in the module for the use statement with that | |
449 | /// name and update that item's visibility. | |
450 | /// | |
451 | /// FIXME: This solution won't work with glob imports and doesn't respect | |
452 | /// namespaces. See <https://github.com/rust-lang/rust/pull/57922#discussion_r251234202>. | |
453 | fn update_visibility_of_intermediate_use_statements(&mut self, segments: &[hir::PathSegment]) { | |
454 | if let Some([module, segment]) = segments.rchunks_exact(2).next() { | |
455 | if let Some(item) = module.def | |
456 | .and_then(|def| def.mod_def_id()) | |
457 | .and_then(|def_id| self.tcx.hir().as_local_hir_id(def_id)) | |
458 | .map(|module_hir_id| self.tcx.hir().expect_item_by_hir_id(module_hir_id)) | |
459 | { | |
460 | if let hir::ItemKind::Mod(m) = &item.node { | |
461 | for item_id in m.item_ids.as_ref() { | |
462 | let item = self.tcx.hir().expect_item(item_id.id); | |
463 | let def_id = self.tcx.hir().local_def_id(item_id.id); | |
464 | if !self.tcx.hygienic_eq(segment.ident, item.ident, def_id) { continue; } | |
465 | if let hir::ItemKind::Use(..) = item.node { | |
466 | self.update(item.id, Some(AccessLevel::Exported)); | |
467 | } | |
468 | } | |
469 | } | |
470 | } | |
471 | } | |
472 | } | |
473 | } | |
474 | ||
475 | impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { | |
476 | /// We want to visit items in the context of their containing | |
477 | /// module and so forth, so supply a crate for doing a deep walk. | |
478 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
479 | NestedVisitorMap::All(&self.tcx.hir()) | |
480 | } | |
481 | ||
482 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
483 | let inherited_item_level = match item.node { | |
484 | hir::ItemKind::Impl(..) => | |
485 | Option::<AccessLevel>::of_impl(item.id, self.tcx, &self.access_levels), | |
486 | // Foreign modules inherit level from parents. | |
487 | hir::ItemKind::ForeignMod(..) => self.prev_level, | |
488 | // Other `pub` items inherit levels from parents. | |
489 | hir::ItemKind::Const(..) | hir::ItemKind::Enum(..) | hir::ItemKind::ExternCrate(..) | | |
490 | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Fn(..) | hir::ItemKind::Mod(..) | | |
491 | hir::ItemKind::Static(..) | hir::ItemKind::Struct(..) | | |
492 | hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) | | |
493 | hir::ItemKind::Existential(..) | | |
494 | hir::ItemKind::Ty(..) | hir::ItemKind::Union(..) | hir::ItemKind::Use(..) => { | |
495 | if item.vis.node.is_pub() { self.prev_level } else { None } | |
496 | } | |
497 | }; | |
498 | ||
499 | // Update level of the item itself. | |
500 | let item_level = self.update(item.id, inherited_item_level); | |
501 | ||
502 | // Update levels of nested things. | |
503 | match item.node { | |
504 | hir::ItemKind::Enum(ref def, _) => { | |
505 | for variant in &def.variants { | |
506 | let variant_level = self.update(variant.node.data.id(), item_level); | |
507 | for field in variant.node.data.fields() { | |
508 | self.update(field.id, variant_level); | |
509 | } | |
510 | } | |
511 | } | |
512 | hir::ItemKind::Impl(.., ref trait_ref, _, ref impl_item_refs) => { | |
513 | for impl_item_ref in impl_item_refs { | |
514 | if trait_ref.is_some() || impl_item_ref.vis.node.is_pub() { | |
515 | self.update(impl_item_ref.id.node_id, item_level); | |
516 | } | |
517 | } | |
518 | } | |
519 | hir::ItemKind::Trait(.., ref trait_item_refs) => { | |
520 | for trait_item_ref in trait_item_refs { | |
521 | self.update(trait_item_ref.id.node_id, item_level); | |
522 | } | |
523 | } | |
524 | hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => { | |
525 | if !def.is_struct() { | |
526 | self.update(def.id(), item_level); | |
527 | } | |
528 | for field in def.fields() { | |
529 | if field.vis.node.is_pub() { | |
530 | self.update(field.id, item_level); | |
531 | } | |
532 | } | |
533 | } | |
534 | hir::ItemKind::ForeignMod(ref foreign_mod) => { | |
535 | for foreign_item in &foreign_mod.items { | |
536 | if foreign_item.vis.node.is_pub() { | |
537 | self.update(foreign_item.id, item_level); | |
538 | } | |
539 | } | |
540 | } | |
541 | hir::ItemKind::Existential(..) | | |
542 | hir::ItemKind::Use(..) | | |
543 | hir::ItemKind::Static(..) | | |
544 | hir::ItemKind::Const(..) | | |
545 | hir::ItemKind::GlobalAsm(..) | | |
546 | hir::ItemKind::Ty(..) | | |
547 | hir::ItemKind::Mod(..) | | |
548 | hir::ItemKind::TraitAlias(..) | | |
549 | hir::ItemKind::Fn(..) | | |
550 | hir::ItemKind::ExternCrate(..) => {} | |
551 | } | |
552 | ||
553 | // Mark all items in interfaces of reachable items as reachable. | |
554 | match item.node { | |
555 | // The interface is empty. | |
556 | hir::ItemKind::ExternCrate(..) => {} | |
557 | // All nested items are checked by `visit_item`. | |
558 | hir::ItemKind::Mod(..) => {} | |
559 | // Re-exports are handled in `visit_mod`. However, in order to avoid looping over | |
560 | // all of the items of a mod in `visit_mod` looking for use statements, we handle | |
561 | // making sure that intermediate use statements have their visibilities updated here. | |
562 | hir::ItemKind::Use(ref path, _) => { | |
563 | if item_level.is_some() { | |
564 | self.update_visibility_of_intermediate_use_statements(path.segments.as_ref()); | |
565 | } | |
566 | } | |
567 | // The interface is empty. | |
568 | hir::ItemKind::GlobalAsm(..) => {} | |
569 | hir::ItemKind::Existential(..) => { | |
570 | // FIXME: This is some serious pessimization intended to workaround deficiencies | |
571 | // in the reachability pass (`middle/reachable.rs`). Types are marked as link-time | |
572 | // reachable if they are returned via `impl Trait`, even from private functions. | |
573 | let exist_level = cmp::max(item_level, Some(AccessLevel::ReachableFromImplTrait)); | |
574 | self.reach(item.id, exist_level).generics().predicates().ty(); | |
575 | } | |
576 | // Visit everything. | |
577 | hir::ItemKind::Const(..) | hir::ItemKind::Static(..) | | |
578 | hir::ItemKind::Fn(..) | hir::ItemKind::Ty(..) => { | |
579 | if item_level.is_some() { | |
580 | self.reach(item.id, item_level).generics().predicates().ty(); | |
581 | } | |
582 | } | |
583 | hir::ItemKind::Trait(.., ref trait_item_refs) => { | |
584 | if item_level.is_some() { | |
585 | self.reach(item.id, item_level).generics().predicates(); | |
586 | ||
587 | for trait_item_ref in trait_item_refs { | |
588 | let mut reach = self.reach(trait_item_ref.id.node_id, item_level); | |
589 | reach.generics().predicates(); | |
590 | ||
591 | if trait_item_ref.kind == AssociatedItemKind::Type && | |
592 | !trait_item_ref.defaultness.has_value() { | |
593 | // No type to visit. | |
594 | } else { | |
595 | reach.ty(); | |
596 | } | |
597 | } | |
598 | } | |
599 | } | |
600 | hir::ItemKind::TraitAlias(..) => { | |
601 | if item_level.is_some() { | |
602 | self.reach(item.id, item_level).generics().predicates(); | |
603 | } | |
604 | } | |
605 | // Visit everything except for private impl items. | |
606 | hir::ItemKind::Impl(.., ref impl_item_refs) => { | |
607 | if item_level.is_some() { | |
608 | self.reach(item.id, item_level).generics().predicates().ty().trait_ref(); | |
609 | ||
610 | for impl_item_ref in impl_item_refs { | |
611 | let impl_item_level = self.get(impl_item_ref.id.node_id); | |
612 | if impl_item_level.is_some() { | |
613 | self.reach(impl_item_ref.id.node_id, impl_item_level) | |
614 | .generics().predicates().ty(); | |
615 | } | |
616 | } | |
617 | } | |
618 | } | |
619 | ||
620 | // Visit everything, but enum variants have their own levels. | |
621 | hir::ItemKind::Enum(ref def, _) => { | |
622 | if item_level.is_some() { | |
623 | self.reach(item.id, item_level).generics().predicates(); | |
624 | } | |
625 | for variant in &def.variants { | |
626 | let variant_level = self.get(variant.node.data.id()); | |
627 | if variant_level.is_some() { | |
628 | for field in variant.node.data.fields() { | |
629 | self.reach(field.id, variant_level).ty(); | |
630 | } | |
631 | // Corner case: if the variant is reachable, but its | |
632 | // enum is not, make the enum reachable as well. | |
633 | self.update(item.id, variant_level); | |
634 | } | |
635 | } | |
636 | } | |
637 | // Visit everything, but foreign items have their own levels. | |
638 | hir::ItemKind::ForeignMod(ref foreign_mod) => { | |
639 | for foreign_item in &foreign_mod.items { | |
640 | let foreign_item_level = self.get(foreign_item.id); | |
641 | if foreign_item_level.is_some() { | |
642 | self.reach(foreign_item.id, foreign_item_level) | |
643 | .generics().predicates().ty(); | |
644 | } | |
645 | } | |
646 | } | |
647 | // Visit everything except for private fields. | |
648 | hir::ItemKind::Struct(ref struct_def, _) | | |
649 | hir::ItemKind::Union(ref struct_def, _) => { | |
650 | if item_level.is_some() { | |
651 | self.reach(item.id, item_level).generics().predicates(); | |
652 | for field in struct_def.fields() { | |
653 | let field_level = self.get(field.id); | |
654 | if field_level.is_some() { | |
655 | self.reach(field.id, field_level).ty(); | |
656 | } | |
657 | } | |
658 | } | |
659 | } | |
660 | } | |
661 | ||
662 | let orig_level = mem::replace(&mut self.prev_level, item_level); | |
663 | intravisit::walk_item(self, item); | |
664 | self.prev_level = orig_level; | |
665 | } | |
666 | ||
667 | fn visit_block(&mut self, b: &'tcx hir::Block) { | |
668 | // Blocks can have public items, for example impls, but they always | |
669 | // start as completely private regardless of publicity of a function, | |
670 | // constant, type, field, etc., in which this block resides. | |
671 | let orig_level = mem::replace(&mut self.prev_level, None); | |
672 | intravisit::walk_block(self, b); | |
673 | self.prev_level = orig_level; | |
674 | } | |
675 | ||
676 | fn visit_mod(&mut self, m: &'tcx hir::Mod, _sp: Span, id: hir::HirId) { | |
677 | // This code is here instead of in visit_item so that the | |
678 | // crate module gets processed as well. | |
679 | if self.prev_level.is_some() { | |
680 | let def_id = self.tcx.hir().local_def_id_from_hir_id(id); | |
681 | if let Some(exports) = self.tcx.module_exports(def_id) { | |
682 | for export in exports.iter() { | |
683 | if export.vis == ty::Visibility::Public { | |
684 | if let Some(def_id) = export.def.opt_def_id() { | |
685 | if let Some(node_id) = self.tcx.hir().as_local_node_id(def_id) { | |
686 | self.update(node_id, Some(AccessLevel::Exported)); | |
687 | } | |
688 | } | |
689 | } | |
690 | } | |
691 | } | |
692 | } | |
693 | ||
694 | intravisit::walk_mod(self, m, id); | |
695 | } | |
696 | ||
697 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { | |
698 | if md.legacy { | |
699 | self.update(md.id, Some(AccessLevel::Public)); | |
700 | return | |
701 | } | |
702 | ||
703 | let module_did = ty::DefIdTree::parent( | |
704 | self.tcx, | |
705 | self.tcx.hir().local_def_id(md.id) | |
706 | ).unwrap(); | |
707 | let mut module_id = self.tcx.hir().as_local_node_id(module_did).unwrap(); | |
708 | let level = if md.vis.node.is_pub() { self.get(module_id) } else { None }; | |
709 | let level = self.update(md.id, level); | |
710 | if level.is_none() { | |
711 | return | |
712 | } | |
713 | ||
714 | loop { | |
715 | let module = if module_id == ast::CRATE_NODE_ID { | |
716 | &self.tcx.hir().krate().module | |
717 | } else if let hir::ItemKind::Mod(ref module) = | |
718 | self.tcx.hir().expect_item(module_id).node { | |
719 | module | |
720 | } else { | |
721 | unreachable!() | |
722 | }; | |
723 | for id in &module.item_ids { | |
724 | self.update(id.id, level); | |
725 | } | |
726 | let def_id = self.tcx.hir().local_def_id(module_id); | |
727 | if let Some(exports) = self.tcx.module_exports(def_id) { | |
728 | for export in exports.iter() { | |
729 | if let Some(node_id) = self.tcx.hir().as_local_node_id(export.def.def_id()) { | |
730 | self.update(node_id, level); | |
731 | } | |
732 | } | |
733 | } | |
734 | ||
735 | if module_id == ast::CRATE_NODE_ID { | |
736 | break | |
737 | } | |
738 | module_id = self.tcx.hir().get_parent_node(module_id); | |
739 | } | |
740 | } | |
741 | } | |
742 | ||
743 | impl<'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'_, 'a, 'tcx> { | |
744 | fn generics(&mut self) -> &mut Self { | |
745 | for param in &self.ev.tcx.generics_of(self.item_def_id).params { | |
746 | match param.kind { | |
747 | GenericParamDefKind::Type { has_default, .. } => { | |
748 | if has_default { | |
749 | self.visit(self.ev.tcx.type_of(param.def_id)); | |
750 | } | |
751 | } | |
752 | GenericParamDefKind::Lifetime => {} | |
753 | } | |
754 | } | |
755 | self | |
756 | } | |
757 | ||
758 | fn predicates(&mut self) -> &mut Self { | |
759 | self.visit_predicates(self.ev.tcx.predicates_of(self.item_def_id)); | |
760 | self | |
761 | } | |
762 | ||
763 | fn ty(&mut self) -> &mut Self { | |
764 | self.visit(self.ev.tcx.type_of(self.item_def_id)); | |
765 | self | |
766 | } | |
767 | ||
768 | fn trait_ref(&mut self) -> &mut Self { | |
769 | if let Some(trait_ref) = self.ev.tcx.impl_trait_ref(self.item_def_id) { | |
770 | self.visit_trait(trait_ref); | |
771 | } | |
772 | self | |
773 | } | |
774 | } | |
775 | ||
776 | impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'a, 'tcx> { | |
777 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.ev.tcx } | |
778 | fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) -> bool { | |
779 | if let Some(node_id) = self.ev.tcx.hir().as_local_node_id(def_id) { | |
780 | self.ev.update(node_id, self.access_level); | |
781 | } | |
782 | false | |
783 | } | |
784 | } | |
785 | ||
786 | ////////////////////////////////////////////////////////////////////////////////////// | |
787 | /// Name privacy visitor, checks privacy and reports violations. | |
788 | /// Most of name privacy checks are performed during the main resolution phase, | |
789 | /// or later in type checking when field accesses and associated items are resolved. | |
790 | /// This pass performs remaining checks for fields in struct expressions and patterns. | |
791 | ////////////////////////////////////////////////////////////////////////////////////// | |
792 | ||
793 | struct NamePrivacyVisitor<'a, 'tcx: 'a> { | |
794 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
795 | tables: &'a ty::TypeckTables<'tcx>, | |
796 | current_item: ast::NodeId, | |
797 | empty_tables: &'a ty::TypeckTables<'tcx>, | |
798 | } | |
799 | ||
800 | impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { | |
801 | // Checks that a field in a struct constructor (expression or pattern) is accessible. | |
802 | fn check_field(&mut self, | |
803 | use_ctxt: Span, // syntax context of the field name at the use site | |
804 | span: Span, // span of the field pattern, e.g., `x: 0` | |
805 | def: &'tcx ty::AdtDef, // definition of the struct or enum | |
806 | field: &'tcx ty::FieldDef) { // definition of the field | |
807 | let ident = Ident::new(keywords::Invalid.name(), use_ctxt); | |
808 | let current_hir = self.tcx.hir().node_to_hir_id(self.current_item); | |
809 | let def_id = self.tcx.adjust_ident(ident, def.did, current_hir).1; | |
810 | if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { | |
811 | struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", | |
812 | field.ident, def.variant_descr(), self.tcx.item_path_str(def.did)) | |
813 | .span_label(span, format!("field `{}` is private", field.ident)) | |
814 | .emit(); | |
815 | } | |
816 | } | |
817 | } | |
818 | ||
819 | impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { | |
820 | /// We want to visit items in the context of their containing | |
821 | /// module and so forth, so supply a crate for doing a deep walk. | |
822 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
823 | NestedVisitorMap::All(&self.tcx.hir()) | |
824 | } | |
825 | ||
826 | fn visit_mod(&mut self, _m: &'tcx hir::Mod, _s: Span, _n: hir::HirId) { | |
827 | // Don't visit nested modules, since we run a separate visitor walk | |
828 | // for each module in `privacy_access_levels` | |
829 | } | |
830 | ||
831 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
832 | let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body)); | |
833 | let body = self.tcx.hir().body(body); | |
834 | self.visit_body(body); | |
835 | self.tables = orig_tables; | |
836 | } | |
837 | ||
838 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
839 | let orig_current_item = mem::replace(&mut self.current_item, item.id); | |
840 | let orig_tables = | |
841 | mem::replace(&mut self.tables, item_tables(self.tcx, item.id, self.empty_tables)); | |
842 | intravisit::walk_item(self, item); | |
843 | self.current_item = orig_current_item; | |
844 | self.tables = orig_tables; | |
845 | } | |
846 | ||
847 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { | |
848 | let orig_tables = | |
849 | mem::replace(&mut self.tables, item_tables(self.tcx, ti.id, self.empty_tables)); | |
850 | intravisit::walk_trait_item(self, ti); | |
851 | self.tables = orig_tables; | |
852 | } | |
853 | ||
854 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { | |
855 | let orig_tables = | |
856 | mem::replace(&mut self.tables, item_tables(self.tcx, ii.id, self.empty_tables)); | |
857 | intravisit::walk_impl_item(self, ii); | |
858 | self.tables = orig_tables; | |
859 | } | |
860 | ||
861 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { | |
862 | match expr.node { | |
863 | hir::ExprKind::Struct(ref qpath, ref fields, ref base) => { | |
864 | let def = self.tables.qpath_def(qpath, expr.hir_id); | |
865 | let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); | |
866 | let variant = adt.variant_of_def(def); | |
867 | if let Some(ref base) = *base { | |
868 | // If the expression uses FRU we need to make sure all the unmentioned fields | |
869 | // are checked for privacy (RFC 736). Rather than computing the set of | |
870 | // unmentioned fields, just check them all. | |
871 | for (vf_index, variant_field) in variant.fields.iter().enumerate() { | |
872 | let field = fields.iter().find(|f| { | |
873 | self.tcx.field_index(f.id, self.tables) == vf_index | |
874 | }); | |
875 | let (use_ctxt, span) = match field { | |
876 | Some(field) => (field.ident.span, field.span), | |
877 | None => (base.span, base.span), | |
878 | }; | |
879 | self.check_field(use_ctxt, span, adt, variant_field); | |
880 | } | |
881 | } else { | |
882 | for field in fields { | |
883 | let use_ctxt = field.ident.span; | |
884 | let index = self.tcx.field_index(field.id, self.tables); | |
885 | self.check_field(use_ctxt, field.span, adt, &variant.fields[index]); | |
886 | } | |
887 | } | |
888 | } | |
889 | _ => {} | |
890 | } | |
891 | ||
892 | intravisit::walk_expr(self, expr); | |
893 | } | |
894 | ||
895 | fn visit_pat(&mut self, pat: &'tcx hir::Pat) { | |
896 | match pat.node { | |
897 | PatKind::Struct(ref qpath, ref fields, _) => { | |
898 | let def = self.tables.qpath_def(qpath, pat.hir_id); | |
899 | let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); | |
900 | let variant = adt.variant_of_def(def); | |
901 | for field in fields { | |
902 | let use_ctxt = field.node.ident.span; | |
903 | let index = self.tcx.field_index(field.node.id, self.tables); | |
904 | self.check_field(use_ctxt, field.span, adt, &variant.fields[index]); | |
905 | } | |
906 | } | |
907 | _ => {} | |
908 | } | |
909 | ||
910 | intravisit::walk_pat(self, pat); | |
911 | } | |
912 | } | |
913 | ||
914 | //////////////////////////////////////////////////////////////////////////////////////////// | |
915 | /// Type privacy visitor, checks types for privacy and reports violations. | |
916 | /// Both explicitly written types and inferred types of expressions and patters are checked. | |
917 | /// Checks are performed on "semantic" types regardless of names and their hygiene. | |
918 | //////////////////////////////////////////////////////////////////////////////////////////// | |
919 | ||
920 | struct TypePrivacyVisitor<'a, 'tcx: 'a> { | |
921 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
922 | tables: &'a ty::TypeckTables<'tcx>, | |
923 | current_item: DefId, | |
924 | in_body: bool, | |
925 | span: Span, | |
926 | empty_tables: &'a ty::TypeckTables<'tcx>, | |
927 | } | |
928 | ||
929 | impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { | |
930 | fn item_is_accessible(&self, did: DefId) -> bool { | |
931 | def_id_visibility(self.tcx, did).0.is_accessible_from(self.current_item, self.tcx) | |
932 | } | |
933 | ||
934 | // Take node-id of an expression or pattern and check its type for privacy. | |
935 | fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { | |
936 | self.span = span; | |
937 | if self.visit(self.tables.node_type(id)) || self.visit(self.tables.node_substs(id)) { | |
938 | return true; | |
939 | } | |
940 | if let Some(adjustments) = self.tables.adjustments().get(id) { | |
941 | for adjustment in adjustments { | |
942 | if self.visit(adjustment.target) { | |
943 | return true; | |
944 | } | |
945 | } | |
946 | } | |
947 | false | |
948 | } | |
949 | ||
950 | fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { | |
951 | let is_error = !self.item_is_accessible(def_id); | |
952 | if is_error { | |
953 | self.tcx.sess.span_err(self.span, &format!("{} `{}` is private", kind, descr)); | |
954 | } | |
955 | is_error | |
956 | } | |
957 | } | |
958 | ||
959 | impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { | |
960 | /// We want to visit items in the context of their containing | |
961 | /// module and so forth, so supply a crate for doing a deep walk. | |
962 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
963 | NestedVisitorMap::All(&self.tcx.hir()) | |
964 | } | |
965 | ||
966 | fn visit_mod(&mut self, _m: &'tcx hir::Mod, _s: Span, _n: hir::HirId) { | |
967 | // Don't visit nested modules, since we run a separate visitor walk | |
968 | // for each module in `privacy_access_levels` | |
969 | } | |
970 | ||
971 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
972 | let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body)); | |
973 | let orig_in_body = mem::replace(&mut self.in_body, true); | |
974 | let body = self.tcx.hir().body(body); | |
975 | self.visit_body(body); | |
976 | self.tables = orig_tables; | |
977 | self.in_body = orig_in_body; | |
978 | } | |
979 | ||
980 | fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty) { | |
981 | self.span = hir_ty.span; | |
982 | if self.in_body { | |
983 | // Types in bodies. | |
984 | if self.visit(self.tables.node_type(hir_ty.hir_id)) { | |
985 | return; | |
986 | } | |
987 | } else { | |
988 | // Types in signatures. | |
989 | // FIXME: This is very ineffective. Ideally each HIR type should be converted | |
990 | // into a semantic type only once and the result should be cached somehow. | |
991 | if self.visit(rustc_typeck::hir_ty_to_ty(self.tcx, hir_ty)) { | |
992 | return; | |
993 | } | |
994 | } | |
995 | ||
996 | intravisit::walk_ty(self, hir_ty); | |
997 | } | |
998 | ||
999 | fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef) { | |
1000 | self.span = trait_ref.path.span; | |
1001 | if !self.in_body { | |
1002 | // Avoid calling `hir_trait_to_predicates` in bodies, it will ICE. | |
1003 | // The traits' privacy in bodies is already checked as a part of trait object types. | |
1004 | let (principal, projections) = | |
1005 | rustc_typeck::hir_trait_to_predicates(self.tcx, trait_ref); | |
1006 | if self.visit_trait(*principal.skip_binder()) { | |
1007 | return; | |
1008 | } | |
1009 | for (poly_predicate, _) in projections { | |
1010 | let tcx = self.tcx; | |
1011 | if self.visit(poly_predicate.skip_binder().ty) || | |
1012 | self.visit_trait(poly_predicate.skip_binder().projection_ty.trait_ref(tcx)) { | |
1013 | return; | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | intravisit::walk_trait_ref(self, trait_ref); | |
1019 | } | |
1020 | ||
1021 | // Check types of expressions | |
1022 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { | |
1023 | if self.check_expr_pat_type(expr.hir_id, expr.span) { | |
1024 | // Do not check nested expressions if the error already happened. | |
1025 | return; | |
1026 | } | |
1027 | match expr.node { | |
1028 | hir::ExprKind::Assign(.., ref rhs) | hir::ExprKind::Match(ref rhs, ..) => { | |
1029 | // Do not report duplicate errors for `x = y` and `match x { ... }`. | |
1030 | if self.check_expr_pat_type(rhs.hir_id, rhs.span) { | |
1031 | return; | |
1032 | } | |
1033 | } | |
1034 | hir::ExprKind::MethodCall(_, span, _) => { | |
1035 | // Method calls have to be checked specially. | |
1036 | self.span = span; | |
1037 | if let Some(def) = self.tables.type_dependent_defs().get(expr.hir_id) { | |
1038 | if self.visit(self.tcx.type_of(def.def_id())) { | |
1039 | return; | |
1040 | } | |
1041 | } else { | |
1042 | self.tcx.sess.delay_span_bug(expr.span, | |
1043 | "no type-dependent def for method call"); | |
1044 | } | |
1045 | } | |
1046 | _ => {} | |
1047 | } | |
1048 | ||
1049 | intravisit::walk_expr(self, expr); | |
1050 | } | |
1051 | ||
1052 | // Prohibit access to associated items with insufficient nominal visibility. | |
1053 | // | |
1054 | // Additionally, until better reachability analysis for macros 2.0 is available, | |
1055 | // we prohibit access to private statics from other crates, this allows to give | |
1056 | // more code internal visibility at link time. (Access to private functions | |
1057 | // is already prohibited by type privacy for function types.) | |
1058 | fn visit_qpath(&mut self, qpath: &'tcx hir::QPath, id: hir::HirId, span: Span) { | |
1059 | let def = match *qpath { | |
1060 | hir::QPath::Resolved(_, ref path) => match path.def { | |
1061 | Def::Method(..) | Def::AssociatedConst(..) | | |
1062 | Def::AssociatedTy(..) | Def::AssociatedExistential(..) | | |
1063 | Def::Static(..) => Some(path.def), | |
1064 | _ => None, | |
1065 | } | |
1066 | hir::QPath::TypeRelative(..) => { | |
1067 | self.tables.type_dependent_defs().get(id).cloned() | |
1068 | } | |
1069 | }; | |
1070 | if let Some(def) = def { | |
1071 | let def_id = def.def_id(); | |
1072 | let is_local_static = if let Def::Static(..) = def { def_id.is_local() } else { false }; | |
1073 | if !self.item_is_accessible(def_id) && !is_local_static { | |
1074 | let name = match *qpath { | |
1075 | hir::QPath::Resolved(_, ref path) => path.to_string(), | |
1076 | hir::QPath::TypeRelative(_, ref segment) => segment.ident.to_string(), | |
1077 | }; | |
1078 | let msg = format!("{} `{}` is private", def.kind_name(), name); | |
1079 | self.tcx.sess.span_err(span, &msg); | |
1080 | return; | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | intravisit::walk_qpath(self, qpath, id, span); | |
1085 | } | |
1086 | ||
1087 | // Check types of patterns. | |
1088 | fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { | |
1089 | if self.check_expr_pat_type(pattern.hir_id, pattern.span) { | |
1090 | // Do not check nested patterns if the error already happened. | |
1091 | return; | |
1092 | } | |
1093 | ||
1094 | intravisit::walk_pat(self, pattern); | |
1095 | } | |
1096 | ||
1097 | fn visit_local(&mut self, local: &'tcx hir::Local) { | |
1098 | if let Some(ref init) = local.init { | |
1099 | if self.check_expr_pat_type(init.hir_id, init.span) { | |
1100 | // Do not report duplicate errors for `let x = y`. | |
1101 | return; | |
1102 | } | |
1103 | } | |
1104 | ||
1105 | intravisit::walk_local(self, local); | |
1106 | } | |
1107 | ||
1108 | // Check types in item interfaces. | |
1109 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
1110 | let orig_current_item = | |
1111 | mem::replace(&mut self.current_item, self.tcx.hir().local_def_id(item.id)); | |
1112 | let orig_in_body = mem::replace(&mut self.in_body, false); | |
1113 | let orig_tables = | |
1114 | mem::replace(&mut self.tables, item_tables(self.tcx, item.id, self.empty_tables)); | |
1115 | intravisit::walk_item(self, item); | |
1116 | self.tables = orig_tables; | |
1117 | self.in_body = orig_in_body; | |
1118 | self.current_item = orig_current_item; | |
1119 | } | |
1120 | ||
1121 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { | |
1122 | let orig_tables = | |
1123 | mem::replace(&mut self.tables, item_tables(self.tcx, ti.id, self.empty_tables)); | |
1124 | intravisit::walk_trait_item(self, ti); | |
1125 | self.tables = orig_tables; | |
1126 | } | |
1127 | ||
1128 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { | |
1129 | let orig_tables = | |
1130 | mem::replace(&mut self.tables, item_tables(self.tcx, ii.id, self.empty_tables)); | |
1131 | intravisit::walk_impl_item(self, ii); | |
1132 | self.tables = orig_tables; | |
1133 | } | |
1134 | } | |
1135 | ||
1136 | impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for TypePrivacyVisitor<'a, 'tcx> { | |
1137 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx } | |
1138 | fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { | |
1139 | self.check_def_id(def_id, kind, descr) | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | /////////////////////////////////////////////////////////////////////////////// | |
1144 | /// Obsolete visitors for checking for private items in public interfaces. | |
1145 | /// These visitors are supposed to be kept in frozen state and produce an | |
1146 | /// "old error node set". For backward compatibility the new visitor reports | |
1147 | /// warnings instead of hard errors when the erroneous node is not in this old set. | |
1148 | /////////////////////////////////////////////////////////////////////////////// | |
1149 | ||
1150 | struct ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx: 'a> { | |
1151 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1152 | access_levels: &'a AccessLevels, | |
1153 | in_variant: bool, | |
1154 | // Set of errors produced by this obsolete visitor. | |
1155 | old_error_set: NodeSet, | |
1156 | } | |
1157 | ||
1158 | struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> { | |
1159 | inner: &'a ObsoleteVisiblePrivateTypesVisitor<'b, 'tcx>, | |
1160 | /// Whether the type refers to private types. | |
1161 | contains_private: bool, | |
1162 | /// Whether we've recurred at all (i.e., if we're pointing at the | |
1163 | /// first type on which `visit_ty` was called). | |
1164 | at_outer_type: bool, | |
1165 | /// Whether that first type is a public path. | |
1166 | outer_type_is_public_path: bool, | |
1167 | } | |
1168 | ||
1169 | impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { | |
1170 | fn path_is_private_type(&self, path: &hir::Path) -> bool { | |
1171 | let did = match path.def { | |
1172 | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => return false, | |
1173 | def => def.def_id(), | |
1174 | }; | |
1175 | ||
1176 | // A path can only be private if: | |
1177 | // it's in this crate... | |
1178 | if let Some(node_id) = self.tcx.hir().as_local_node_id(did) { | |
1179 | // .. and it corresponds to a private type in the AST (this returns | |
1180 | // `None` for type parameters). | |
1181 | match self.tcx.hir().find(node_id) { | |
1182 | Some(Node::Item(ref item)) => !item.vis.node.is_pub(), | |
1183 | Some(_) | None => false, | |
1184 | } | |
1185 | } else { | |
1186 | return false | |
1187 | } | |
1188 | } | |
1189 | ||
1190 | fn trait_is_public(&self, trait_id: ast::NodeId) -> bool { | |
1191 | // FIXME: this would preferably be using `exported_items`, but all | |
1192 | // traits are exported currently (see `EmbargoVisitor.exported_trait`). | |
1193 | self.access_levels.is_public(trait_id) | |
1194 | } | |
1195 | ||
1196 | fn check_generic_bound(&mut self, bound: &hir::GenericBound) { | |
1197 | if let hir::GenericBound::Trait(ref trait_ref, _) = *bound { | |
1198 | if self.path_is_private_type(&trait_ref.trait_ref.path) { | |
1199 | self.old_error_set.insert(trait_ref.trait_ref.ref_id); | |
1200 | } | |
1201 | } | |
1202 | } | |
1203 | ||
1204 | fn item_is_public(&self, id: &ast::NodeId, vis: &hir::Visibility) -> bool { | |
1205 | self.access_levels.is_reachable(*id) || vis.node.is_pub() | |
1206 | } | |
1207 | } | |
1208 | ||
1209 | impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { | |
1210 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { | |
1211 | NestedVisitorMap::None | |
1212 | } | |
1213 | ||
1214 | fn visit_ty(&mut self, ty: &hir::Ty) { | |
1215 | if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = ty.node { | |
1216 | if self.inner.path_is_private_type(path) { | |
1217 | self.contains_private = true; | |
1218 | // Found what we're looking for, so let's stop working. | |
1219 | return | |
1220 | } | |
1221 | } | |
1222 | if let hir::TyKind::Path(_) = ty.node { | |
1223 | if self.at_outer_type { | |
1224 | self.outer_type_is_public_path = true; | |
1225 | } | |
1226 | } | |
1227 | self.at_outer_type = false; | |
1228 | intravisit::walk_ty(self, ty) | |
1229 | } | |
1230 | ||
1231 | // Don't want to recurse into `[, .. expr]`. | |
1232 | fn visit_expr(&mut self, _: &hir::Expr) {} | |
1233 | } | |
1234 | ||
1235 | impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { | |
1236 | /// We want to visit items in the context of their containing | |
1237 | /// module and so forth, so supply a crate for doing a deep walk. | |
1238 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
1239 | NestedVisitorMap::All(&self.tcx.hir()) | |
1240 | } | |
1241 | ||
1242 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
1243 | match item.node { | |
1244 | // Contents of a private mod can be re-exported, so we need | |
1245 | // to check internals. | |
1246 | hir::ItemKind::Mod(_) => {} | |
1247 | ||
1248 | // An `extern {}` doesn't introduce a new privacy | |
1249 | // namespace (the contents have their own privacies). | |
1250 | hir::ItemKind::ForeignMod(_) => {} | |
1251 | ||
1252 | hir::ItemKind::Trait(.., ref bounds, _) => { | |
1253 | if !self.trait_is_public(item.id) { | |
1254 | return | |
1255 | } | |
1256 | ||
1257 | for bound in bounds.iter() { | |
1258 | self.check_generic_bound(bound) | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | // Impls need some special handling to try to offer useful | |
1263 | // error messages without (too many) false positives | |
1264 | // (i.e., we could just return here to not check them at | |
1265 | // all, or some worse estimation of whether an impl is | |
1266 | // publicly visible). | |
1267 | hir::ItemKind::Impl(.., ref g, ref trait_ref, ref self_, ref impl_item_refs) => { | |
1268 | // `impl [... for] Private` is never visible. | |
1269 | let self_contains_private; | |
1270 | // `impl [... for] Public<...>`, but not `impl [... for] | |
1271 | // Vec<Public>` or `(Public,)`, etc. | |
1272 | let self_is_public_path; | |
1273 | ||
1274 | // Check the properties of the `Self` type: | |
1275 | { | |
1276 | let mut visitor = ObsoleteCheckTypeForPrivatenessVisitor { | |
1277 | inner: self, | |
1278 | contains_private: false, | |
1279 | at_outer_type: true, | |
1280 | outer_type_is_public_path: false, | |
1281 | }; | |
1282 | visitor.visit_ty(&self_); | |
1283 | self_contains_private = visitor.contains_private; | |
1284 | self_is_public_path = visitor.outer_type_is_public_path; | |
1285 | } | |
1286 | ||
1287 | // Miscellaneous info about the impl: | |
1288 | ||
1289 | // `true` iff this is `impl Private for ...`. | |
1290 | let not_private_trait = | |
1291 | trait_ref.as_ref().map_or(true, // no trait counts as public trait | |
1292 | |tr| { | |
1293 | let did = tr.path.def.def_id(); | |
1294 | ||
1295 | if let Some(node_id) = self.tcx.hir().as_local_node_id(did) { | |
1296 | self.trait_is_public(node_id) | |
1297 | } else { | |
1298 | true // external traits must be public | |
1299 | } | |
1300 | }); | |
1301 | ||
1302 | // `true` iff this is a trait impl or at least one method is public. | |
1303 | // | |
1304 | // `impl Public { $( fn ...() {} )* }` is not visible. | |
1305 | // | |
1306 | // This is required over just using the methods' privacy | |
1307 | // directly because we might have `impl<T: Foo<Private>> ...`, | |
1308 | // and we shouldn't warn about the generics if all the methods | |
1309 | // are private (because `T` won't be visible externally). | |
1310 | let trait_or_some_public_method = | |
1311 | trait_ref.is_some() || | |
1312 | impl_item_refs.iter() | |
1313 | .any(|impl_item_ref| { | |
1314 | let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); | |
1315 | match impl_item.node { | |
1316 | hir::ImplItemKind::Const(..) | | |
1317 | hir::ImplItemKind::Method(..) => { | |
1318 | self.access_levels.is_reachable(impl_item.id) | |
1319 | } | |
1320 | hir::ImplItemKind::Existential(..) | | |
1321 | hir::ImplItemKind::Type(_) => false, | |
1322 | } | |
1323 | }); | |
1324 | ||
1325 | if !self_contains_private && | |
1326 | not_private_trait && | |
1327 | trait_or_some_public_method { | |
1328 | ||
1329 | intravisit::walk_generics(self, g); | |
1330 | ||
1331 | match *trait_ref { | |
1332 | None => { | |
1333 | for impl_item_ref in impl_item_refs { | |
1334 | // This is where we choose whether to walk down | |
1335 | // further into the impl to check its items. We | |
1336 | // should only walk into public items so that we | |
1337 | // don't erroneously report errors for private | |
1338 | // types in private items. | |
1339 | let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); | |
1340 | match impl_item.node { | |
1341 | hir::ImplItemKind::Const(..) | | |
1342 | hir::ImplItemKind::Method(..) | |
1343 | if self.item_is_public(&impl_item.id, &impl_item.vis) => | |
1344 | { | |
1345 | intravisit::walk_impl_item(self, impl_item) | |
1346 | } | |
1347 | hir::ImplItemKind::Type(..) => { | |
1348 | intravisit::walk_impl_item(self, impl_item) | |
1349 | } | |
1350 | _ => {} | |
1351 | } | |
1352 | } | |
1353 | } | |
1354 | Some(ref tr) => { | |
1355 | // Any private types in a trait impl fall into three | |
1356 | // categories. | |
1357 | // 1. mentioned in the trait definition | |
1358 | // 2. mentioned in the type params/generics | |
1359 | // 3. mentioned in the associated types of the impl | |
1360 | // | |
1361 | // Those in 1. can only occur if the trait is in | |
1362 | // this crate and will've been warned about on the | |
1363 | // trait definition (there's no need to warn twice | |
1364 | // so we don't check the methods). | |
1365 | // | |
1366 | // Those in 2. are warned via walk_generics and this | |
1367 | // call here. | |
1368 | intravisit::walk_path(self, &tr.path); | |
1369 | ||
1370 | // Those in 3. are warned with this call. | |
1371 | for impl_item_ref in impl_item_refs { | |
1372 | let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); | |
1373 | if let hir::ImplItemKind::Type(ref ty) = impl_item.node { | |
1374 | self.visit_ty(ty); | |
1375 | } | |
1376 | } | |
1377 | } | |
1378 | } | |
1379 | } else if trait_ref.is_none() && self_is_public_path { | |
1380 | // `impl Public<Private> { ... }`. Any public static | |
1381 | // methods will be visible as `Public::foo`. | |
1382 | let mut found_pub_static = false; | |
1383 | for impl_item_ref in impl_item_refs { | |
1384 | if self.item_is_public(&impl_item_ref.id.node_id, &impl_item_ref.vis) { | |
1385 | let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); | |
1386 | match impl_item_ref.kind { | |
1387 | AssociatedItemKind::Const => { | |
1388 | found_pub_static = true; | |
1389 | intravisit::walk_impl_item(self, impl_item); | |
1390 | } | |
1391 | AssociatedItemKind::Method { has_self: false } => { | |
1392 | found_pub_static = true; | |
1393 | intravisit::walk_impl_item(self, impl_item); | |
1394 | } | |
1395 | _ => {} | |
1396 | } | |
1397 | } | |
1398 | } | |
1399 | if found_pub_static { | |
1400 | intravisit::walk_generics(self, g) | |
1401 | } | |
1402 | } | |
1403 | return | |
1404 | } | |
1405 | ||
1406 | // `type ... = ...;` can contain private types, because | |
1407 | // we're introducing a new name. | |
1408 | hir::ItemKind::Ty(..) => return, | |
1409 | ||
1410 | // Not at all public, so we don't care. | |
1411 | _ if !self.item_is_public(&item.id, &item.vis) => { | |
1412 | return; | |
1413 | } | |
1414 | ||
1415 | _ => {} | |
1416 | } | |
1417 | ||
1418 | // We've carefully constructed it so that if we're here, then | |
1419 | // any `visit_ty`'s will be called on things that are in | |
1420 | // public signatures, i.e., things that we're interested in for | |
1421 | // this visitor. | |
1422 | intravisit::walk_item(self, item); | |
1423 | } | |
1424 | ||
1425 | fn visit_generics(&mut self, generics: &'tcx hir::Generics) { | |
1426 | for param in &generics.params { | |
1427 | for bound in ¶m.bounds { | |
1428 | self.check_generic_bound(bound); | |
1429 | } | |
1430 | } | |
1431 | for predicate in &generics.where_clause.predicates { | |
1432 | match predicate { | |
1433 | hir::WherePredicate::BoundPredicate(bound_pred) => { | |
1434 | for bound in bound_pred.bounds.iter() { | |
1435 | self.check_generic_bound(bound) | |
1436 | } | |
1437 | } | |
1438 | hir::WherePredicate::RegionPredicate(_) => {} | |
1439 | hir::WherePredicate::EqPredicate(eq_pred) => { | |
1440 | self.visit_ty(&eq_pred.rhs_ty); | |
1441 | } | |
1442 | } | |
1443 | } | |
1444 | } | |
1445 | ||
1446 | fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { | |
1447 | if self.access_levels.is_reachable(item.id) { | |
1448 | intravisit::walk_foreign_item(self, item) | |
1449 | } | |
1450 | } | |
1451 | ||
1452 | fn visit_ty(&mut self, t: &'tcx hir::Ty) { | |
1453 | if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = t.node { | |
1454 | if self.path_is_private_type(path) { | |
1455 | self.old_error_set.insert(t.id); | |
1456 | } | |
1457 | } | |
1458 | intravisit::walk_ty(self, t) | |
1459 | } | |
1460 | ||
1461 | fn visit_variant(&mut self, | |
1462 | v: &'tcx hir::Variant, | |
1463 | g: &'tcx hir::Generics, | |
1464 | item_id: hir::HirId) { | |
1465 | if self.access_levels.is_reachable(v.node.data.id()) { | |
1466 | self.in_variant = true; | |
1467 | intravisit::walk_variant(self, v, g, item_id); | |
1468 | self.in_variant = false; | |
1469 | } | |
1470 | } | |
1471 | ||
1472 | fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { | |
1473 | if s.vis.node.is_pub() || self.in_variant { | |
1474 | intravisit::walk_struct_field(self, s); | |
1475 | } | |
1476 | } | |
1477 | ||
1478 | // We don't need to introspect into these at all: an | |
1479 | // expression/block context can't possibly contain exported things. | |
1480 | // (Making them no-ops stops us from traversing the whole AST without | |
1481 | // having to be super careful about our `walk_...` calls above.) | |
1482 | fn visit_block(&mut self, _: &'tcx hir::Block) {} | |
1483 | fn visit_expr(&mut self, _: &'tcx hir::Expr) {} | |
1484 | } | |
1485 | ||
1486 | /////////////////////////////////////////////////////////////////////////////// | |
1487 | /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and | |
1488 | /// finds any private components in it. | |
1489 | /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types | |
1490 | /// and traits in public interfaces. | |
1491 | /////////////////////////////////////////////////////////////////////////////// | |
1492 | ||
1493 | struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> { | |
1494 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1495 | item_id: ast::NodeId, | |
1496 | item_def_id: DefId, | |
1497 | span: Span, | |
1498 | /// The visitor checks that each component type is at least this visible. | |
1499 | required_visibility: ty::Visibility, | |
1500 | has_pub_restricted: bool, | |
1501 | has_old_errors: bool, | |
1502 | in_assoc_ty: bool, | |
1503 | private_crates: FxHashSet<CrateNum> | |
1504 | } | |
1505 | ||
1506 | impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
1507 | fn generics(&mut self) -> &mut Self { | |
1508 | for param in &self.tcx.generics_of(self.item_def_id).params { | |
1509 | match param.kind { | |
1510 | GenericParamDefKind::Type { has_default, .. } => { | |
1511 | if has_default { | |
1512 | self.visit(self.tcx.type_of(param.def_id)); | |
1513 | } | |
1514 | } | |
1515 | GenericParamDefKind::Lifetime => {} | |
1516 | } | |
1517 | } | |
1518 | self | |
1519 | } | |
1520 | ||
1521 | fn predicates(&mut self) -> &mut Self { | |
1522 | // N.B., we use `explicit_predicates_of` and not `predicates_of` | |
1523 | // because we don't want to report privacy errors due to where | |
1524 | // clauses that the compiler inferred. We only want to | |
1525 | // consider the ones that the user wrote. This is important | |
1526 | // for the inferred outlives rules; see | |
1527 | // `src/test/ui/rfc-2093-infer-outlives/privacy.rs`. | |
1528 | self.visit_predicates(self.tcx.explicit_predicates_of(self.item_def_id)); | |
1529 | self | |
1530 | } | |
1531 | ||
1532 | fn ty(&mut self) -> &mut Self { | |
1533 | self.visit(self.tcx.type_of(self.item_def_id)); | |
1534 | self | |
1535 | } | |
1536 | ||
1537 | fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { | |
1538 | if self.leaks_private_dep(def_id) { | |
1539 | self.tcx.lint_node(lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, | |
1540 | self.item_id, | |
1541 | self.span, | |
1542 | &format!("{} `{}` from private dependency '{}' in public \ | |
1543 | interface", kind, descr, | |
1544 | self.tcx.crate_name(def_id.krate))); | |
1545 | ||
1546 | } | |
1547 | ||
1548 | let node_id = match self.tcx.hir().as_local_node_id(def_id) { | |
1549 | Some(node_id) => node_id, | |
1550 | None => return false, | |
1551 | }; | |
1552 | ||
1553 | let (vis, vis_span, vis_descr) = def_id_visibility(self.tcx, def_id); | |
1554 | if !vis.is_at_least(self.required_visibility, self.tcx) { | |
1555 | let msg = format!("{} {} `{}` in public interface", vis_descr, kind, descr); | |
1556 | if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty { | |
1557 | let mut err = if kind == "trait" { | |
1558 | struct_span_err!(self.tcx.sess, self.span, E0445, "{}", msg) | |
1559 | } else { | |
1560 | struct_span_err!(self.tcx.sess, self.span, E0446, "{}", msg) | |
1561 | }; | |
1562 | err.span_label(self.span, format!("can't leak {} {}", vis_descr, kind)); | |
1563 | err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); | |
1564 | err.emit(); | |
1565 | } else { | |
1566 | let err_code = if kind == "trait" { "E0445" } else { "E0446" }; | |
1567 | self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, node_id, self.span, | |
1568 | &format!("{} (error {})", msg, err_code)); | |
1569 | } | |
1570 | ||
1571 | } | |
1572 | ||
1573 | false | |
1574 | } | |
1575 | ||
1576 | /// An item is 'leaked' from a private dependency if all | |
1577 | /// of the following are true: | |
1578 | /// 1. It's contained within a public type | |
1579 | /// 2. It comes from a private crate | |
1580 | fn leaks_private_dep(&self, item_id: DefId) -> bool { | |
1581 | let ret = self.required_visibility == ty::Visibility::Public && | |
1582 | self.private_crates.contains(&item_id.krate); | |
1583 | ||
1584 | log::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret); | |
1585 | return ret; | |
1586 | } | |
1587 | } | |
1588 | ||
1589 | impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
1590 | fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx } | |
1591 | fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { | |
1592 | self.check_def_id(def_id, kind, descr) | |
1593 | } | |
1594 | } | |
1595 | ||
1596 | struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> { | |
1597 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1598 | has_pub_restricted: bool, | |
1599 | old_error_set: &'a NodeSet, | |
1600 | private_crates: FxHashSet<CrateNum> | |
1601 | } | |
1602 | ||
1603 | impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { | |
1604 | fn check(&self, item_id: ast::NodeId, required_visibility: ty::Visibility) | |
1605 | -> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
1606 | let mut has_old_errors = false; | |
1607 | ||
1608 | // Slow path taken only if there any errors in the crate. | |
1609 | for &id in self.old_error_set { | |
1610 | // Walk up the nodes until we find `item_id` (or we hit a root). | |
1611 | let mut id = id; | |
1612 | loop { | |
1613 | if id == item_id { | |
1614 | has_old_errors = true; | |
1615 | break; | |
1616 | } | |
1617 | let parent = self.tcx.hir().get_parent_node(id); | |
1618 | if parent == id { | |
1619 | break; | |
1620 | } | |
1621 | id = parent; | |
1622 | } | |
1623 | ||
1624 | if has_old_errors { | |
1625 | break; | |
1626 | } | |
1627 | } | |
1628 | ||
1629 | SearchInterfaceForPrivateItemsVisitor { | |
1630 | tcx: self.tcx, | |
1631 | item_id, | |
1632 | item_def_id: self.tcx.hir().local_def_id(item_id), | |
1633 | span: self.tcx.hir().span(item_id), | |
1634 | required_visibility, | |
1635 | has_pub_restricted: self.has_pub_restricted, | |
1636 | has_old_errors, | |
1637 | in_assoc_ty: false, | |
1638 | private_crates: self.private_crates.clone() | |
1639 | } | |
1640 | } | |
1641 | ||
1642 | fn check_trait_or_impl_item(&self, node_id: ast::NodeId, assoc_item_kind: AssociatedItemKind, | |
1643 | defaultness: hir::Defaultness, vis: ty::Visibility) { | |
1644 | let mut check = self.check(node_id, vis); | |
1645 | ||
1646 | let (check_ty, is_assoc_ty) = match assoc_item_kind { | |
1647 | AssociatedItemKind::Const | AssociatedItemKind::Method { .. } => (true, false), | |
1648 | AssociatedItemKind::Type => (defaultness.has_value(), true), | |
1649 | // `ty()` for existential types is the underlying type, | |
1650 | // it's not a part of interface, so we skip it. | |
1651 | AssociatedItemKind::Existential => (false, true), | |
1652 | }; | |
1653 | check.in_assoc_ty = is_assoc_ty; | |
1654 | check.generics().predicates(); | |
1655 | if check_ty { | |
1656 | check.ty(); | |
1657 | } | |
1658 | } | |
1659 | } | |
1660 | ||
1661 | impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { | |
1662 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
1663 | NestedVisitorMap::OnlyBodies(&self.tcx.hir()) | |
1664 | } | |
1665 | ||
1666 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
1667 | let tcx = self.tcx; | |
1668 | let item_visibility = ty::Visibility::from_hir(&item.vis, item.id, tcx); | |
1669 | ||
1670 | match item.node { | |
1671 | // Crates are always public. | |
1672 | hir::ItemKind::ExternCrate(..) => {} | |
1673 | // All nested items are checked by `visit_item`. | |
1674 | hir::ItemKind::Mod(..) => {} | |
1675 | // Checked in resolve. | |
1676 | hir::ItemKind::Use(..) => {} | |
1677 | // No subitems. | |
1678 | hir::ItemKind::GlobalAsm(..) => {} | |
1679 | // Subitems of these items have inherited publicity. | |
1680 | hir::ItemKind::Const(..) | hir::ItemKind::Static(..) | | |
1681 | hir::ItemKind::Fn(..) | hir::ItemKind::Ty(..) => { | |
1682 | self.check(item.id, item_visibility).generics().predicates().ty(); | |
1683 | } | |
1684 | hir::ItemKind::Existential(..) => { | |
1685 | // `ty()` for existential types is the underlying type, | |
1686 | // it's not a part of interface, so we skip it. | |
1687 | self.check(item.id, item_visibility).generics().predicates(); | |
1688 | } | |
1689 | hir::ItemKind::Trait(.., ref trait_item_refs) => { | |
1690 | self.check(item.id, item_visibility).generics().predicates(); | |
1691 | ||
1692 | for trait_item_ref in trait_item_refs { | |
1693 | self.check_trait_or_impl_item(trait_item_ref.id.node_id, trait_item_ref.kind, | |
1694 | trait_item_ref.defaultness, item_visibility); | |
1695 | } | |
1696 | } | |
1697 | hir::ItemKind::TraitAlias(..) => { | |
1698 | self.check(item.id, item_visibility).generics().predicates(); | |
1699 | } | |
1700 | hir::ItemKind::Enum(ref def, _) => { | |
1701 | self.check(item.id, item_visibility).generics().predicates(); | |
1702 | ||
1703 | for variant in &def.variants { | |
1704 | for field in variant.node.data.fields() { | |
1705 | self.check(field.id, item_visibility).ty(); | |
1706 | } | |
1707 | } | |
1708 | } | |
1709 | // Subitems of foreign modules have their own publicity. | |
1710 | hir::ItemKind::ForeignMod(ref foreign_mod) => { | |
1711 | for foreign_item in &foreign_mod.items { | |
1712 | let vis = ty::Visibility::from_hir(&foreign_item.vis, item.id, tcx); | |
1713 | self.check(foreign_item.id, vis).generics().predicates().ty(); | |
1714 | } | |
1715 | } | |
1716 | // Subitems of structs and unions have their own publicity. | |
1717 | hir::ItemKind::Struct(ref struct_def, _) | | |
1718 | hir::ItemKind::Union(ref struct_def, _) => { | |
1719 | self.check(item.id, item_visibility).generics().predicates(); | |
1720 | ||
1721 | for field in struct_def.fields() { | |
1722 | let field_visibility = ty::Visibility::from_hir(&field.vis, item.id, tcx); | |
1723 | self.check(field.id, min(item_visibility, field_visibility, tcx)).ty(); | |
1724 | } | |
1725 | } | |
1726 | // An inherent impl is public when its type is public | |
1727 | // Subitems of inherent impls have their own publicity. | |
1728 | // A trait impl is public when both its type and its trait are public | |
1729 | // Subitems of trait impls have inherited publicity. | |
1730 | hir::ItemKind::Impl(.., ref trait_ref, _, ref impl_item_refs) => { | |
1731 | let impl_vis = ty::Visibility::of_impl(item.id, tcx, &Default::default()); | |
1732 | self.check(item.id, impl_vis).generics().predicates(); | |
1733 | for impl_item_ref in impl_item_refs { | |
1734 | let impl_item = tcx.hir().impl_item(impl_item_ref.id); | |
1735 | let impl_item_vis = if trait_ref.is_none() { | |
1736 | min(ty::Visibility::from_hir(&impl_item.vis, item.id, tcx), impl_vis, tcx) | |
1737 | } else { | |
1738 | impl_vis | |
1739 | }; | |
1740 | self.check_trait_or_impl_item(impl_item_ref.id.node_id, impl_item_ref.kind, | |
1741 | impl_item_ref.defaultness, impl_item_vis); | |
1742 | } | |
1743 | } | |
1744 | } | |
1745 | } | |
1746 | } | |
1747 | ||
1748 | pub fn provide(providers: &mut Providers<'_>) { | |
1749 | *providers = Providers { | |
1750 | privacy_access_levels, | |
1751 | check_mod_privacy, | |
1752 | ..*providers | |
1753 | }; | |
1754 | } | |
1755 | ||
1756 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Lrc<AccessLevels> { | |
1757 | tcx.privacy_access_levels(LOCAL_CRATE) | |
1758 | } | |
1759 | ||
1760 | fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) { | |
1761 | let empty_tables = ty::TypeckTables::empty(None); | |
1762 | ||
1763 | ||
1764 | // Check privacy of names not checked in previous compilation stages. | |
1765 | let mut visitor = NamePrivacyVisitor { | |
1766 | tcx, | |
1767 | tables: &empty_tables, | |
1768 | current_item: DUMMY_NODE_ID, | |
1769 | empty_tables: &empty_tables, | |
1770 | }; | |
1771 | let (module, span, node_id) = tcx.hir().get_module(module_def_id); | |
1772 | let hir_id = tcx.hir().node_to_hir_id(node_id); | |
1773 | intravisit::walk_mod(&mut visitor, module, hir_id); | |
1774 | ||
1775 | // Check privacy of explicitly written types and traits as well as | |
1776 | // inferred types of expressions and patterns. | |
1777 | let mut visitor = TypePrivacyVisitor { | |
1778 | tcx, | |
1779 | tables: &empty_tables, | |
1780 | current_item: module_def_id, | |
1781 | in_body: false, | |
1782 | span, | |
1783 | empty_tables: &empty_tables, | |
1784 | }; | |
1785 | intravisit::walk_mod(&mut visitor, module, hir_id); | |
1786 | } | |
1787 | ||
1788 | fn privacy_access_levels<'tcx>( | |
1789 | tcx: TyCtxt<'_, 'tcx, 'tcx>, | |
1790 | krate: CrateNum, | |
1791 | ) -> Lrc<AccessLevels> { | |
1792 | assert_eq!(krate, LOCAL_CRATE); | |
1793 | ||
1794 | let krate = tcx.hir().krate(); | |
1795 | ||
1796 | for &module in krate.modules.keys() { | |
1797 | tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module)); | |
1798 | } | |
1799 | ||
1800 | let private_crates: FxHashSet<CrateNum> = tcx.sess.opts.extern_private.iter() | |
1801 | .flat_map(|c| { | |
1802 | tcx.crates().iter().find(|&&krate| &tcx.crate_name(krate) == c).cloned() | |
1803 | }).collect(); | |
1804 | ||
1805 | ||
1806 | // Build up a set of all exported items in the AST. This is a set of all | |
1807 | // items which are reachable from external crates based on visibility. | |
1808 | let mut visitor = EmbargoVisitor { | |
1809 | tcx, | |
1810 | access_levels: Default::default(), | |
1811 | prev_level: Some(AccessLevel::Public), | |
1812 | changed: false, | |
1813 | }; | |
1814 | loop { | |
1815 | intravisit::walk_crate(&mut visitor, krate); | |
1816 | if visitor.changed { | |
1817 | visitor.changed = false; | |
1818 | } else { | |
1819 | break | |
1820 | } | |
1821 | } | |
1822 | visitor.update(ast::CRATE_NODE_ID, Some(AccessLevel::Public)); | |
1823 | ||
1824 | { | |
1825 | let mut visitor = ObsoleteVisiblePrivateTypesVisitor { | |
1826 | tcx, | |
1827 | access_levels: &visitor.access_levels, | |
1828 | in_variant: false, | |
1829 | old_error_set: Default::default(), | |
1830 | }; | |
1831 | intravisit::walk_crate(&mut visitor, krate); | |
1832 | ||
1833 | ||
1834 | let has_pub_restricted = { | |
1835 | let mut pub_restricted_visitor = PubRestrictedVisitor { | |
1836 | tcx, | |
1837 | has_pub_restricted: false | |
1838 | }; | |
1839 | intravisit::walk_crate(&mut pub_restricted_visitor, krate); | |
1840 | pub_restricted_visitor.has_pub_restricted | |
1841 | }; | |
1842 | ||
1843 | // Check for private types and traits in public interfaces. | |
1844 | let mut visitor = PrivateItemsInPublicInterfacesVisitor { | |
1845 | tcx, | |
1846 | has_pub_restricted, | |
1847 | old_error_set: &visitor.old_error_set, | |
1848 | private_crates | |
1849 | }; | |
1850 | krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor)); | |
1851 | } | |
1852 | ||
1853 | Lrc::new(visitor.access_levels) | |
1854 | } | |
1855 | ||
1856 | __build_diagnostic_array! { librustc_privacy, DIAGNOSTICS } |