]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
e9174d1e | 11 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", |
7cac9316 XL |
12 | html_favicon_url = "https://doc.rust-lang.org/favicon.ico", |
13 | html_root_url = "https://doc.rust-lang.org/nightly/")] | |
32a655c1 | 14 | #![deny(warnings)] |
85aaf69f | 15 | |
85aaf69f | 16 | #![feature(rustc_diagnostic_macros)] |
7cac9316 | 17 | |
041b39d2 | 18 | #[macro_use] extern crate rustc; |
85aaf69f | 19 | #[macro_use] extern crate syntax; |
ea8adc8c | 20 | extern crate rustc_typeck; |
3157f602 | 21 | extern crate syntax_pos; |
e9174d1e | 22 | |
3157f602 | 23 | use rustc::hir::{self, PatKind}; |
cc61c64b | 24 | use rustc::hir::def::Def; |
041b39d2 | 25 | use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId}; |
476ff2be SL |
26 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; |
27 | use rustc::hir::itemlikevisit::DeepVisitor; | |
3157f602 | 28 | use rustc::lint; |
92a42be0 | 29 | use rustc::middle::privacy::{AccessLevel, AccessLevels}; |
476ff2be SL |
30 | use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; |
31 | use rustc::ty::fold::TypeVisitor; | |
cc61c64b | 32 | use rustc::ty::maps::Providers; |
54a0048b | 33 | use rustc::util::nodemap::NodeSet; |
7cac9316 XL |
34 | use syntax::ast::{self, CRATE_NODE_ID, Ident}; |
35 | use syntax::symbol::keywords; | |
36 | use syntax_pos::Span; | |
85aaf69f | 37 | |
3157f602 XL |
38 | use std::cmp; |
39 | use std::mem::replace; | |
cc61c64b | 40 | use std::rc::Rc; |
85aaf69f | 41 | |
3b2f2976 | 42 | mod diagnostics; |
85aaf69f | 43 | |
cc61c64b XL |
44 | //////////////////////////////////////////////////////////////////////////////// |
45 | /// Visitor used to determine if pub(restricted) is used anywhere in the crate. | |
46 | /// | |
47 | /// This is done so that `private_in_public` warnings can be turned into hard errors | |
48 | /// in crates that have been updated to use pub(restricted). | |
49 | //////////////////////////////////////////////////////////////////////////////// | |
50 | struct PubRestrictedVisitor<'a, 'tcx: 'a> { | |
51 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
52 | has_pub_restricted: bool, | |
53 | } | |
54 | ||
55 | impl<'a, 'tcx> Visitor<'tcx> for PubRestrictedVisitor<'a, 'tcx> { | |
56 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
57 | NestedVisitorMap::All(&self.tcx.hir) | |
58 | } | |
59 | fn visit_vis(&mut self, vis: &'tcx hir::Visibility) { | |
60 | self.has_pub_restricted = self.has_pub_restricted || vis.is_pub_restricted(); | |
61 | } | |
62 | } | |
63 | ||
85aaf69f SL |
64 | //////////////////////////////////////////////////////////////////////////////// |
65 | /// The embargo visitor, used to determine the exports of the ast | |
66 | //////////////////////////////////////////////////////////////////////////////// | |
67 | ||
68 | struct EmbargoVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 69 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
85aaf69f | 70 | |
92a42be0 SL |
71 | // Accessibility levels for reachable nodes |
72 | access_levels: AccessLevels, | |
73 | // Previous accessibility level, None means unreachable | |
74 | prev_level: Option<AccessLevel>, | |
75 | // Have something changed in the level map? | |
76 | changed: bool, | |
85aaf69f SL |
77 | } |
78 | ||
7453a54e | 79 | struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> { |
476ff2be | 80 | item_def_id: DefId, |
7453a54e SL |
81 | ev: &'b mut EmbargoVisitor<'a, 'tcx>, |
82 | } | |
83 | ||
85aaf69f | 84 | impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { |
476ff2be | 85 | fn item_ty_level(&self, item_def_id: DefId) -> Option<AccessLevel> { |
7cac9316 | 86 | let ty_def_id = match self.tcx.type_of(item_def_id).sty { |
476ff2be SL |
87 | ty::TyAdt(adt, _) => adt.did, |
88 | ty::TyDynamic(ref obj, ..) if obj.principal().is_some() => | |
89 | obj.principal().unwrap().def_id(), | |
041b39d2 | 90 | ty::TyProjection(ref proj) => proj.trait_ref(self.tcx).def_id, |
476ff2be SL |
91 | _ => return Some(AccessLevel::Public) |
92 | }; | |
32a655c1 | 93 | if let Some(node_id) = self.tcx.hir.as_local_node_id(ty_def_id) { |
476ff2be | 94 | self.get(node_id) |
92a42be0 SL |
95 | } else { |
96 | Some(AccessLevel::Public) | |
97 | } | |
98 | } | |
99 | ||
476ff2be SL |
100 | fn impl_trait_level(&self, impl_def_id: DefId) -> Option<AccessLevel> { |
101 | if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) { | |
32a655c1 | 102 | if let Some(node_id) = self.tcx.hir.as_local_node_id(trait_ref.def_id) { |
476ff2be SL |
103 | return self.get(node_id); |
104 | } | |
92a42be0 | 105 | } |
476ff2be | 106 | Some(AccessLevel::Public) |
92a42be0 SL |
107 | } |
108 | ||
109 | fn get(&self, id: ast::NodeId) -> Option<AccessLevel> { | |
110 | self.access_levels.map.get(&id).cloned() | |
111 | } | |
112 | ||
113 | // Updates node level and returns the updated level | |
114 | fn update(&mut self, id: ast::NodeId, level: Option<AccessLevel>) -> Option<AccessLevel> { | |
115 | let old_level = self.get(id); | |
116 | // Accessibility levels can only grow | |
117 | if level > old_level { | |
118 | self.access_levels.map.insert(id, level.unwrap()); | |
119 | self.changed = true; | |
120 | level | |
121 | } else { | |
122 | old_level | |
123 | } | |
85aaf69f | 124 | } |
7453a54e | 125 | |
476ff2be SL |
126 | fn reach<'b>(&'b mut self, item_id: ast::NodeId) |
127 | -> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { | |
128 | ReachEverythingInTheInterfaceVisitor { | |
32a655c1 | 129 | item_def_id: self.tcx.hir.local_def_id(item_id), |
476ff2be SL |
130 | ev: self, |
131 | } | |
7453a54e | 132 | } |
85aaf69f SL |
133 | } |
134 | ||
476ff2be | 135 | impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { |
92a42be0 SL |
136 | /// We want to visit items in the context of their containing |
137 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 138 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 139 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 | 140 | } |
85aaf69f | 141 | |
476ff2be | 142 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
92a42be0 SL |
143 | let inherited_item_level = match item.node { |
144 | // Impls inherit level from their types and traits | |
476ff2be | 145 | hir::ItemImpl(..) => { |
32a655c1 | 146 | let def_id = self.tcx.hir.local_def_id(item.id); |
476ff2be | 147 | cmp::min(self.item_ty_level(def_id), self.impl_trait_level(def_id)) |
92a42be0 | 148 | } |
476ff2be | 149 | hir::ItemDefaultImpl(..) => { |
32a655c1 | 150 | let def_id = self.tcx.hir.local_def_id(item.id); |
476ff2be | 151 | self.impl_trait_level(def_id) |
92a42be0 SL |
152 | } |
153 | // Foreign mods inherit level from parents | |
154 | hir::ItemForeignMod(..) => { | |
155 | self.prev_level | |
156 | } | |
157 | // Other `pub` items inherit levels from parents | |
cc61c64b XL |
158 | hir::ItemConst(..) | hir::ItemEnum(..) | hir::ItemExternCrate(..) | |
159 | hir::ItemGlobalAsm(..) | hir::ItemFn(..) | hir::ItemMod(..) | | |
160 | hir::ItemStatic(..) | hir::ItemStruct(..) | hir::ItemTrait(..) | | |
161 | hir::ItemTy(..) | hir::ItemUnion(..) | hir::ItemUse(..) => { | |
92a42be0 | 162 | if item.vis == hir::Public { self.prev_level } else { None } |
85aaf69f | 163 | } |
92a42be0 | 164 | }; |
85aaf69f | 165 | |
7453a54e | 166 | // Update level of the item itself |
92a42be0 | 167 | let item_level = self.update(item.id, inherited_item_level); |
85aaf69f | 168 | |
7453a54e | 169 | // Update levels of nested things |
85aaf69f | 170 | match item.node { |
92a42be0 | 171 | hir::ItemEnum(ref def, _) => { |
85aaf69f | 172 | for variant in &def.variants { |
92a42be0 SL |
173 | let variant_level = self.update(variant.node.data.id(), item_level); |
174 | for field in variant.node.data.fields() { | |
54a0048b | 175 | self.update(field.id, variant_level); |
92a42be0 | 176 | } |
85aaf69f SL |
177 | } |
178 | } | |
476ff2be SL |
179 | hir::ItemImpl(.., None, _, ref impl_item_refs) => { |
180 | for impl_item_ref in impl_item_refs { | |
32a655c1 SL |
181 | if impl_item_ref.vis == hir::Public { |
182 | self.update(impl_item_ref.id.node_id, item_level); | |
85aaf69f SL |
183 | } |
184 | } | |
185 | } | |
476ff2be SL |
186 | hir::ItemImpl(.., Some(_), _, ref impl_item_refs) => { |
187 | for impl_item_ref in impl_item_refs { | |
32a655c1 | 188 | self.update(impl_item_ref.id.node_id, item_level); |
92a42be0 SL |
189 | } |
190 | } | |
32a655c1 SL |
191 | hir::ItemTrait(.., ref trait_item_refs) => { |
192 | for trait_item_ref in trait_item_refs { | |
193 | self.update(trait_item_ref.id.node_id, item_level); | |
85aaf69f SL |
194 | } |
195 | } | |
9e0c209e | 196 | hir::ItemStruct(ref def, _) | hir::ItemUnion(ref def, _) => { |
b039eaaf | 197 | if !def.is_struct() { |
92a42be0 | 198 | self.update(def.id(), item_level); |
85aaf69f | 199 | } |
b039eaaf | 200 | for field in def.fields() { |
54a0048b SL |
201 | if field.vis == hir::Public { |
202 | self.update(field.id, item_level); | |
c34b1796 AL |
203 | } |
204 | } | |
85aaf69f | 205 | } |
92a42be0 SL |
206 | hir::ItemForeignMod(ref foreign_mod) => { |
207 | for foreign_item in &foreign_mod.items { | |
208 | if foreign_item.vis == hir::Public { | |
209 | self.update(foreign_item.id, item_level); | |
210 | } | |
211 | } | |
212 | } | |
cc61c64b XL |
213 | hir::ItemUse(..) | hir::ItemStatic(..) | hir::ItemConst(..) | |
214 | hir::ItemGlobalAsm(..) | hir::ItemTy(..) | hir::ItemMod(..) | | |
215 | hir::ItemFn(..) | hir::ItemExternCrate(..) | hir::ItemDefaultImpl(..) => {} | |
7453a54e SL |
216 | } |
217 | ||
218 | // Mark all items in interfaces of reachable items as reachable | |
219 | match item.node { | |
220 | // The interface is empty | |
221 | hir::ItemExternCrate(..) => {} | |
222 | // All nested items are checked by visit_item | |
223 | hir::ItemMod(..) => {} | |
224 | // Reexports are handled in visit_mod | |
225 | hir::ItemUse(..) => {} | |
476ff2be SL |
226 | // The interface is empty |
227 | hir::ItemDefaultImpl(..) => {} | |
cc61c64b XL |
228 | // The interface is empty |
229 | hir::ItemGlobalAsm(..) => {} | |
7453a54e | 230 | // Visit everything |
476ff2be SL |
231 | hir::ItemConst(..) | hir::ItemStatic(..) | |
232 | hir::ItemFn(..) | hir::ItemTy(..) => { | |
7453a54e | 233 | if item_level.is_some() { |
7cac9316 | 234 | self.reach(item.id).generics().predicates().ty(); |
7453a54e SL |
235 | } |
236 | } | |
32a655c1 | 237 | hir::ItemTrait(.., ref trait_item_refs) => { |
476ff2be SL |
238 | if item_level.is_some() { |
239 | self.reach(item.id).generics().predicates(); | |
240 | ||
32a655c1 SL |
241 | for trait_item_ref in trait_item_refs { |
242 | let mut reach = self.reach(trait_item_ref.id.node_id); | |
476ff2be SL |
243 | reach.generics().predicates(); |
244 | ||
32a655c1 SL |
245 | if trait_item_ref.kind == hir::AssociatedItemKind::Type && |
246 | !trait_item_ref.defaultness.has_value() { | |
476ff2be SL |
247 | // No type to visit. |
248 | } else { | |
7cac9316 | 249 | reach.ty(); |
476ff2be SL |
250 | } |
251 | } | |
252 | } | |
253 | } | |
254 | // Visit everything except for private impl items | |
32a655c1 | 255 | hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => { |
476ff2be SL |
256 | if item_level.is_some() { |
257 | self.reach(item.id).generics().predicates().impl_trait_ref(); | |
258 | ||
32a655c1 SL |
259 | for impl_item_ref in impl_item_refs { |
260 | let id = impl_item_ref.id.node_id; | |
476ff2be | 261 | if trait_ref.is_some() || self.get(id).is_some() { |
7cac9316 | 262 | self.reach(id).generics().predicates().ty(); |
476ff2be SL |
263 | } |
264 | } | |
265 | } | |
266 | } | |
267 | ||
7453a54e | 268 | // Visit everything, but enum variants have their own levels |
476ff2be | 269 | hir::ItemEnum(ref def, _) => { |
7453a54e | 270 | if item_level.is_some() { |
476ff2be | 271 | self.reach(item.id).generics().predicates(); |
7453a54e SL |
272 | } |
273 | for variant in &def.variants { | |
274 | if self.get(variant.node.data.id()).is_some() { | |
275 | for field in variant.node.data.fields() { | |
7cac9316 | 276 | self.reach(field.id).ty(); |
7453a54e SL |
277 | } |
278 | // Corner case: if the variant is reachable, but its | |
279 | // enum is not, make the enum reachable as well. | |
280 | self.update(item.id, Some(AccessLevel::Reachable)); | |
281 | } | |
282 | } | |
283 | } | |
284 | // Visit everything, but foreign items have their own levels | |
285 | hir::ItemForeignMod(ref foreign_mod) => { | |
286 | for foreign_item in &foreign_mod.items { | |
287 | if self.get(foreign_item.id).is_some() { | |
7cac9316 | 288 | self.reach(foreign_item.id).generics().predicates().ty(); |
7453a54e SL |
289 | } |
290 | } | |
291 | } | |
292 | // Visit everything except for private fields | |
476ff2be SL |
293 | hir::ItemStruct(ref struct_def, _) | |
294 | hir::ItemUnion(ref struct_def, _) => { | |
7453a54e | 295 | if item_level.is_some() { |
476ff2be | 296 | self.reach(item.id).generics().predicates(); |
7453a54e | 297 | for field in struct_def.fields() { |
54a0048b | 298 | if self.get(field.id).is_some() { |
7cac9316 | 299 | self.reach(field.id).ty(); |
85aaf69f SL |
300 | } |
301 | } | |
302 | } | |
303 | } | |
85aaf69f SL |
304 | } |
305 | ||
92a42be0 SL |
306 | let orig_level = self.prev_level; |
307 | self.prev_level = item_level; | |
308 | ||
309 | intravisit::walk_item(self, item); | |
85aaf69f | 310 | |
92a42be0 | 311 | self.prev_level = orig_level; |
85aaf69f SL |
312 | } |
313 | ||
476ff2be | 314 | fn visit_block(&mut self, b: &'tcx hir::Block) { |
92a42be0 SL |
315 | let orig_level = replace(&mut self.prev_level, None); |
316 | ||
317 | // Blocks can have public items, for example impls, but they always | |
318 | // start as completely private regardless of publicity of a function, | |
319 | // constant, type, field, etc. in which this block resides | |
320 | intravisit::walk_block(self, b); | |
321 | ||
322 | self.prev_level = orig_level; | |
85aaf69f SL |
323 | } |
324 | ||
476ff2be | 325 | fn visit_mod(&mut self, m: &'tcx hir::Mod, _sp: Span, id: ast::NodeId) { |
85aaf69f SL |
326 | // This code is here instead of in visit_item so that the |
327 | // crate module gets processed as well. | |
92a42be0 | 328 | if self.prev_level.is_some() { |
ea8adc8c XL |
329 | let def_id = self.tcx.hir.local_def_id(id); |
330 | if let Some(exports) = self.tcx.module_exports(def_id) { | |
331 | for export in exports.iter() { | |
32a655c1 | 332 | if let Some(node_id) = self.tcx.hir.as_local_node_id(export.def.def_id()) { |
9cc50fc6 SL |
333 | self.update(node_id, Some(AccessLevel::Exported)); |
334 | } | |
85aaf69f SL |
335 | } |
336 | } | |
337 | } | |
92a42be0 | 338 | |
5bcae85e | 339 | intravisit::walk_mod(self, m, id); |
92a42be0 SL |
340 | } |
341 | ||
476ff2be | 342 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { |
7cac9316 XL |
343 | if md.legacy { |
344 | self.update(md.id, Some(AccessLevel::Public)); | |
345 | return | |
346 | } | |
347 | ||
348 | let module_did = ty::DefIdTree::parent(self.tcx, self.tcx.hir.local_def_id(md.id)).unwrap(); | |
349 | let mut module_id = self.tcx.hir.as_local_node_id(module_did).unwrap(); | |
350 | let level = if md.vis == hir::Public { self.get(module_id) } else { None }; | |
351 | let level = self.update(md.id, level); | |
352 | if level.is_none() { | |
353 | return | |
354 | } | |
355 | ||
356 | loop { | |
357 | let module = if module_id == ast::CRATE_NODE_ID { | |
358 | &self.tcx.hir.krate().module | |
359 | } else if let hir::ItemMod(ref module) = self.tcx.hir.expect_item(module_id).node { | |
360 | module | |
361 | } else { | |
362 | unreachable!() | |
363 | }; | |
364 | for id in &module.item_ids { | |
365 | self.update(id.id, level); | |
366 | } | |
367 | if module_id == ast::CRATE_NODE_ID { | |
368 | break | |
369 | } | |
370 | module_id = self.tcx.hir.get_parent_node(module_id); | |
371 | } | |
85aaf69f | 372 | } |
85aaf69f | 373 | |
476ff2be SL |
374 | fn visit_ty(&mut self, ty: &'tcx hir::Ty) { |
375 | if let hir::TyImplTrait(..) = ty.node { | |
376 | if self.get(ty.id).is_some() { | |
377 | // Reach the (potentially private) type and the API being exposed. | |
7cac9316 | 378 | self.reach(ty.id).ty().predicates(); |
7453a54e SL |
379 | } |
380 | } | |
476ff2be SL |
381 | |
382 | intravisit::walk_ty(self, ty); | |
7453a54e SL |
383 | } |
384 | } | |
385 | ||
476ff2be SL |
386 | impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { |
387 | fn generics(&mut self) -> &mut Self { | |
7cac9316 | 388 | for def in &self.ev.tcx.generics_of(self.item_def_id).types { |
8bb4bdeb | 389 | if def.has_default { |
7cac9316 | 390 | self.ev.tcx.type_of(def.def_id).visit_with(self); |
8bb4bdeb XL |
391 | } |
392 | } | |
476ff2be SL |
393 | self |
394 | } | |
9e0c209e | 395 | |
476ff2be | 396 | fn predicates(&mut self) -> &mut Self { |
041b39d2 XL |
397 | let predicates = self.ev.tcx.predicates_of(self.item_def_id); |
398 | for predicate in &predicates.predicates { | |
399 | predicate.visit_with(self); | |
400 | match predicate { | |
401 | &ty::Predicate::Trait(poly_predicate) => { | |
402 | self.check_trait_ref(poly_predicate.skip_binder().trait_ref); | |
403 | }, | |
404 | &ty::Predicate::Projection(poly_predicate) => { | |
405 | let tcx = self.ev.tcx; | |
406 | self.check_trait_ref( | |
407 | poly_predicate.skip_binder().projection_ty.trait_ref(tcx) | |
408 | ); | |
409 | }, | |
410 | _ => (), | |
411 | }; | |
412 | } | |
476ff2be SL |
413 | self |
414 | } | |
415 | ||
7cac9316 | 416 | fn ty(&mut self) -> &mut Self { |
041b39d2 XL |
417 | let ty = self.ev.tcx.type_of(self.item_def_id); |
418 | ty.visit_with(self); | |
419 | if let ty::TyFnDef(def_id, _) = ty.sty { | |
420 | if def_id == self.item_def_id { | |
421 | self.ev.tcx.fn_sig(def_id).visit_with(self); | |
422 | } | |
423 | } | |
476ff2be SL |
424 | self |
425 | } | |
426 | ||
427 | fn impl_trait_ref(&mut self) -> &mut Self { | |
041b39d2 XL |
428 | if let Some(impl_trait_ref) = self.ev.tcx.impl_trait_ref(self.item_def_id) { |
429 | self.check_trait_ref(impl_trait_ref); | |
430 | impl_trait_ref.super_visit_with(self); | |
431 | } | |
476ff2be SL |
432 | self |
433 | } | |
041b39d2 XL |
434 | |
435 | fn check_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) { | |
436 | if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(trait_ref.def_id) { | |
437 | let item = self.ev.tcx.hir.expect_item(node_id); | |
438 | self.ev.update(item.id, Some(AccessLevel::Reachable)); | |
439 | } | |
440 | } | |
476ff2be SL |
441 | } |
442 | ||
443 | impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { | |
444 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
445 | let ty_def_id = match ty.sty { | |
446 | ty::TyAdt(adt, _) => Some(adt.did), | |
447 | ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), | |
041b39d2 | 448 | ty::TyProjection(ref proj) => Some(proj.item_def_id), |
476ff2be | 449 | ty::TyFnDef(def_id, ..) | |
3b2f2976 | 450 | ty::TyClosure(def_id, ..) | |
ea8adc8c | 451 | ty::TyGenerator(def_id, ..) | |
476ff2be SL |
452 | ty::TyAnon(def_id, _) => Some(def_id), |
453 | _ => None | |
454 | }; | |
7453a54e | 455 | |
476ff2be | 456 | if let Some(def_id) = ty_def_id { |
32a655c1 | 457 | if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(def_id) { |
476ff2be | 458 | self.ev.update(node_id, Some(AccessLevel::Reachable)); |
7453a54e SL |
459 | } |
460 | } | |
461 | ||
476ff2be | 462 | ty.super_visit_with(self) |
7453a54e | 463 | } |
7453a54e SL |
464 | } |
465 | ||
7cac9316 XL |
466 | ////////////////////////////////////////////////////////////////////////////////////// |
467 | /// Name privacy visitor, checks privacy and reports violations. | |
468 | /// Most of name privacy checks are performed during the main resolution phase, | |
469 | /// or later in type checking when field accesses and associated items are resolved. | |
470 | /// This pass performs remaining checks for fields in struct expressions and patterns. | |
471 | ////////////////////////////////////////////////////////////////////////////////////// | |
85aaf69f | 472 | |
7cac9316 | 473 | struct NamePrivacyVisitor<'a, 'tcx: 'a> { |
a7813a04 | 474 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
32a655c1 | 475 | tables: &'a ty::TypeckTables<'tcx>, |
7cac9316 | 476 | current_item: ast::NodeId, |
3b2f2976 | 477 | empty_tables: &'a ty::TypeckTables<'tcx>, |
85aaf69f SL |
478 | } |
479 | ||
7cac9316 XL |
480 | impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { |
481 | // Checks that a field is accessible. | |
476ff2be | 482 | fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) { |
ea8adc8c | 483 | let ident = Ident { ctxt: span.ctxt().modern(), ..keywords::Invalid.ident() }; |
7cac9316 XL |
484 | let def_id = self.tcx.adjust_ident(ident, def.did, self.current_item).1; |
485 | if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { | |
9e0c209e | 486 | struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", |
7cac9316 XL |
487 | field.name, def.variant_descr(), self.tcx.item_path_str(def.did)) |
488 | .span_label(span, format!("field `{}` is private", field.name)) | |
9e0c209e | 489 | .emit(); |
85aaf69f SL |
490 | } |
491 | } | |
85aaf69f SL |
492 | } |
493 | ||
3b2f2976 XL |
494 | // Set the correct TypeckTables for the given `item_id` (or an empty table if |
495 | // there is no TypeckTables for the item). | |
496 | fn update_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
497 | item_id: ast::NodeId, | |
498 | tables: &mut &'a ty::TypeckTables<'tcx>, | |
499 | empty_tables: &'a ty::TypeckTables<'tcx>) | |
500 | -> &'a ty::TypeckTables<'tcx> { | |
501 | let def_id = tcx.hir.local_def_id(item_id); | |
502 | ||
503 | if tcx.has_typeck_tables(def_id) { | |
504 | replace(tables, tcx.typeck_tables_of(def_id)) | |
505 | } else { | |
506 | replace(tables, empty_tables) | |
507 | } | |
508 | } | |
509 | ||
7cac9316 | 510 | impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> { |
92a42be0 SL |
511 | /// We want to visit items in the context of their containing |
512 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 513 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 SL |
514 | NestedVisitorMap::All(&self.tcx.hir) |
515 | } | |
516 | ||
517 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
7cac9316 | 518 | let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body)); |
32a655c1 SL |
519 | let body = self.tcx.hir.body(body); |
520 | self.visit_body(body); | |
7cac9316 | 521 | self.tables = orig_tables; |
92a42be0 SL |
522 | } |
523 | ||
476ff2be | 524 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
7cac9316 | 525 | let orig_current_item = replace(&mut self.current_item, item.id); |
3b2f2976 | 526 | let orig_tables = update_tables(self.tcx, item.id, &mut self.tables, self.empty_tables); |
92a42be0 | 527 | intravisit::walk_item(self, item); |
7cac9316 | 528 | self.current_item = orig_current_item; |
3b2f2976 XL |
529 | self.tables = orig_tables; |
530 | } | |
531 | ||
532 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { | |
533 | let orig_tables = update_tables(self.tcx, ti.id, &mut self.tables, self.empty_tables); | |
534 | intravisit::walk_trait_item(self, ti); | |
535 | self.tables = orig_tables; | |
536 | } | |
537 | ||
538 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { | |
539 | let orig_tables = update_tables(self.tcx, ii.id, &mut self.tables, self.empty_tables); | |
540 | intravisit::walk_impl_item(self, ii); | |
541 | self.tables = orig_tables; | |
85aaf69f SL |
542 | } |
543 | ||
476ff2be | 544 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { |
85aaf69f | 545 | match expr.node { |
7cac9316 | 546 | hir::ExprStruct(ref qpath, ref fields, ref base) => { |
3b2f2976 | 547 | let def = self.tables.qpath_def(qpath, expr.hir_id); |
32a655c1 | 548 | let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); |
476ff2be | 549 | let variant = adt.variant_of_def(def); |
7cac9316 XL |
550 | if let Some(ref base) = *base { |
551 | // If the expression uses FRU we need to make sure all the unmentioned fields | |
552 | // are checked for privacy (RFC 736). Rather than computing the set of | |
553 | // unmentioned fields, just check them all. | |
554 | for variant_field in &variant.fields { | |
555 | let field = fields.iter().find(|f| f.name.node == variant_field.name); | |
556 | let span = if let Some(f) = field { f.span } else { base.span }; | |
557 | self.check_field(span, adt, variant_field); | |
9e0c209e SL |
558 | } |
559 | } else { | |
7cac9316 XL |
560 | for field in fields { |
561 | self.check_field(field.span, adt, variant.field_named(field.name.node)); | |
9e0c209e | 562 | } |
85aaf69f SL |
563 | } |
564 | } | |
85aaf69f SL |
565 | _ => {} |
566 | } | |
567 | ||
92a42be0 | 568 | intravisit::walk_expr(self, expr); |
85aaf69f SL |
569 | } |
570 | ||
7cac9316 XL |
571 | fn visit_pat(&mut self, pat: &'tcx hir::Pat) { |
572 | match pat.node { | |
476ff2be | 573 | PatKind::Struct(ref qpath, ref fields, _) => { |
3b2f2976 | 574 | let def = self.tables.qpath_def(qpath, pat.hir_id); |
7cac9316 | 575 | let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap(); |
476ff2be | 576 | let variant = adt.variant_of_def(def); |
e9174d1e | 577 | for field in fields { |
9e0c209e | 578 | self.check_field(field.span, adt, variant.field_named(field.node.name)); |
85aaf69f SL |
579 | } |
580 | } | |
85aaf69f SL |
581 | _ => {} |
582 | } | |
583 | ||
7cac9316 | 584 | intravisit::walk_pat(self, pat); |
85aaf69f | 585 | } |
85aaf69f SL |
586 | } |
587 | ||
041b39d2 XL |
588 | //////////////////////////////////////////////////////////////////////////////////////////// |
589 | /// Type privacy visitor, checks types for privacy and reports violations. | |
590 | /// Both explicitly written types and inferred types of expressions and patters are checked. | |
591 | /// Checks are performed on "semantic" types regardless of names and their hygiene. | |
592 | //////////////////////////////////////////////////////////////////////////////////////////// | |
593 | ||
594 | struct TypePrivacyVisitor<'a, 'tcx: 'a> { | |
595 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
596 | tables: &'a ty::TypeckTables<'tcx>, | |
597 | current_item: DefId, | |
598 | span: Span, | |
3b2f2976 | 599 | empty_tables: &'a ty::TypeckTables<'tcx>, |
041b39d2 XL |
600 | } |
601 | ||
602 | impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { | |
603 | fn def_id_visibility(&self, did: DefId) -> ty::Visibility { | |
604 | match self.tcx.hir.as_local_node_id(did) { | |
605 | Some(node_id) => { | |
606 | let vis = match self.tcx.hir.get(node_id) { | |
607 | hir::map::NodeItem(item) => &item.vis, | |
608 | hir::map::NodeForeignItem(foreign_item) => &foreign_item.vis, | |
609 | hir::map::NodeImplItem(impl_item) => &impl_item.vis, | |
610 | hir::map::NodeTraitItem(..) | | |
611 | hir::map::NodeVariant(..) => { | |
612 | return self.def_id_visibility(self.tcx.hir.get_parent_did(node_id)); | |
613 | } | |
614 | hir::map::NodeStructCtor(vdata) => { | |
615 | let struct_node_id = self.tcx.hir.get_parent(node_id); | |
616 | let struct_vis = match self.tcx.hir.get(struct_node_id) { | |
617 | hir::map::NodeItem(item) => &item.vis, | |
618 | node => bug!("unexpected node kind: {:?}", node), | |
619 | }; | |
620 | let mut ctor_vis | |
621 | = ty::Visibility::from_hir(struct_vis, struct_node_id, self.tcx); | |
622 | for field in vdata.fields() { | |
623 | let field_vis = ty::Visibility::from_hir(&field.vis, node_id, self.tcx); | |
624 | if ctor_vis.is_at_least(field_vis, self.tcx) { | |
625 | ctor_vis = field_vis; | |
626 | } | |
627 | } | |
628 | return ctor_vis; | |
629 | } | |
630 | node => bug!("unexpected node kind: {:?}", node) | |
631 | }; | |
632 | ty::Visibility::from_hir(vis, node_id, self.tcx) | |
633 | } | |
ea8adc8c | 634 | None => self.tcx.visibility(did), |
041b39d2 XL |
635 | } |
636 | } | |
637 | ||
638 | fn item_is_accessible(&self, did: DefId) -> bool { | |
639 | self.def_id_visibility(did).is_accessible_from(self.current_item, self.tcx) | |
640 | } | |
641 | ||
642 | // Take node ID of an expression or pattern and check its type for privacy. | |
3b2f2976 | 643 | fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { |
041b39d2 XL |
644 | self.span = span; |
645 | if let Some(ty) = self.tables.node_id_to_type_opt(id) { | |
646 | if ty.visit_with(self) { | |
647 | return true; | |
648 | } | |
649 | } | |
650 | if self.tables.node_substs(id).visit_with(self) { | |
651 | return true; | |
652 | } | |
3b2f2976 | 653 | if let Some(adjustments) = self.tables.adjustments().get(id) { |
041b39d2 XL |
654 | for adjustment in adjustments { |
655 | if adjustment.target.visit_with(self) { | |
656 | return true; | |
657 | } | |
658 | } | |
659 | } | |
660 | false | |
661 | } | |
041b39d2 XL |
662 | } |
663 | ||
664 | impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { | |
665 | /// We want to visit items in the context of their containing | |
666 | /// module and so forth, so supply a crate for doing a deep walk. | |
667 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
668 | NestedVisitorMap::All(&self.tcx.hir) | |
669 | } | |
670 | ||
671 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
672 | let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body)); | |
673 | let body = self.tcx.hir.body(body); | |
674 | self.visit_body(body); | |
675 | self.tables = orig_tables; | |
676 | } | |
677 | ||
ea8adc8c XL |
678 | fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty) { |
679 | self.span = hir_ty.span; | |
680 | if let Some(ty) = self.tables.node_id_to_type_opt(hir_ty.hir_id) { | |
681 | // Types in bodies. | |
682 | if ty.visit_with(self) { | |
683 | return; | |
684 | } | |
685 | } else { | |
686 | // Types in signatures. | |
687 | // FIXME: This is very ineffective. Ideally each HIR type should be converted | |
688 | // into a semantic type only once and the result should be cached somehow. | |
689 | if rustc_typeck::hir_ty_to_ty(self.tcx, hir_ty).visit_with(self) { | |
690 | return; | |
691 | } | |
692 | } | |
693 | ||
694 | intravisit::walk_ty(self, hir_ty); | |
695 | } | |
696 | ||
697 | fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef) { | |
698 | if !self.item_is_accessible(trait_ref.path.def.def_id()) { | |
699 | let msg = format!("trait `{:?}` is private", trait_ref.path); | |
700 | self.tcx.sess.span_err(self.span, &msg); | |
701 | return; | |
702 | } | |
703 | ||
704 | intravisit::walk_trait_ref(self, trait_ref); | |
705 | } | |
706 | ||
041b39d2 XL |
707 | // Check types of expressions |
708 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { | |
3b2f2976 | 709 | if self.check_expr_pat_type(expr.hir_id, expr.span) { |
041b39d2 XL |
710 | // Do not check nested expressions if the error already happened. |
711 | return; | |
712 | } | |
713 | match expr.node { | |
714 | hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => { | |
715 | // Do not report duplicate errors for `x = y` and `match x { ... }`. | |
3b2f2976 | 716 | if self.check_expr_pat_type(rhs.hir_id, rhs.span) { |
041b39d2 XL |
717 | return; |
718 | } | |
719 | } | |
720 | hir::ExprMethodCall(_, span, _) => { | |
721 | // Method calls have to be checked specially. | |
3b2f2976 | 722 | let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id(); |
041b39d2 XL |
723 | self.span = span; |
724 | if self.tcx.type_of(def_id).visit_with(self) { | |
725 | return; | |
726 | } | |
727 | } | |
728 | _ => {} | |
729 | } | |
730 | ||
731 | intravisit::walk_expr(self, expr); | |
732 | } | |
733 | ||
734 | fn visit_qpath(&mut self, qpath: &'tcx hir::QPath, id: ast::NodeId, span: Span) { | |
735 | // Inherent associated constants don't have self type in substs, | |
736 | // we have to check it additionally. | |
737 | if let hir::QPath::TypeRelative(..) = *qpath { | |
3b2f2976 XL |
738 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
739 | if let Some(def) = self.tables.type_dependent_defs().get(hir_id).cloned() { | |
041b39d2 XL |
740 | if let Some(assoc_item) = self.tcx.opt_associated_item(def.def_id()) { |
741 | if let ty::ImplContainer(impl_def_id) = assoc_item.container { | |
742 | if self.tcx.type_of(impl_def_id).visit_with(self) { | |
743 | return; | |
744 | } | |
745 | } | |
746 | } | |
747 | } | |
748 | } | |
749 | ||
750 | intravisit::walk_qpath(self, qpath, id, span); | |
751 | } | |
752 | ||
753 | // Check types of patterns | |
754 | fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { | |
3b2f2976 | 755 | if self.check_expr_pat_type(pattern.hir_id, pattern.span) { |
041b39d2 XL |
756 | // Do not check nested patterns if the error already happened. |
757 | return; | |
758 | } | |
759 | ||
760 | intravisit::walk_pat(self, pattern); | |
761 | } | |
762 | ||
763 | fn visit_local(&mut self, local: &'tcx hir::Local) { | |
764 | if let Some(ref init) = local.init { | |
3b2f2976 | 765 | if self.check_expr_pat_type(init.hir_id, init.span) { |
041b39d2 XL |
766 | // Do not report duplicate errors for `let x = y`. |
767 | return; | |
768 | } | |
769 | } | |
770 | ||
771 | intravisit::walk_local(self, local); | |
772 | } | |
773 | ||
774 | // Check types in item interfaces | |
775 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
776 | let orig_current_item = self.current_item; | |
3b2f2976 XL |
777 | let orig_tables = update_tables(self.tcx, |
778 | item.id, | |
779 | &mut self.tables, | |
780 | self.empty_tables); | |
041b39d2 XL |
781 | self.current_item = self.tcx.hir.local_def_id(item.id); |
782 | intravisit::walk_item(self, item); | |
3b2f2976 | 783 | self.tables = orig_tables; |
041b39d2 XL |
784 | self.current_item = orig_current_item; |
785 | } | |
3b2f2976 XL |
786 | |
787 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { | |
788 | let orig_tables = update_tables(self.tcx, ti.id, &mut self.tables, self.empty_tables); | |
789 | intravisit::walk_trait_item(self, ti); | |
790 | self.tables = orig_tables; | |
791 | } | |
792 | ||
793 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { | |
794 | let orig_tables = update_tables(self.tcx, ii.id, &mut self.tables, self.empty_tables); | |
795 | intravisit::walk_impl_item(self, ii); | |
796 | self.tables = orig_tables; | |
797 | } | |
041b39d2 XL |
798 | } |
799 | ||
800 | impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { | |
801 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
802 | match ty.sty { | |
803 | ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => { | |
804 | if !self.item_is_accessible(def_id) { | |
805 | let msg = format!("type `{}` is private", ty); | |
806 | self.tcx.sess.span_err(self.span, &msg); | |
807 | return true; | |
808 | } | |
809 | if let ty::TyFnDef(..) = ty.sty { | |
810 | if self.tcx.fn_sig(def_id).visit_with(self) { | |
811 | return true; | |
812 | } | |
813 | } | |
814 | // Inherent static methods don't have self type in substs, | |
815 | // we have to check it additionally. | |
816 | if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) { | |
817 | if let ty::ImplContainer(impl_def_id) = assoc_item.container { | |
818 | if self.tcx.type_of(impl_def_id).visit_with(self) { | |
819 | return true; | |
820 | } | |
821 | } | |
822 | } | |
823 | } | |
824 | ty::TyDynamic(ref predicates, ..) => { | |
825 | let is_private = predicates.skip_binder().iter().any(|predicate| { | |
826 | let def_id = match *predicate { | |
827 | ty::ExistentialPredicate::Trait(trait_ref) => trait_ref.def_id, | |
828 | ty::ExistentialPredicate::Projection(proj) => | |
829 | proj.trait_ref(self.tcx).def_id, | |
830 | ty::ExistentialPredicate::AutoTrait(def_id) => def_id, | |
831 | }; | |
832 | !self.item_is_accessible(def_id) | |
833 | }); | |
834 | if is_private { | |
835 | let msg = format!("type `{}` is private", ty); | |
836 | self.tcx.sess.span_err(self.span, &msg); | |
837 | return true; | |
838 | } | |
839 | } | |
840 | ty::TyProjection(ref proj) => { | |
ea8adc8c XL |
841 | let trait_ref = proj.trait_ref(self.tcx); |
842 | if !self.item_is_accessible(trait_ref.def_id) { | |
843 | let msg = format!("trait `{}` is private", trait_ref); | |
844 | self.tcx.sess.span_err(self.span, &msg); | |
845 | return true; | |
846 | } | |
847 | if trait_ref.super_visit_with(self) { | |
041b39d2 XL |
848 | return true; |
849 | } | |
850 | } | |
851 | ty::TyAnon(def_id, ..) => { | |
852 | for predicate in &self.tcx.predicates_of(def_id).predicates { | |
853 | let trait_ref = match *predicate { | |
854 | ty::Predicate::Trait(ref poly_trait_predicate) => { | |
855 | Some(poly_trait_predicate.skip_binder().trait_ref) | |
856 | } | |
857 | ty::Predicate::Projection(ref poly_projection_predicate) => { | |
858 | if poly_projection_predicate.skip_binder().ty.visit_with(self) { | |
859 | return true; | |
860 | } | |
861 | Some(poly_projection_predicate.skip_binder() | |
862 | .projection_ty.trait_ref(self.tcx)) | |
863 | } | |
864 | ty::Predicate::TypeOutlives(..) => None, | |
865 | _ => bug!("unexpected predicate: {:?}", predicate), | |
866 | }; | |
867 | if let Some(trait_ref) = trait_ref { | |
868 | if !self.item_is_accessible(trait_ref.def_id) { | |
869 | let msg = format!("trait `{}` is private", trait_ref); | |
870 | self.tcx.sess.span_err(self.span, &msg); | |
871 | return true; | |
872 | } | |
873 | // `Self` here is the same `TyAnon`, so skip it to avoid infinite recursion | |
874 | for subst in trait_ref.substs.iter().skip(1) { | |
875 | if subst.visit_with(self) { | |
876 | return true; | |
877 | } | |
878 | } | |
879 | } | |
880 | } | |
881 | } | |
882 | _ => {} | |
883 | } | |
884 | ||
885 | ty.super_visit_with(self) | |
886 | } | |
887 | } | |
888 | ||
9cc50fc6 SL |
889 | /////////////////////////////////////////////////////////////////////////////// |
890 | /// Obsolete visitors for checking for private items in public interfaces. | |
891 | /// These visitors are supposed to be kept in frozen state and produce an | |
892 | /// "old error node set". For backward compatibility the new visitor reports | |
893 | /// warnings instead of hard errors when the erroneous node is not in this old set. | |
894 | /////////////////////////////////////////////////////////////////////////////// | |
895 | ||
896 | struct ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 897 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
92a42be0 | 898 | access_levels: &'a AccessLevels, |
85aaf69f | 899 | in_variant: bool, |
9cc50fc6 SL |
900 | // set of errors produced by this obsolete visitor |
901 | old_error_set: NodeSet, | |
85aaf69f SL |
902 | } |
903 | ||
9cc50fc6 SL |
904 | struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> { |
905 | inner: &'a ObsoleteVisiblePrivateTypesVisitor<'b, 'tcx>, | |
85aaf69f SL |
906 | /// whether the type refers to private types. |
907 | contains_private: bool, | |
908 | /// whether we've recurred at all (i.e. if we're pointing at the | |
909 | /// first type on which visit_ty was called). | |
910 | at_outer_type: bool, | |
911 | // whether that first type is a public path. | |
912 | outer_type_is_public_path: bool, | |
913 | } | |
914 | ||
9cc50fc6 | 915 | impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { |
476ff2be SL |
916 | fn path_is_private_type(&self, path: &hir::Path) -> bool { |
917 | let did = match path.def { | |
3157f602 XL |
918 | Def::PrimTy(..) | Def::SelfTy(..) => return false, |
919 | def => def.def_id(), | |
85aaf69f | 920 | }; |
b039eaaf | 921 | |
85aaf69f SL |
922 | // A path can only be private if: |
923 | // it's in this crate... | |
32a655c1 | 924 | if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { |
b039eaaf SL |
925 | // .. and it corresponds to a private type in the AST (this returns |
926 | // None for type parameters) | |
32a655c1 | 927 | match self.tcx.hir.find(node_id) { |
3157f602 | 928 | Some(hir::map::NodeItem(ref item)) => item.vis != hir::Public, |
b039eaaf SL |
929 | Some(_) | None => false, |
930 | } | |
931 | } else { | |
85aaf69f SL |
932 | return false |
933 | } | |
85aaf69f SL |
934 | } |
935 | ||
936 | fn trait_is_public(&self, trait_id: ast::NodeId) -> bool { | |
937 | // FIXME: this would preferably be using `exported_items`, but all | |
938 | // traits are exported currently (see `EmbargoVisitor.exported_trait`) | |
92a42be0 | 939 | self.access_levels.is_public(trait_id) |
85aaf69f SL |
940 | } |
941 | ||
9cc50fc6 | 942 | fn check_ty_param_bound(&mut self, |
e9174d1e SL |
943 | ty_param_bound: &hir::TyParamBound) { |
944 | if let hir::TraitTyParamBound(ref trait_ref, _) = *ty_param_bound { | |
476ff2be | 945 | if self.path_is_private_type(&trait_ref.trait_ref.path) { |
9cc50fc6 | 946 | self.old_error_set.insert(trait_ref.trait_ref.ref_id); |
85aaf69f SL |
947 | } |
948 | } | |
949 | } | |
c34b1796 | 950 | |
54a0048b SL |
951 | fn item_is_public(&self, id: &ast::NodeId, vis: &hir::Visibility) -> bool { |
952 | self.access_levels.is_reachable(*id) || *vis == hir::Public | |
c34b1796 | 953 | } |
85aaf69f SL |
954 | } |
955 | ||
9cc50fc6 | 956 | impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { |
476ff2be SL |
957 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { |
958 | NestedVisitorMap::None | |
959 | } | |
960 | ||
e9174d1e | 961 | fn visit_ty(&mut self, ty: &hir::Ty) { |
476ff2be SL |
962 | if let hir::TyPath(hir::QPath::Resolved(_, ref path)) = ty.node { |
963 | if self.inner.path_is_private_type(path) { | |
85aaf69f SL |
964 | self.contains_private = true; |
965 | // found what we're looking for so let's stop | |
966 | // working. | |
967 | return | |
476ff2be SL |
968 | } |
969 | } | |
970 | if let hir::TyPath(_) = ty.node { | |
971 | if self.at_outer_type { | |
85aaf69f SL |
972 | self.outer_type_is_public_path = true; |
973 | } | |
974 | } | |
975 | self.at_outer_type = false; | |
92a42be0 | 976 | intravisit::walk_ty(self, ty) |
85aaf69f SL |
977 | } |
978 | ||
979 | // don't want to recurse into [, .. expr] | |
e9174d1e | 980 | fn visit_expr(&mut self, _: &hir::Expr) {} |
85aaf69f SL |
981 | } |
982 | ||
476ff2be | 983 | impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { |
92a42be0 SL |
984 | /// We want to visit items in the context of their containing |
985 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 986 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 987 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 SL |
988 | } |
989 | ||
476ff2be | 990 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
85aaf69f SL |
991 | match item.node { |
992 | // contents of a private mod can be reexported, so we need | |
993 | // to check internals. | |
e9174d1e | 994 | hir::ItemMod(_) => {} |
85aaf69f SL |
995 | |
996 | // An `extern {}` doesn't introduce a new privacy | |
997 | // namespace (the contents have their own privacies). | |
e9174d1e | 998 | hir::ItemForeignMod(_) => {} |
85aaf69f | 999 | |
9e0c209e | 1000 | hir::ItemTrait(.., ref bounds, _) => { |
85aaf69f SL |
1001 | if !self.trait_is_public(item.id) { |
1002 | return | |
1003 | } | |
1004 | ||
62682a34 | 1005 | for bound in bounds.iter() { |
85aaf69f SL |
1006 | self.check_ty_param_bound(bound) |
1007 | } | |
1008 | } | |
1009 | ||
1010 | // impls need some special handling to try to offer useful | |
1011 | // error messages without (too many) false positives | |
1012 | // (i.e. we could just return here to not check them at | |
1013 | // all, or some worse estimation of whether an impl is | |
c34b1796 | 1014 | // publicly visible). |
476ff2be | 1015 | hir::ItemImpl(.., ref g, ref trait_ref, ref self_, ref impl_item_refs) => { |
85aaf69f SL |
1016 | // `impl [... for] Private` is never visible. |
1017 | let self_contains_private; | |
1018 | // impl [... for] Public<...>, but not `impl [... for] | |
d9579d0f | 1019 | // Vec<Public>` or `(Public,)` etc. |
85aaf69f SL |
1020 | let self_is_public_path; |
1021 | ||
1022 | // check the properties of the Self type: | |
1023 | { | |
9cc50fc6 | 1024 | let mut visitor = ObsoleteCheckTypeForPrivatenessVisitor { |
85aaf69f SL |
1025 | inner: self, |
1026 | contains_private: false, | |
1027 | at_outer_type: true, | |
1028 | outer_type_is_public_path: false, | |
1029 | }; | |
7453a54e | 1030 | visitor.visit_ty(&self_); |
85aaf69f SL |
1031 | self_contains_private = visitor.contains_private; |
1032 | self_is_public_path = visitor.outer_type_is_public_path; | |
1033 | } | |
1034 | ||
1035 | // miscellaneous info about the impl | |
1036 | ||
1037 | // `true` iff this is `impl Private for ...`. | |
1038 | let not_private_trait = | |
1039 | trait_ref.as_ref().map_or(true, // no trait counts as public trait | |
1040 | |tr| { | |
476ff2be | 1041 | let did = tr.path.def.def_id(); |
85aaf69f | 1042 | |
32a655c1 | 1043 | if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { |
b039eaaf SL |
1044 | self.trait_is_public(node_id) |
1045 | } else { | |
1046 | true // external traits must be public | |
1047 | } | |
85aaf69f SL |
1048 | }); |
1049 | ||
1050 | // `true` iff this is a trait impl or at least one method is public. | |
1051 | // | |
1052 | // `impl Public { $( fn ...() {} )* }` is not visible. | |
1053 | // | |
1054 | // This is required over just using the methods' privacy | |
1055 | // directly because we might have `impl<T: Foo<Private>> ...`, | |
1056 | // and we shouldn't warn about the generics if all the methods | |
1057 | // are private (because `T` won't be visible externally). | |
1058 | let trait_or_some_public_method = | |
1059 | trait_ref.is_some() || | |
476ff2be SL |
1060 | impl_item_refs.iter() |
1061 | .any(|impl_item_ref| { | |
32a655c1 | 1062 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
476ff2be SL |
1063 | match impl_item.node { |
1064 | hir::ImplItemKind::Const(..) | | |
1065 | hir::ImplItemKind::Method(..) => { | |
1066 | self.access_levels.is_reachable(impl_item.id) | |
1067 | } | |
1068 | hir::ImplItemKind::Type(_) => false, | |
1069 | } | |
1070 | }); | |
85aaf69f SL |
1071 | |
1072 | if !self_contains_private && | |
1073 | not_private_trait && | |
1074 | trait_or_some_public_method { | |
1075 | ||
92a42be0 | 1076 | intravisit::walk_generics(self, g); |
85aaf69f SL |
1077 | |
1078 | match *trait_ref { | |
1079 | None => { | |
476ff2be | 1080 | for impl_item_ref in impl_item_refs { |
c34b1796 AL |
1081 | // This is where we choose whether to walk down |
1082 | // further into the impl to check its items. We | |
1083 | // should only walk into public items so that we | |
1084 | // don't erroneously report errors for private | |
1085 | // types in private items. | |
32a655c1 | 1086 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
c34b1796 | 1087 | match impl_item.node { |
92a42be0 SL |
1088 | hir::ImplItemKind::Const(..) | |
1089 | hir::ImplItemKind::Method(..) | |
54a0048b | 1090 | if self.item_is_public(&impl_item.id, &impl_item.vis) => |
c34b1796 | 1091 | { |
92a42be0 | 1092 | intravisit::walk_impl_item(self, impl_item) |
c34b1796 | 1093 | } |
92a42be0 SL |
1094 | hir::ImplItemKind::Type(..) => { |
1095 | intravisit::walk_impl_item(self, impl_item) | |
85aaf69f | 1096 | } |
c34b1796 | 1097 | _ => {} |
85aaf69f SL |
1098 | } |
1099 | } | |
1100 | } | |
1101 | Some(ref tr) => { | |
c34b1796 | 1102 | // Any private types in a trait impl fall into three |
85aaf69f SL |
1103 | // categories. |
1104 | // 1. mentioned in the trait definition | |
1105 | // 2. mentioned in the type params/generics | |
c34b1796 | 1106 | // 3. mentioned in the associated types of the impl |
85aaf69f SL |
1107 | // |
1108 | // Those in 1. can only occur if the trait is in | |
1109 | // this crate and will've been warned about on the | |
1110 | // trait definition (there's no need to warn twice | |
1111 | // so we don't check the methods). | |
1112 | // | |
1113 | // Those in 2. are warned via walk_generics and this | |
1114 | // call here. | |
92a42be0 | 1115 | intravisit::walk_path(self, &tr.path); |
c34b1796 AL |
1116 | |
1117 | // Those in 3. are warned with this call. | |
476ff2be | 1118 | for impl_item_ref in impl_item_refs { |
32a655c1 | 1119 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
92a42be0 | 1120 | if let hir::ImplItemKind::Type(ref ty) = impl_item.node { |
d9579d0f | 1121 | self.visit_ty(ty); |
c34b1796 AL |
1122 | } |
1123 | } | |
85aaf69f SL |
1124 | } |
1125 | } | |
1126 | } else if trait_ref.is_none() && self_is_public_path { | |
1127 | // impl Public<Private> { ... }. Any public static | |
1128 | // methods will be visible as `Public::foo`. | |
1129 | let mut found_pub_static = false; | |
476ff2be | 1130 | for impl_item_ref in impl_item_refs { |
32a655c1 SL |
1131 | if self.item_is_public(&impl_item_ref.id.node_id, &impl_item_ref.vis) { |
1132 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); | |
1133 | match impl_item_ref.kind { | |
1134 | hir::AssociatedItemKind::Const => { | |
d9579d0f | 1135 | found_pub_static = true; |
92a42be0 | 1136 | intravisit::walk_impl_item(self, impl_item); |
d9579d0f | 1137 | } |
32a655c1 | 1138 | hir::AssociatedItemKind::Method { has_self: false } => { |
85aaf69f | 1139 | found_pub_static = true; |
92a42be0 | 1140 | intravisit::walk_impl_item(self, impl_item); |
85aaf69f | 1141 | } |
32a655c1 | 1142 | _ => {} |
85aaf69f | 1143 | } |
85aaf69f SL |
1144 | } |
1145 | } | |
1146 | if found_pub_static { | |
92a42be0 | 1147 | intravisit::walk_generics(self, g) |
85aaf69f SL |
1148 | } |
1149 | } | |
1150 | return | |
1151 | } | |
1152 | ||
1153 | // `type ... = ...;` can contain private types, because | |
1154 | // we're introducing a new name. | |
e9174d1e | 1155 | hir::ItemTy(..) => return, |
85aaf69f SL |
1156 | |
1157 | // not at all public, so we don't care | |
54a0048b | 1158 | _ if !self.item_is_public(&item.id, &item.vis) => { |
c34b1796 AL |
1159 | return; |
1160 | } | |
85aaf69f SL |
1161 | |
1162 | _ => {} | |
1163 | } | |
1164 | ||
c34b1796 | 1165 | // We've carefully constructed it so that if we're here, then |
85aaf69f SL |
1166 | // any `visit_ty`'s will be called on things that are in |
1167 | // public signatures, i.e. things that we're interested in for | |
1168 | // this visitor. | |
92a42be0 | 1169 | intravisit::walk_item(self, item); |
85aaf69f SL |
1170 | } |
1171 | ||
476ff2be | 1172 | fn visit_generics(&mut self, generics: &'tcx hir::Generics) { |
62682a34 SL |
1173 | for ty_param in generics.ty_params.iter() { |
1174 | for bound in ty_param.bounds.iter() { | |
85aaf69f SL |
1175 | self.check_ty_param_bound(bound) |
1176 | } | |
1177 | } | |
1178 | for predicate in &generics.where_clause.predicates { | |
1179 | match predicate { | |
e9174d1e | 1180 | &hir::WherePredicate::BoundPredicate(ref bound_pred) => { |
62682a34 | 1181 | for bound in bound_pred.bounds.iter() { |
85aaf69f SL |
1182 | self.check_ty_param_bound(bound) |
1183 | } | |
1184 | } | |
e9174d1e SL |
1185 | &hir::WherePredicate::RegionPredicate(_) => {} |
1186 | &hir::WherePredicate::EqPredicate(ref eq_pred) => { | |
32a655c1 | 1187 | self.visit_ty(&eq_pred.rhs_ty); |
85aaf69f SL |
1188 | } |
1189 | } | |
1190 | } | |
1191 | } | |
1192 | ||
476ff2be | 1193 | fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { |
92a42be0 SL |
1194 | if self.access_levels.is_reachable(item.id) { |
1195 | intravisit::walk_foreign_item(self, item) | |
85aaf69f SL |
1196 | } |
1197 | } | |
1198 | ||
476ff2be SL |
1199 | fn visit_ty(&mut self, t: &'tcx hir::Ty) { |
1200 | if let hir::TyPath(hir::QPath::Resolved(_, ref path)) = t.node { | |
1201 | if self.path_is_private_type(path) { | |
9cc50fc6 | 1202 | self.old_error_set.insert(t.id); |
85aaf69f SL |
1203 | } |
1204 | } | |
92a42be0 | 1205 | intravisit::walk_ty(self, t) |
85aaf69f SL |
1206 | } |
1207 | ||
476ff2be SL |
1208 | fn visit_variant(&mut self, |
1209 | v: &'tcx hir::Variant, | |
1210 | g: &'tcx hir::Generics, | |
1211 | item_id: ast::NodeId) { | |
92a42be0 | 1212 | if self.access_levels.is_reachable(v.node.data.id()) { |
85aaf69f | 1213 | self.in_variant = true; |
92a42be0 | 1214 | intravisit::walk_variant(self, v, g, item_id); |
85aaf69f SL |
1215 | self.in_variant = false; |
1216 | } | |
1217 | } | |
1218 | ||
476ff2be | 1219 | fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { |
54a0048b | 1220 | if s.vis == hir::Public || self.in_variant { |
92a42be0 | 1221 | intravisit::walk_struct_field(self, s); |
85aaf69f SL |
1222 | } |
1223 | } | |
1224 | ||
85aaf69f SL |
1225 | // we don't need to introspect into these at all: an |
1226 | // expression/block context can't possibly contain exported things. | |
1227 | // (Making them no-ops stops us from traversing the whole AST without | |
1228 | // having to be super careful about our `walk_...` calls above.) | |
476ff2be SL |
1229 | fn visit_block(&mut self, _: &'tcx hir::Block) {} |
1230 | fn visit_expr(&mut self, _: &'tcx hir::Expr) {} | |
85aaf69f SL |
1231 | } |
1232 | ||
9cc50fc6 SL |
1233 | /////////////////////////////////////////////////////////////////////////////// |
1234 | /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and | |
1235 | /// finds any private components in it. | |
1236 | /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types | |
1237 | /// and traits in public interfaces. | |
1238 | /////////////////////////////////////////////////////////////////////////////// | |
1239 | ||
1240 | struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 1241 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
476ff2be SL |
1242 | item_def_id: DefId, |
1243 | span: Span, | |
54a0048b SL |
1244 | /// The visitor checks that each component type is at least this visible |
1245 | required_visibility: ty::Visibility, | |
1246 | /// The visibility of the least visible component that has been visited | |
1247 | min_visibility: ty::Visibility, | |
cc61c64b | 1248 | has_pub_restricted: bool, |
476ff2be | 1249 | has_old_errors: bool, |
9cc50fc6 SL |
1250 | } |
1251 | ||
1252 | impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
476ff2be | 1253 | fn generics(&mut self) -> &mut Self { |
7cac9316 | 1254 | for def in &self.tcx.generics_of(self.item_def_id).types { |
8bb4bdeb | 1255 | if def.has_default { |
7cac9316 | 1256 | self.tcx.type_of(def.def_id).visit_with(self); |
8bb4bdeb XL |
1257 | } |
1258 | } | |
476ff2be | 1259 | self |
54a0048b | 1260 | } |
54a0048b | 1261 | |
476ff2be | 1262 | fn predicates(&mut self) -> &mut Self { |
041b39d2 XL |
1263 | let predicates = self.tcx.predicates_of(self.item_def_id); |
1264 | for predicate in &predicates.predicates { | |
1265 | predicate.visit_with(self); | |
1266 | match predicate { | |
1267 | &ty::Predicate::Trait(poly_predicate) => { | |
1268 | self.check_trait_ref(poly_predicate.skip_binder().trait_ref); | |
1269 | }, | |
1270 | &ty::Predicate::Projection(poly_predicate) => { | |
1271 | let tcx = self.tcx; | |
1272 | self.check_trait_ref( | |
1273 | poly_predicate.skip_binder().projection_ty.trait_ref(tcx) | |
1274 | ); | |
1275 | }, | |
1276 | _ => (), | |
1277 | }; | |
1278 | } | |
476ff2be SL |
1279 | self |
1280 | } | |
1281 | ||
7cac9316 | 1282 | fn ty(&mut self) -> &mut Self { |
041b39d2 XL |
1283 | let ty = self.tcx.type_of(self.item_def_id); |
1284 | ty.visit_with(self); | |
1285 | if let ty::TyFnDef(def_id, _) = ty.sty { | |
1286 | if def_id == self.item_def_id { | |
1287 | self.tcx.fn_sig(def_id).visit_with(self); | |
1288 | } | |
1289 | } | |
476ff2be SL |
1290 | self |
1291 | } | |
1292 | ||
1293 | fn impl_trait_ref(&mut self) -> &mut Self { | |
041b39d2 XL |
1294 | if let Some(impl_trait_ref) = self.tcx.impl_trait_ref(self.item_def_id) { |
1295 | self.check_trait_ref(impl_trait_ref); | |
1296 | impl_trait_ref.super_visit_with(self); | |
1297 | } | |
476ff2be | 1298 | self |
9cc50fc6 | 1299 | } |
041b39d2 XL |
1300 | |
1301 | fn check_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) { | |
1302 | // Non-local means public (private items can't leave their crate, modulo bugs) | |
1303 | if let Some(node_id) = self.tcx.hir.as_local_node_id(trait_ref.def_id) { | |
1304 | let item = self.tcx.hir.expect_item(node_id); | |
1305 | let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); | |
1306 | if !vis.is_at_least(self.min_visibility, self.tcx) { | |
1307 | self.min_visibility = vis; | |
1308 | } | |
1309 | if !vis.is_at_least(self.required_visibility, self.tcx) { | |
1310 | if self.has_pub_restricted || self.has_old_errors { | |
1311 | struct_span_err!(self.tcx.sess, self.span, E0445, | |
1312 | "private trait `{}` in public interface", trait_ref) | |
1313 | .span_label(self.span, format!( | |
1314 | "private trait can't be public")) | |
1315 | .emit(); | |
1316 | } else { | |
3b2f2976 XL |
1317 | self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, |
1318 | node_id, | |
1319 | self.span, | |
1320 | &format!("private trait `{}` in public \ | |
1321 | interface (error E0445)", trait_ref)); | |
041b39d2 XL |
1322 | } |
1323 | } | |
1324 | } | |
1325 | } | |
9cc50fc6 SL |
1326 | } |
1327 | ||
476ff2be SL |
1328 | impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { |
1329 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
1330 | let ty_def_id = match ty.sty { | |
1331 | ty::TyAdt(adt, _) => Some(adt.did), | |
1332 | ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), | |
1333 | ty::TyProjection(ref proj) => { | |
32a655c1 | 1334 | if self.required_visibility == ty::Visibility::Invisible { |
9cc50fc6 SL |
1335 | // Conservatively approximate the whole type alias as public without |
1336 | // recursing into its components when determining impl publicity. | |
1337 | // For example, `impl <Type as Trait>::Alias {...}` may be a public impl | |
1338 | // even if both `Type` and `Trait` are private. | |
1339 | // Ideally, associated types should be substituted in the same way as | |
1340 | // free type aliases, but this isn't done yet. | |
476ff2be | 1341 | return false; |
9cc50fc6 | 1342 | } |
041b39d2 XL |
1343 | let trait_ref = proj.trait_ref(self.tcx); |
1344 | Some(trait_ref.def_id) | |
476ff2be SL |
1345 | } |
1346 | _ => None | |
1347 | }; | |
54a0048b | 1348 | |
476ff2be SL |
1349 | if let Some(def_id) = ty_def_id { |
1350 | // Non-local means public (private items can't leave their crate, modulo bugs) | |
32a655c1 SL |
1351 | if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { |
1352 | let item = self.tcx.hir.expect_item(node_id); | |
476ff2be SL |
1353 | let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); |
1354 | ||
32a655c1 | 1355 | if !vis.is_at_least(self.min_visibility, self.tcx) { |
476ff2be SL |
1356 | self.min_visibility = vis; |
1357 | } | |
32a655c1 | 1358 | if !vis.is_at_least(self.required_visibility, self.tcx) { |
cc61c64b | 1359 | if self.has_pub_restricted || self.has_old_errors { |
476ff2be SL |
1360 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0446, |
1361 | "private type `{}` in public interface", ty); | |
7cac9316 | 1362 | err.span_label(self.span, "can't leak private type"); |
476ff2be SL |
1363 | err.emit(); |
1364 | } else { | |
3b2f2976 XL |
1365 | self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, |
1366 | node_id, | |
1367 | self.span, | |
1368 | &format!("private type `{}` in public \ | |
1369 | interface (error E0446)", ty)); | |
9cc50fc6 SL |
1370 | } |
1371 | } | |
9cc50fc6 SL |
1372 | } |
1373 | } | |
1374 | ||
041b39d2 | 1375 | ty.super_visit_with(self) |
9cc50fc6 | 1376 | } |
9cc50fc6 SL |
1377 | } |
1378 | ||
1379 | struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 1380 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
cc61c64b | 1381 | has_pub_restricted: bool, |
9cc50fc6 | 1382 | old_error_set: &'a NodeSet, |
476ff2be | 1383 | inner_visibility: ty::Visibility, |
9cc50fc6 SL |
1384 | } |
1385 | ||
1386 | impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { | |
476ff2be SL |
1387 | fn check(&self, item_id: ast::NodeId, required_visibility: ty::Visibility) |
1388 | -> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
1389 | let mut has_old_errors = false; | |
1390 | ||
1391 | // Slow path taken only if there any errors in the crate. | |
1392 | for &id in self.old_error_set { | |
1393 | // Walk up the nodes until we find `item_id` (or we hit a root). | |
1394 | let mut id = id; | |
1395 | loop { | |
1396 | if id == item_id { | |
1397 | has_old_errors = true; | |
1398 | break; | |
1399 | } | |
32a655c1 | 1400 | let parent = self.tcx.hir.get_parent_node(id); |
476ff2be SL |
1401 | if parent == id { |
1402 | break; | |
1403 | } | |
1404 | id = parent; | |
1405 | } | |
1406 | ||
1407 | if has_old_errors { | |
1408 | break; | |
1409 | } | |
1410 | } | |
9cc50fc6 | 1411 | |
476ff2be SL |
1412 | SearchInterfaceForPrivateItemsVisitor { |
1413 | tcx: self.tcx, | |
32a655c1 SL |
1414 | item_def_id: self.tcx.hir.local_def_id(item_id), |
1415 | span: self.tcx.hir.span(item_id), | |
476ff2be | 1416 | min_visibility: ty::Visibility::Public, |
3b2f2976 | 1417 | required_visibility, |
cc61c64b | 1418 | has_pub_restricted: self.has_pub_restricted, |
3b2f2976 | 1419 | has_old_errors, |
476ff2be | 1420 | } |
9cc50fc6 SL |
1421 | } |
1422 | } | |
1423 | ||
476ff2be SL |
1424 | impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { |
1425 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
32a655c1 | 1426 | NestedVisitorMap::OnlyBodies(&self.tcx.hir) |
476ff2be SL |
1427 | } |
1428 | ||
1429 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
1430 | let tcx = self.tcx; | |
54a0048b | 1431 | let min = |vis1: ty::Visibility, vis2| { |
32a655c1 | 1432 | if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } |
9cc50fc6 | 1433 | }; |
54a0048b | 1434 | |
476ff2be | 1435 | let item_visibility = ty::Visibility::from_hir(&item.vis, item.id, tcx); |
54a0048b | 1436 | |
9cc50fc6 SL |
1437 | match item.node { |
1438 | // Crates are always public | |
1439 | hir::ItemExternCrate(..) => {} | |
1440 | // All nested items are checked by visit_item | |
1441 | hir::ItemMod(..) => {} | |
1442 | // Checked in resolve | |
1443 | hir::ItemUse(..) => {} | |
cc61c64b XL |
1444 | // No subitems |
1445 | hir::ItemGlobalAsm(..) => {} | |
9cc50fc6 SL |
1446 | // Subitems of these items have inherited publicity |
1447 | hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) | | |
476ff2be | 1448 | hir::ItemTy(..) => { |
7cac9316 | 1449 | self.check(item.id, item_visibility).generics().predicates().ty(); |
476ff2be SL |
1450 | |
1451 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1452 | self.inner_visibility = item_visibility; | |
1453 | intravisit::walk_item(self, item); | |
1454 | } | |
32a655c1 | 1455 | hir::ItemTrait(.., ref trait_item_refs) => { |
476ff2be SL |
1456 | self.check(item.id, item_visibility).generics().predicates(); |
1457 | ||
32a655c1 SL |
1458 | for trait_item_ref in trait_item_refs { |
1459 | let mut check = self.check(trait_item_ref.id.node_id, item_visibility); | |
476ff2be SL |
1460 | check.generics().predicates(); |
1461 | ||
32a655c1 SL |
1462 | if trait_item_ref.kind == hir::AssociatedItemKind::Type && |
1463 | !trait_item_ref.defaultness.has_value() { | |
476ff2be SL |
1464 | // No type to visit. |
1465 | } else { | |
7cac9316 | 1466 | check.ty(); |
476ff2be SL |
1467 | } |
1468 | } | |
1469 | } | |
1470 | hir::ItemEnum(ref def, _) => { | |
1471 | self.check(item.id, item_visibility).generics().predicates(); | |
1472 | ||
1473 | for variant in &def.variants { | |
1474 | for field in variant.node.data.fields() { | |
7cac9316 | 1475 | self.check(field.id, item_visibility).ty(); |
476ff2be SL |
1476 | } |
1477 | } | |
9cc50fc6 SL |
1478 | } |
1479 | // Subitems of foreign modules have their own publicity | |
1480 | hir::ItemForeignMod(ref foreign_mod) => { | |
1481 | for foreign_item in &foreign_mod.items { | |
476ff2be | 1482 | let vis = ty::Visibility::from_hir(&foreign_item.vis, item.id, tcx); |
7cac9316 | 1483 | self.check(foreign_item.id, vis).generics().predicates().ty(); |
9cc50fc6 SL |
1484 | } |
1485 | } | |
9e0c209e | 1486 | // Subitems of structs and unions have their own publicity |
476ff2be SL |
1487 | hir::ItemStruct(ref struct_def, _) | |
1488 | hir::ItemUnion(ref struct_def, _) => { | |
1489 | self.check(item.id, item_visibility).generics().predicates(); | |
54a0048b SL |
1490 | |
1491 | for field in struct_def.fields() { | |
476ff2be | 1492 | let field_visibility = ty::Visibility::from_hir(&field.vis, item.id, tcx); |
7cac9316 | 1493 | self.check(field.id, min(item_visibility, field_visibility)).ty(); |
9cc50fc6 SL |
1494 | } |
1495 | } | |
1496 | // The interface is empty | |
1497 | hir::ItemDefaultImpl(..) => {} | |
1498 | // An inherent impl is public when its type is public | |
1499 | // Subitems of inherent impls have their own publicity | |
476ff2be | 1500 | hir::ItemImpl(.., None, _, ref impl_item_refs) => { |
32a655c1 | 1501 | let ty_vis = |
7cac9316 | 1502 | self.check(item.id, ty::Visibility::Invisible).ty().min_visibility; |
476ff2be | 1503 | self.check(item.id, ty_vis).generics().predicates(); |
54a0048b | 1504 | |
476ff2be | 1505 | for impl_item_ref in impl_item_refs { |
32a655c1 | 1506 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
54a0048b | 1507 | let impl_item_vis = |
476ff2be SL |
1508 | ty::Visibility::from_hir(&impl_item.vis, item.id, tcx); |
1509 | self.check(impl_item.id, min(impl_item_vis, ty_vis)) | |
7cac9316 | 1510 | .generics().predicates().ty(); |
476ff2be SL |
1511 | |
1512 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1513 | self.inner_visibility = impl_item_vis; | |
1514 | intravisit::walk_impl_item(self, impl_item); | |
9cc50fc6 SL |
1515 | } |
1516 | } | |
1517 | // A trait impl is public when both its type and its trait are public | |
1518 | // Subitems of trait impls have inherited publicity | |
476ff2be | 1519 | hir::ItemImpl(.., Some(_), _, ref impl_item_refs) => { |
32a655c1 | 1520 | let vis = self.check(item.id, ty::Visibility::Invisible) |
7cac9316 | 1521 | .ty().impl_trait_ref().min_visibility; |
476ff2be SL |
1522 | self.check(item.id, vis).generics().predicates(); |
1523 | for impl_item_ref in impl_item_refs { | |
32a655c1 | 1524 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
7cac9316 | 1525 | self.check(impl_item.id, vis).generics().predicates().ty(); |
476ff2be SL |
1526 | |
1527 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1528 | self.inner_visibility = vis; | |
1529 | intravisit::walk_impl_item(self, impl_item); | |
9cc50fc6 SL |
1530 | } |
1531 | } | |
1532 | } | |
1533 | } | |
476ff2be SL |
1534 | |
1535 | fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem) { | |
1536 | // handled in `visit_item` above | |
1537 | } | |
1538 | ||
1539 | fn visit_ty(&mut self, ty: &'tcx hir::Ty) { | |
1540 | if let hir::TyImplTrait(..) = ty.node { | |
1541 | // Check the traits being exposed, as they're separate, | |
1542 | // e.g. `impl Iterator<Item=T>` has two predicates, | |
1543 | // `X: Iterator` and `<X as Iterator>::Item == T`, | |
1544 | // where `X` is the `impl Iterator<Item=T>` itself, | |
7cac9316 | 1545 | // stored in `predicates_of`, not in the `Ty` itself. |
476ff2be SL |
1546 | self.check(ty.id, self.inner_visibility).predicates(); |
1547 | } | |
1548 | ||
1549 | intravisit::walk_ty(self, ty); | |
1550 | } | |
1551 | ||
1552 | // Don't recurse into expressions in array sizes or const initializers | |
1553 | fn visit_expr(&mut self, _: &'tcx hir::Expr) {} | |
1554 | // Don't recurse into patterns in function arguments | |
1555 | fn visit_pat(&mut self, _: &'tcx hir::Pat) {} | |
9cc50fc6 SL |
1556 | } |
1557 | ||
cc61c64b XL |
1558 | pub fn provide(providers: &mut Providers) { |
1559 | *providers = Providers { | |
1560 | privacy_access_levels, | |
1561 | ..*providers | |
1562 | }; | |
1563 | } | |
1564 | ||
1565 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Rc<AccessLevels> { | |
1566 | tcx.dep_graph.with_ignore(|| { // FIXME | |
7cac9316 | 1567 | tcx.privacy_access_levels(LOCAL_CRATE) |
cc61c64b XL |
1568 | }) |
1569 | } | |
1570 | ||
1571 | fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1572 | krate: CrateNum) | |
1573 | -> Rc<AccessLevels> { | |
1574 | assert_eq!(krate, LOCAL_CRATE); | |
9cc50fc6 | 1575 | |
32a655c1 | 1576 | let krate = tcx.hir.krate(); |
3b2f2976 | 1577 | let empty_tables = ty::TypeckTables::empty(None); |
85aaf69f | 1578 | |
7cac9316 XL |
1579 | // Check privacy of names not checked in previous compilation stages. |
1580 | let mut visitor = NamePrivacyVisitor { | |
3b2f2976 XL |
1581 | tcx, |
1582 | tables: &empty_tables, | |
7cac9316 | 1583 | current_item: CRATE_NODE_ID, |
3b2f2976 | 1584 | empty_tables: &empty_tables, |
85aaf69f | 1585 | }; |
92a42be0 | 1586 | intravisit::walk_crate(&mut visitor, krate); |
85aaf69f | 1587 | |
041b39d2 XL |
1588 | // Check privacy of explicitly written types and traits as well as |
1589 | // inferred types of expressions and patterns. | |
1590 | let mut visitor = TypePrivacyVisitor { | |
3b2f2976 XL |
1591 | tcx, |
1592 | tables: &empty_tables, | |
041b39d2 XL |
1593 | current_item: DefId::local(CRATE_DEF_INDEX), |
1594 | span: krate.span, | |
3b2f2976 | 1595 | empty_tables: &empty_tables, |
041b39d2 XL |
1596 | }; |
1597 | intravisit::walk_crate(&mut visitor, krate); | |
1598 | ||
85aaf69f SL |
1599 | // Build up a set of all exported items in the AST. This is a set of all |
1600 | // items which are reachable from external crates based on visibility. | |
1601 | let mut visitor = EmbargoVisitor { | |
3b2f2976 | 1602 | tcx, |
92a42be0 SL |
1603 | access_levels: Default::default(), |
1604 | prev_level: Some(AccessLevel::Public), | |
1605 | changed: false, | |
85aaf69f SL |
1606 | }; |
1607 | loop { | |
92a42be0 SL |
1608 | intravisit::walk_crate(&mut visitor, krate); |
1609 | if visitor.changed { | |
1610 | visitor.changed = false; | |
1611 | } else { | |
85aaf69f SL |
1612 | break |
1613 | } | |
1614 | } | |
92a42be0 | 1615 | visitor.update(ast::CRATE_NODE_ID, Some(AccessLevel::Public)); |
85aaf69f | 1616 | |
85aaf69f | 1617 | { |
9cc50fc6 | 1618 | let mut visitor = ObsoleteVisiblePrivateTypesVisitor { |
3b2f2976 | 1619 | tcx, |
9cc50fc6 | 1620 | access_levels: &visitor.access_levels, |
85aaf69f | 1621 | in_variant: false, |
9cc50fc6 | 1622 | old_error_set: NodeSet(), |
85aaf69f | 1623 | }; |
92a42be0 | 1624 | intravisit::walk_crate(&mut visitor, krate); |
9cc50fc6 | 1625 | |
cc61c64b XL |
1626 | |
1627 | let has_pub_restricted = { | |
1628 | let mut pub_restricted_visitor = PubRestrictedVisitor { | |
3b2f2976 | 1629 | tcx, |
cc61c64b XL |
1630 | has_pub_restricted: false |
1631 | }; | |
1632 | intravisit::walk_crate(&mut pub_restricted_visitor, krate); | |
1633 | pub_restricted_visitor.has_pub_restricted | |
1634 | }; | |
1635 | ||
9cc50fc6 SL |
1636 | // Check for private types and traits in public interfaces |
1637 | let mut visitor = PrivateItemsInPublicInterfacesVisitor { | |
3b2f2976 XL |
1638 | tcx, |
1639 | has_pub_restricted, | |
9cc50fc6 | 1640 | old_error_set: &visitor.old_error_set, |
476ff2be | 1641 | inner_visibility: ty::Visibility::Public, |
9cc50fc6 | 1642 | }; |
476ff2be | 1643 | krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor)); |
85aaf69f | 1644 | } |
92a42be0 | 1645 | |
cc61c64b | 1646 | Rc::new(visitor.access_levels) |
85aaf69f | 1647 | } |
92a42be0 SL |
1648 | |
1649 | __build_diagnostic_array! { librustc_privacy, DIAGNOSTICS } |