]>
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 | ||
11 | #![crate_name = "rustc_privacy"] | |
e9174d1e | 12 | #![unstable(feature = "rustc_private", issue = "27812")] |
85aaf69f SL |
13 | #![crate_type = "dylib"] |
14 | #![crate_type = "rlib"] | |
e9174d1e | 15 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", |
62682a34 | 16 | html_favicon_url = "https://doc.rust-lang.org/favicon.ico", |
e9174d1e | 17 | html_root_url = "https://doc.rust-lang.org/nightly/")] |
32a655c1 | 18 | #![deny(warnings)] |
85aaf69f | 19 | |
85aaf69f SL |
20 | #![feature(rustc_diagnostic_macros)] |
21 | #![feature(rustc_private)] | |
22 | #![feature(staged_api)] | |
23 | ||
3157f602 | 24 | extern crate rustc; |
85aaf69f | 25 | #[macro_use] extern crate syntax; |
3157f602 | 26 | extern crate syntax_pos; |
e9174d1e | 27 | |
9cc50fc6 | 28 | use rustc::dep_graph::DepNode; |
3157f602 | 29 | use rustc::hir::{self, PatKind}; |
8bb4bdeb | 30 | use rustc::hir::def::{self, Def}; |
32a655c1 | 31 | use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; |
476ff2be SL |
32 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; |
33 | use rustc::hir::itemlikevisit::DeepVisitor; | |
3157f602 XL |
34 | use rustc::hir::pat_util::EnumerateAndAdjustIterator; |
35 | use rustc::lint; | |
92a42be0 | 36 | use rustc::middle::privacy::{AccessLevel, AccessLevels}; |
476ff2be SL |
37 | use rustc::ty::{self, TyCtxt, Ty, TypeFoldable}; |
38 | use rustc::ty::fold::TypeVisitor; | |
54a0048b | 39 | use rustc::util::nodemap::NodeSet; |
62682a34 | 40 | use syntax::ast; |
3157f602 | 41 | use syntax_pos::Span; |
85aaf69f | 42 | |
3157f602 XL |
43 | use std::cmp; |
44 | use std::mem::replace; | |
85aaf69f | 45 | |
3157f602 | 46 | pub mod diagnostics; |
85aaf69f | 47 | |
85aaf69f SL |
48 | //////////////////////////////////////////////////////////////////////////////// |
49 | /// The embargo visitor, used to determine the exports of the ast | |
50 | //////////////////////////////////////////////////////////////////////////////// | |
51 | ||
52 | struct EmbargoVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 53 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
85aaf69f SL |
54 | export_map: &'a def::ExportMap, |
55 | ||
92a42be0 SL |
56 | // Accessibility levels for reachable nodes |
57 | access_levels: AccessLevels, | |
58 | // Previous accessibility level, None means unreachable | |
59 | prev_level: Option<AccessLevel>, | |
60 | // Have something changed in the level map? | |
61 | changed: bool, | |
85aaf69f SL |
62 | } |
63 | ||
7453a54e | 64 | struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> { |
476ff2be | 65 | item_def_id: DefId, |
7453a54e SL |
66 | ev: &'b mut EmbargoVisitor<'a, 'tcx>, |
67 | } | |
68 | ||
85aaf69f | 69 | impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> { |
476ff2be SL |
70 | fn item_ty_level(&self, item_def_id: DefId) -> Option<AccessLevel> { |
71 | let ty_def_id = match self.tcx.item_type(item_def_id).sty { | |
72 | ty::TyAdt(adt, _) => adt.did, | |
73 | ty::TyDynamic(ref obj, ..) if obj.principal().is_some() => | |
74 | obj.principal().unwrap().def_id(), | |
75 | ty::TyProjection(ref proj) => proj.trait_ref.def_id, | |
76 | _ => return Some(AccessLevel::Public) | |
77 | }; | |
32a655c1 | 78 | if let Some(node_id) = self.tcx.hir.as_local_node_id(ty_def_id) { |
476ff2be | 79 | self.get(node_id) |
92a42be0 SL |
80 | } else { |
81 | Some(AccessLevel::Public) | |
82 | } | |
83 | } | |
84 | ||
476ff2be SL |
85 | fn impl_trait_level(&self, impl_def_id: DefId) -> Option<AccessLevel> { |
86 | if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) { | |
32a655c1 | 87 | if let Some(node_id) = self.tcx.hir.as_local_node_id(trait_ref.def_id) { |
476ff2be SL |
88 | return self.get(node_id); |
89 | } | |
92a42be0 | 90 | } |
476ff2be | 91 | Some(AccessLevel::Public) |
92a42be0 SL |
92 | } |
93 | ||
94 | fn get(&self, id: ast::NodeId) -> Option<AccessLevel> { | |
95 | self.access_levels.map.get(&id).cloned() | |
96 | } | |
97 | ||
98 | // Updates node level and returns the updated level | |
99 | fn update(&mut self, id: ast::NodeId, level: Option<AccessLevel>) -> Option<AccessLevel> { | |
100 | let old_level = self.get(id); | |
101 | // Accessibility levels can only grow | |
102 | if level > old_level { | |
103 | self.access_levels.map.insert(id, level.unwrap()); | |
104 | self.changed = true; | |
105 | level | |
106 | } else { | |
107 | old_level | |
108 | } | |
85aaf69f | 109 | } |
7453a54e | 110 | |
476ff2be SL |
111 | fn reach<'b>(&'b mut self, item_id: ast::NodeId) |
112 | -> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { | |
113 | ReachEverythingInTheInterfaceVisitor { | |
32a655c1 | 114 | item_def_id: self.tcx.hir.local_def_id(item_id), |
476ff2be SL |
115 | ev: self, |
116 | } | |
7453a54e | 117 | } |
85aaf69f SL |
118 | } |
119 | ||
476ff2be | 120 | impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> { |
92a42be0 SL |
121 | /// We want to visit items in the context of their containing |
122 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 123 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 124 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 | 125 | } |
85aaf69f | 126 | |
476ff2be | 127 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
92a42be0 SL |
128 | let inherited_item_level = match item.node { |
129 | // Impls inherit level from their types and traits | |
476ff2be | 130 | hir::ItemImpl(..) => { |
32a655c1 | 131 | let def_id = self.tcx.hir.local_def_id(item.id); |
476ff2be | 132 | cmp::min(self.item_ty_level(def_id), self.impl_trait_level(def_id)) |
92a42be0 | 133 | } |
476ff2be | 134 | hir::ItemDefaultImpl(..) => { |
32a655c1 | 135 | let def_id = self.tcx.hir.local_def_id(item.id); |
476ff2be | 136 | self.impl_trait_level(def_id) |
92a42be0 SL |
137 | } |
138 | // Foreign mods inherit level from parents | |
139 | hir::ItemForeignMod(..) => { | |
140 | self.prev_level | |
141 | } | |
142 | // Other `pub` items inherit levels from parents | |
85aaf69f | 143 | _ => { |
92a42be0 | 144 | if item.vis == hir::Public { self.prev_level } else { None } |
85aaf69f | 145 | } |
92a42be0 | 146 | }; |
85aaf69f | 147 | |
7453a54e | 148 | // Update level of the item itself |
92a42be0 | 149 | let item_level = self.update(item.id, inherited_item_level); |
85aaf69f | 150 | |
7453a54e | 151 | // Update levels of nested things |
85aaf69f | 152 | match item.node { |
92a42be0 | 153 | hir::ItemEnum(ref def, _) => { |
85aaf69f | 154 | for variant in &def.variants { |
92a42be0 SL |
155 | let variant_level = self.update(variant.node.data.id(), item_level); |
156 | for field in variant.node.data.fields() { | |
54a0048b | 157 | self.update(field.id, variant_level); |
92a42be0 | 158 | } |
85aaf69f SL |
159 | } |
160 | } | |
476ff2be SL |
161 | hir::ItemImpl(.., None, _, ref impl_item_refs) => { |
162 | for impl_item_ref in impl_item_refs { | |
32a655c1 SL |
163 | if impl_item_ref.vis == hir::Public { |
164 | self.update(impl_item_ref.id.node_id, item_level); | |
85aaf69f SL |
165 | } |
166 | } | |
167 | } | |
476ff2be SL |
168 | hir::ItemImpl(.., Some(_), _, ref impl_item_refs) => { |
169 | for impl_item_ref in impl_item_refs { | |
32a655c1 | 170 | self.update(impl_item_ref.id.node_id, item_level); |
92a42be0 SL |
171 | } |
172 | } | |
32a655c1 SL |
173 | hir::ItemTrait(.., ref trait_item_refs) => { |
174 | for trait_item_ref in trait_item_refs { | |
175 | self.update(trait_item_ref.id.node_id, item_level); | |
85aaf69f SL |
176 | } |
177 | } | |
9e0c209e | 178 | hir::ItemStruct(ref def, _) | hir::ItemUnion(ref def, _) => { |
b039eaaf | 179 | if !def.is_struct() { |
92a42be0 | 180 | self.update(def.id(), item_level); |
85aaf69f | 181 | } |
b039eaaf | 182 | for field in def.fields() { |
54a0048b SL |
183 | if field.vis == hir::Public { |
184 | self.update(field.id, item_level); | |
c34b1796 AL |
185 | } |
186 | } | |
85aaf69f | 187 | } |
92a42be0 SL |
188 | hir::ItemForeignMod(ref foreign_mod) => { |
189 | for foreign_item in &foreign_mod.items { | |
190 | if foreign_item.vis == hir::Public { | |
191 | self.update(foreign_item.id, item_level); | |
192 | } | |
193 | } | |
194 | } | |
7453a54e SL |
195 | _ => {} |
196 | } | |
197 | ||
198 | // Mark all items in interfaces of reachable items as reachable | |
199 | match item.node { | |
200 | // The interface is empty | |
201 | hir::ItemExternCrate(..) => {} | |
202 | // All nested items are checked by visit_item | |
203 | hir::ItemMod(..) => {} | |
204 | // Reexports are handled in visit_mod | |
205 | hir::ItemUse(..) => {} | |
476ff2be SL |
206 | // The interface is empty |
207 | hir::ItemDefaultImpl(..) => {} | |
7453a54e | 208 | // Visit everything |
476ff2be SL |
209 | hir::ItemConst(..) | hir::ItemStatic(..) | |
210 | hir::ItemFn(..) | hir::ItemTy(..) => { | |
7453a54e | 211 | if item_level.is_some() { |
476ff2be | 212 | self.reach(item.id).generics().predicates().item_type(); |
7453a54e SL |
213 | } |
214 | } | |
32a655c1 | 215 | hir::ItemTrait(.., ref trait_item_refs) => { |
476ff2be SL |
216 | if item_level.is_some() { |
217 | self.reach(item.id).generics().predicates(); | |
218 | ||
32a655c1 SL |
219 | for trait_item_ref in trait_item_refs { |
220 | let mut reach = self.reach(trait_item_ref.id.node_id); | |
476ff2be SL |
221 | reach.generics().predicates(); |
222 | ||
32a655c1 SL |
223 | if trait_item_ref.kind == hir::AssociatedItemKind::Type && |
224 | !trait_item_ref.defaultness.has_value() { | |
476ff2be SL |
225 | // No type to visit. |
226 | } else { | |
227 | reach.item_type(); | |
228 | } | |
229 | } | |
230 | } | |
231 | } | |
232 | // Visit everything except for private impl items | |
32a655c1 | 233 | hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => { |
476ff2be SL |
234 | if item_level.is_some() { |
235 | self.reach(item.id).generics().predicates().impl_trait_ref(); | |
236 | ||
32a655c1 SL |
237 | for impl_item_ref in impl_item_refs { |
238 | let id = impl_item_ref.id.node_id; | |
476ff2be SL |
239 | if trait_ref.is_some() || self.get(id).is_some() { |
240 | self.reach(id).generics().predicates().item_type(); | |
241 | } | |
242 | } | |
243 | } | |
244 | } | |
245 | ||
7453a54e | 246 | // Visit everything, but enum variants have their own levels |
476ff2be | 247 | hir::ItemEnum(ref def, _) => { |
7453a54e | 248 | if item_level.is_some() { |
476ff2be | 249 | self.reach(item.id).generics().predicates(); |
7453a54e SL |
250 | } |
251 | for variant in &def.variants { | |
252 | if self.get(variant.node.data.id()).is_some() { | |
253 | for field in variant.node.data.fields() { | |
476ff2be | 254 | self.reach(field.id).item_type(); |
7453a54e SL |
255 | } |
256 | // Corner case: if the variant is reachable, but its | |
257 | // enum is not, make the enum reachable as well. | |
258 | self.update(item.id, Some(AccessLevel::Reachable)); | |
259 | } | |
260 | } | |
261 | } | |
262 | // Visit everything, but foreign items have their own levels | |
263 | hir::ItemForeignMod(ref foreign_mod) => { | |
264 | for foreign_item in &foreign_mod.items { | |
265 | if self.get(foreign_item.id).is_some() { | |
476ff2be | 266 | self.reach(foreign_item.id).generics().predicates().item_type(); |
7453a54e SL |
267 | } |
268 | } | |
269 | } | |
270 | // Visit everything except for private fields | |
476ff2be SL |
271 | hir::ItemStruct(ref struct_def, _) | |
272 | hir::ItemUnion(ref struct_def, _) => { | |
7453a54e | 273 | if item_level.is_some() { |
476ff2be | 274 | self.reach(item.id).generics().predicates(); |
7453a54e | 275 | for field in struct_def.fields() { |
54a0048b | 276 | if self.get(field.id).is_some() { |
476ff2be | 277 | self.reach(field.id).item_type(); |
85aaf69f SL |
278 | } |
279 | } | |
280 | } | |
281 | } | |
85aaf69f SL |
282 | } |
283 | ||
92a42be0 SL |
284 | let orig_level = self.prev_level; |
285 | self.prev_level = item_level; | |
286 | ||
287 | intravisit::walk_item(self, item); | |
85aaf69f | 288 | |
92a42be0 | 289 | self.prev_level = orig_level; |
85aaf69f SL |
290 | } |
291 | ||
476ff2be | 292 | fn visit_block(&mut self, b: &'tcx hir::Block) { |
92a42be0 SL |
293 | let orig_level = replace(&mut self.prev_level, None); |
294 | ||
295 | // Blocks can have public items, for example impls, but they always | |
296 | // start as completely private regardless of publicity of a function, | |
297 | // constant, type, field, etc. in which this block resides | |
298 | intravisit::walk_block(self, b); | |
299 | ||
300 | self.prev_level = orig_level; | |
85aaf69f SL |
301 | } |
302 | ||
476ff2be | 303 | fn visit_mod(&mut self, m: &'tcx hir::Mod, _sp: Span, id: ast::NodeId) { |
85aaf69f SL |
304 | // This code is here instead of in visit_item so that the |
305 | // crate module gets processed as well. | |
92a42be0 | 306 | if self.prev_level.is_some() { |
9cc50fc6 SL |
307 | if let Some(exports) = self.export_map.get(&id) { |
308 | for export in exports { | |
32a655c1 | 309 | if let Some(node_id) = self.tcx.hir.as_local_node_id(export.def.def_id()) { |
9cc50fc6 SL |
310 | self.update(node_id, Some(AccessLevel::Exported)); |
311 | } | |
85aaf69f SL |
312 | } |
313 | } | |
314 | } | |
92a42be0 | 315 | |
5bcae85e | 316 | intravisit::walk_mod(self, m, id); |
92a42be0 SL |
317 | } |
318 | ||
476ff2be | 319 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { |
92a42be0 | 320 | self.update(md.id, Some(AccessLevel::Public)); |
85aaf69f | 321 | } |
85aaf69f | 322 | |
476ff2be SL |
323 | fn visit_ty(&mut self, ty: &'tcx hir::Ty) { |
324 | if let hir::TyImplTrait(..) = ty.node { | |
325 | if self.get(ty.id).is_some() { | |
326 | // Reach the (potentially private) type and the API being exposed. | |
327 | self.reach(ty.id).item_type().predicates(); | |
7453a54e SL |
328 | } |
329 | } | |
476ff2be SL |
330 | |
331 | intravisit::walk_ty(self, ty); | |
7453a54e SL |
332 | } |
333 | } | |
334 | ||
476ff2be SL |
335 | impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { |
336 | fn generics(&mut self) -> &mut Self { | |
8bb4bdeb XL |
337 | for def in &self.ev.tcx.item_generics(self.item_def_id).types { |
338 | if def.has_default { | |
339 | self.ev.tcx.item_type(def.def_id).visit_with(self); | |
340 | } | |
341 | } | |
476ff2be SL |
342 | self |
343 | } | |
9e0c209e | 344 | |
476ff2be SL |
345 | fn predicates(&mut self) -> &mut Self { |
346 | self.ev.tcx.item_predicates(self.item_def_id).visit_with(self); | |
347 | self | |
348 | } | |
349 | ||
350 | fn item_type(&mut self) -> &mut Self { | |
351 | self.ev.tcx.item_type(self.item_def_id).visit_with(self); | |
352 | self | |
353 | } | |
354 | ||
355 | fn impl_trait_ref(&mut self) -> &mut Self { | |
356 | self.ev.tcx.impl_trait_ref(self.item_def_id).visit_with(self); | |
357 | self | |
358 | } | |
359 | } | |
360 | ||
361 | impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> { | |
362 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
363 | let ty_def_id = match ty.sty { | |
364 | ty::TyAdt(adt, _) => Some(adt.did), | |
365 | ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), | |
366 | ty::TyProjection(ref proj) => Some(proj.trait_ref.def_id), | |
367 | ty::TyFnDef(def_id, ..) | | |
368 | ty::TyAnon(def_id, _) => Some(def_id), | |
369 | _ => None | |
370 | }; | |
7453a54e | 371 | |
476ff2be | 372 | if let Some(def_id) = ty_def_id { |
32a655c1 | 373 | if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(def_id) { |
476ff2be | 374 | self.ev.update(node_id, Some(AccessLevel::Reachable)); |
7453a54e SL |
375 | } |
376 | } | |
377 | ||
476ff2be | 378 | ty.super_visit_with(self) |
7453a54e SL |
379 | } |
380 | ||
476ff2be | 381 | fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool { |
32a655c1 SL |
382 | if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(trait_ref.def_id) { |
383 | let item = self.ev.tcx.hir.expect_item(node_id); | |
7453a54e SL |
384 | self.ev.update(item.id, Some(AccessLevel::Reachable)); |
385 | } | |
386 | ||
476ff2be | 387 | trait_ref.super_visit_with(self) |
7453a54e | 388 | } |
7453a54e SL |
389 | } |
390 | ||
85aaf69f SL |
391 | //////////////////////////////////////////////////////////////////////////////// |
392 | /// The privacy visitor, where privacy checks take place (violations reported) | |
393 | //////////////////////////////////////////////////////////////////////////////// | |
394 | ||
395 | struct PrivacyVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 396 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
32a655c1 | 397 | curitem: DefId, |
85aaf69f | 398 | in_foreign: bool, |
32a655c1 | 399 | tables: &'a ty::TypeckTables<'tcx>, |
85aaf69f SL |
400 | } |
401 | ||
402 | impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { | |
54a0048b | 403 | fn item_is_accessible(&self, did: DefId) -> bool { |
32a655c1 | 404 | match self.tcx.hir.as_local_node_id(did) { |
54a0048b | 405 | Some(node_id) => |
32a655c1 | 406 | ty::Visibility::from_hir(&self.tcx.hir.expect_item(node_id).vis, node_id, self.tcx), |
54a0048b | 407 | None => self.tcx.sess.cstore.visibility(did), |
32a655c1 | 408 | }.is_accessible_from(self.curitem, self.tcx) |
85aaf69f SL |
409 | } |
410 | ||
411 | // Checks that a field is in scope. | |
476ff2be | 412 | fn check_field(&mut self, span: Span, def: &'tcx ty::AdtDef, field: &'tcx ty::FieldDef) { |
32a655c1 | 413 | if !def.is_enum() && !field.vis.is_accessible_from(self.curitem, self.tcx) { |
9e0c209e SL |
414 | struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private", |
415 | field.name, def.variant_descr(), self.tcx.item_path_str(def.did)) | |
416 | .span_label(span, &format!("field `{}` is private", field.name)) | |
417 | .emit(); | |
85aaf69f SL |
418 | } |
419 | } | |
420 | ||
421 | // Checks that a method is in scope. | |
54a0048b | 422 | fn check_method(&mut self, span: Span, method_def_id: DefId) { |
476ff2be | 423 | match self.tcx.associated_item(method_def_id).container { |
85aaf69f SL |
424 | // Trait methods are always all public. The only controlling factor |
425 | // is whether the trait itself is accessible or not. | |
54a0048b SL |
426 | ty::TraitContainer(trait_def_id) if !self.item_is_accessible(trait_def_id) => { |
427 | let msg = format!("source trait `{}` is private", | |
428 | self.tcx.item_path_str(trait_def_id)); | |
429 | self.tcx.sess.span_err(span, &msg); | |
85aaf69f | 430 | } |
54a0048b | 431 | _ => {} |
85aaf69f SL |
432 | } |
433 | } | |
434 | } | |
435 | ||
476ff2be | 436 | impl<'a, 'tcx> Visitor<'tcx> for PrivacyVisitor<'a, 'tcx> { |
92a42be0 SL |
437 | /// We want to visit items in the context of their containing |
438 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 439 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 SL |
440 | NestedVisitorMap::All(&self.tcx.hir) |
441 | } | |
442 | ||
443 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
444 | let old_tables = self.tables; | |
445 | self.tables = self.tcx.body_tables(body); | |
446 | let body = self.tcx.hir.body(body); | |
447 | self.visit_body(body); | |
448 | self.tables = old_tables; | |
92a42be0 SL |
449 | } |
450 | ||
476ff2be | 451 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
32a655c1 | 452 | let orig_curitem = replace(&mut self.curitem, self.tcx.hir.local_def_id(item.id)); |
92a42be0 | 453 | intravisit::walk_item(self, item); |
85aaf69f SL |
454 | self.curitem = orig_curitem; |
455 | } | |
456 | ||
476ff2be | 457 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { |
85aaf69f | 458 | match expr.node { |
54a0048b | 459 | hir::ExprMethodCall(..) => { |
c1a9b12d | 460 | let method_call = ty::MethodCall::expr(expr.id); |
32a655c1 | 461 | let method = self.tables.method_map[&method_call]; |
54a0048b | 462 | self.check_method(expr.span, method.def_id); |
85aaf69f | 463 | } |
476ff2be | 464 | hir::ExprStruct(ref qpath, ref expr_fields, _) => { |
32a655c1 SL |
465 | let def = self.tables.qpath_def(qpath, expr.id); |
466 | let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap(); | |
476ff2be | 467 | let variant = adt.variant_of_def(def); |
e9174d1e SL |
468 | // RFC 736: ensure all unmentioned fields are visible. |
469 | // Rather than computing the set of unmentioned fields | |
9e0c209e SL |
470 | // (i.e. `all_fields - fields`), just check them all, |
471 | // unless the ADT is a union, then unmentioned fields | |
472 | // are not checked. | |
473 | if adt.is_union() { | |
474 | for expr_field in expr_fields { | |
475 | self.check_field(expr.span, adt, variant.field_named(expr_field.name.node)); | |
476 | } | |
477 | } else { | |
478 | for field in &variant.fields { | |
479 | let expr_field = expr_fields.iter().find(|f| f.name.node == field.name); | |
480 | let span = if let Some(f) = expr_field { f.span } else { expr.span }; | |
481 | self.check_field(span, adt, field); | |
482 | } | |
85aaf69f SL |
483 | } |
484 | } | |
85aaf69f SL |
485 | _ => {} |
486 | } | |
487 | ||
92a42be0 | 488 | intravisit::walk_expr(self, expr); |
85aaf69f SL |
489 | } |
490 | ||
476ff2be | 491 | fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { |
85aaf69f SL |
492 | // Foreign functions do not have their patterns mapped in the def_map, |
493 | // and there's nothing really relevant there anyway, so don't bother | |
494 | // checking privacy. If you can name the type then you can pass it to an | |
495 | // external C function anyway. | |
496 | if self.in_foreign { return } | |
497 | ||
498 | match pattern.node { | |
476ff2be | 499 | PatKind::Struct(ref qpath, ref fields, _) => { |
32a655c1 SL |
500 | let def = self.tables.qpath_def(qpath, pattern.id); |
501 | let adt = self.tables.pat_ty(pattern).ty_adt_def().unwrap(); | |
476ff2be | 502 | let variant = adt.variant_of_def(def); |
e9174d1e | 503 | for field in fields { |
9e0c209e | 504 | self.check_field(field.span, adt, variant.field_named(field.node.name)); |
85aaf69f SL |
505 | } |
506 | } | |
3157f602 | 507 | PatKind::TupleStruct(_, ref fields, ddpos) => { |
32a655c1 | 508 | match self.tables.pat_ty(pattern).sty { |
9e0c209e SL |
509 | // enum fields have no privacy at this time |
510 | ty::TyAdt(def, _) if !def.is_enum() => { | |
3157f602 XL |
511 | let expected_len = def.struct_variant().fields.len(); |
512 | for (i, field) in fields.iter().enumerate_and_adjust(expected_len, ddpos) { | |
7453a54e | 513 | if let PatKind::Wild = field.node { |
85aaf69f SL |
514 | continue |
515 | } | |
54a0048b | 516 | self.check_field(field.span, def, &def.struct_variant().fields[i]); |
85aaf69f SL |
517 | } |
518 | } | |
85aaf69f SL |
519 | _ => {} |
520 | } | |
85aaf69f SL |
521 | } |
522 | _ => {} | |
523 | } | |
524 | ||
92a42be0 | 525 | intravisit::walk_pat(self, pattern); |
85aaf69f SL |
526 | } |
527 | ||
476ff2be | 528 | fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) { |
85aaf69f | 529 | self.in_foreign = true; |
92a42be0 | 530 | intravisit::walk_foreign_item(self, fi); |
85aaf69f SL |
531 | self.in_foreign = false; |
532 | } | |
85aaf69f SL |
533 | } |
534 | ||
9cc50fc6 SL |
535 | /////////////////////////////////////////////////////////////////////////////// |
536 | /// Obsolete visitors for checking for private items in public interfaces. | |
537 | /// These visitors are supposed to be kept in frozen state and produce an | |
538 | /// "old error node set". For backward compatibility the new visitor reports | |
539 | /// warnings instead of hard errors when the erroneous node is not in this old set. | |
540 | /////////////////////////////////////////////////////////////////////////////// | |
541 | ||
542 | struct ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 543 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
92a42be0 | 544 | access_levels: &'a AccessLevels, |
85aaf69f | 545 | in_variant: bool, |
9cc50fc6 SL |
546 | // set of errors produced by this obsolete visitor |
547 | old_error_set: NodeSet, | |
85aaf69f SL |
548 | } |
549 | ||
9cc50fc6 SL |
550 | struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> { |
551 | inner: &'a ObsoleteVisiblePrivateTypesVisitor<'b, 'tcx>, | |
85aaf69f SL |
552 | /// whether the type refers to private types. |
553 | contains_private: bool, | |
554 | /// whether we've recurred at all (i.e. if we're pointing at the | |
555 | /// first type on which visit_ty was called). | |
556 | at_outer_type: bool, | |
557 | // whether that first type is a public path. | |
558 | outer_type_is_public_path: bool, | |
559 | } | |
560 | ||
9cc50fc6 | 561 | impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { |
476ff2be SL |
562 | fn path_is_private_type(&self, path: &hir::Path) -> bool { |
563 | let did = match path.def { | |
3157f602 XL |
564 | Def::PrimTy(..) | Def::SelfTy(..) => return false, |
565 | def => def.def_id(), | |
85aaf69f | 566 | }; |
b039eaaf | 567 | |
85aaf69f SL |
568 | // A path can only be private if: |
569 | // it's in this crate... | |
32a655c1 | 570 | if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { |
b039eaaf SL |
571 | // .. and it corresponds to a private type in the AST (this returns |
572 | // None for type parameters) | |
32a655c1 | 573 | match self.tcx.hir.find(node_id) { |
3157f602 | 574 | Some(hir::map::NodeItem(ref item)) => item.vis != hir::Public, |
b039eaaf SL |
575 | Some(_) | None => false, |
576 | } | |
577 | } else { | |
85aaf69f SL |
578 | return false |
579 | } | |
85aaf69f SL |
580 | } |
581 | ||
582 | fn trait_is_public(&self, trait_id: ast::NodeId) -> bool { | |
583 | // FIXME: this would preferably be using `exported_items`, but all | |
584 | // traits are exported currently (see `EmbargoVisitor.exported_trait`) | |
92a42be0 | 585 | self.access_levels.is_public(trait_id) |
85aaf69f SL |
586 | } |
587 | ||
9cc50fc6 | 588 | fn check_ty_param_bound(&mut self, |
e9174d1e SL |
589 | ty_param_bound: &hir::TyParamBound) { |
590 | if let hir::TraitTyParamBound(ref trait_ref, _) = *ty_param_bound { | |
476ff2be | 591 | if self.path_is_private_type(&trait_ref.trait_ref.path) { |
9cc50fc6 | 592 | self.old_error_set.insert(trait_ref.trait_ref.ref_id); |
85aaf69f SL |
593 | } |
594 | } | |
595 | } | |
c34b1796 | 596 | |
54a0048b SL |
597 | fn item_is_public(&self, id: &ast::NodeId, vis: &hir::Visibility) -> bool { |
598 | self.access_levels.is_reachable(*id) || *vis == hir::Public | |
c34b1796 | 599 | } |
85aaf69f SL |
600 | } |
601 | ||
9cc50fc6 | 602 | impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { |
476ff2be SL |
603 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { |
604 | NestedVisitorMap::None | |
605 | } | |
606 | ||
e9174d1e | 607 | fn visit_ty(&mut self, ty: &hir::Ty) { |
476ff2be SL |
608 | if let hir::TyPath(hir::QPath::Resolved(_, ref path)) = ty.node { |
609 | if self.inner.path_is_private_type(path) { | |
85aaf69f SL |
610 | self.contains_private = true; |
611 | // found what we're looking for so let's stop | |
612 | // working. | |
613 | return | |
476ff2be SL |
614 | } |
615 | } | |
616 | if let hir::TyPath(_) = ty.node { | |
617 | if self.at_outer_type { | |
85aaf69f SL |
618 | self.outer_type_is_public_path = true; |
619 | } | |
620 | } | |
621 | self.at_outer_type = false; | |
92a42be0 | 622 | intravisit::walk_ty(self, ty) |
85aaf69f SL |
623 | } |
624 | ||
625 | // don't want to recurse into [, .. expr] | |
e9174d1e | 626 | fn visit_expr(&mut self, _: &hir::Expr) {} |
85aaf69f SL |
627 | } |
628 | ||
476ff2be | 629 | impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { |
92a42be0 SL |
630 | /// We want to visit items in the context of their containing |
631 | /// module and so forth, so supply a crate for doing a deep walk. | |
476ff2be | 632 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 633 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 SL |
634 | } |
635 | ||
476ff2be | 636 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
85aaf69f SL |
637 | match item.node { |
638 | // contents of a private mod can be reexported, so we need | |
639 | // to check internals. | |
e9174d1e | 640 | hir::ItemMod(_) => {} |
85aaf69f SL |
641 | |
642 | // An `extern {}` doesn't introduce a new privacy | |
643 | // namespace (the contents have their own privacies). | |
e9174d1e | 644 | hir::ItemForeignMod(_) => {} |
85aaf69f | 645 | |
9e0c209e | 646 | hir::ItemTrait(.., ref bounds, _) => { |
85aaf69f SL |
647 | if !self.trait_is_public(item.id) { |
648 | return | |
649 | } | |
650 | ||
62682a34 | 651 | for bound in bounds.iter() { |
85aaf69f SL |
652 | self.check_ty_param_bound(bound) |
653 | } | |
654 | } | |
655 | ||
656 | // impls need some special handling to try to offer useful | |
657 | // error messages without (too many) false positives | |
658 | // (i.e. we could just return here to not check them at | |
659 | // all, or some worse estimation of whether an impl is | |
c34b1796 | 660 | // publicly visible). |
476ff2be | 661 | hir::ItemImpl(.., ref g, ref trait_ref, ref self_, ref impl_item_refs) => { |
85aaf69f SL |
662 | // `impl [... for] Private` is never visible. |
663 | let self_contains_private; | |
664 | // impl [... for] Public<...>, but not `impl [... for] | |
d9579d0f | 665 | // Vec<Public>` or `(Public,)` etc. |
85aaf69f SL |
666 | let self_is_public_path; |
667 | ||
668 | // check the properties of the Self type: | |
669 | { | |
9cc50fc6 | 670 | let mut visitor = ObsoleteCheckTypeForPrivatenessVisitor { |
85aaf69f SL |
671 | inner: self, |
672 | contains_private: false, | |
673 | at_outer_type: true, | |
674 | outer_type_is_public_path: false, | |
675 | }; | |
7453a54e | 676 | visitor.visit_ty(&self_); |
85aaf69f SL |
677 | self_contains_private = visitor.contains_private; |
678 | self_is_public_path = visitor.outer_type_is_public_path; | |
679 | } | |
680 | ||
681 | // miscellaneous info about the impl | |
682 | ||
683 | // `true` iff this is `impl Private for ...`. | |
684 | let not_private_trait = | |
685 | trait_ref.as_ref().map_or(true, // no trait counts as public trait | |
686 | |tr| { | |
476ff2be | 687 | let did = tr.path.def.def_id(); |
85aaf69f | 688 | |
32a655c1 | 689 | if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { |
b039eaaf SL |
690 | self.trait_is_public(node_id) |
691 | } else { | |
692 | true // external traits must be public | |
693 | } | |
85aaf69f SL |
694 | }); |
695 | ||
696 | // `true` iff this is a trait impl or at least one method is public. | |
697 | // | |
698 | // `impl Public { $( fn ...() {} )* }` is not visible. | |
699 | // | |
700 | // This is required over just using the methods' privacy | |
701 | // directly because we might have `impl<T: Foo<Private>> ...`, | |
702 | // and we shouldn't warn about the generics if all the methods | |
703 | // are private (because `T` won't be visible externally). | |
704 | let trait_or_some_public_method = | |
705 | trait_ref.is_some() || | |
476ff2be SL |
706 | impl_item_refs.iter() |
707 | .any(|impl_item_ref| { | |
32a655c1 | 708 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
476ff2be SL |
709 | match impl_item.node { |
710 | hir::ImplItemKind::Const(..) | | |
711 | hir::ImplItemKind::Method(..) => { | |
712 | self.access_levels.is_reachable(impl_item.id) | |
713 | } | |
714 | hir::ImplItemKind::Type(_) => false, | |
715 | } | |
716 | }); | |
85aaf69f SL |
717 | |
718 | if !self_contains_private && | |
719 | not_private_trait && | |
720 | trait_or_some_public_method { | |
721 | ||
92a42be0 | 722 | intravisit::walk_generics(self, g); |
85aaf69f SL |
723 | |
724 | match *trait_ref { | |
725 | None => { | |
476ff2be | 726 | for impl_item_ref in impl_item_refs { |
c34b1796 AL |
727 | // This is where we choose whether to walk down |
728 | // further into the impl to check its items. We | |
729 | // should only walk into public items so that we | |
730 | // don't erroneously report errors for private | |
731 | // types in private items. | |
32a655c1 | 732 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
c34b1796 | 733 | match impl_item.node { |
92a42be0 SL |
734 | hir::ImplItemKind::Const(..) | |
735 | hir::ImplItemKind::Method(..) | |
54a0048b | 736 | if self.item_is_public(&impl_item.id, &impl_item.vis) => |
c34b1796 | 737 | { |
92a42be0 | 738 | intravisit::walk_impl_item(self, impl_item) |
c34b1796 | 739 | } |
92a42be0 SL |
740 | hir::ImplItemKind::Type(..) => { |
741 | intravisit::walk_impl_item(self, impl_item) | |
85aaf69f | 742 | } |
c34b1796 | 743 | _ => {} |
85aaf69f SL |
744 | } |
745 | } | |
746 | } | |
747 | Some(ref tr) => { | |
c34b1796 | 748 | // Any private types in a trait impl fall into three |
85aaf69f SL |
749 | // categories. |
750 | // 1. mentioned in the trait definition | |
751 | // 2. mentioned in the type params/generics | |
c34b1796 | 752 | // 3. mentioned in the associated types of the impl |
85aaf69f SL |
753 | // |
754 | // Those in 1. can only occur if the trait is in | |
755 | // this crate and will've been warned about on the | |
756 | // trait definition (there's no need to warn twice | |
757 | // so we don't check the methods). | |
758 | // | |
759 | // Those in 2. are warned via walk_generics and this | |
760 | // call here. | |
92a42be0 | 761 | intravisit::walk_path(self, &tr.path); |
c34b1796 AL |
762 | |
763 | // Those in 3. are warned with this call. | |
476ff2be | 764 | for impl_item_ref in impl_item_refs { |
32a655c1 | 765 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
92a42be0 | 766 | if let hir::ImplItemKind::Type(ref ty) = impl_item.node { |
d9579d0f | 767 | self.visit_ty(ty); |
c34b1796 AL |
768 | } |
769 | } | |
85aaf69f SL |
770 | } |
771 | } | |
772 | } else if trait_ref.is_none() && self_is_public_path { | |
773 | // impl Public<Private> { ... }. Any public static | |
774 | // methods will be visible as `Public::foo`. | |
775 | let mut found_pub_static = false; | |
476ff2be | 776 | for impl_item_ref in impl_item_refs { |
32a655c1 SL |
777 | if self.item_is_public(&impl_item_ref.id.node_id, &impl_item_ref.vis) { |
778 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); | |
779 | match impl_item_ref.kind { | |
780 | hir::AssociatedItemKind::Const => { | |
d9579d0f | 781 | found_pub_static = true; |
92a42be0 | 782 | intravisit::walk_impl_item(self, impl_item); |
d9579d0f | 783 | } |
32a655c1 | 784 | hir::AssociatedItemKind::Method { has_self: false } => { |
85aaf69f | 785 | found_pub_static = true; |
92a42be0 | 786 | intravisit::walk_impl_item(self, impl_item); |
85aaf69f | 787 | } |
32a655c1 | 788 | _ => {} |
85aaf69f | 789 | } |
85aaf69f SL |
790 | } |
791 | } | |
792 | if found_pub_static { | |
92a42be0 | 793 | intravisit::walk_generics(self, g) |
85aaf69f SL |
794 | } |
795 | } | |
796 | return | |
797 | } | |
798 | ||
799 | // `type ... = ...;` can contain private types, because | |
800 | // we're introducing a new name. | |
e9174d1e | 801 | hir::ItemTy(..) => return, |
85aaf69f SL |
802 | |
803 | // not at all public, so we don't care | |
54a0048b | 804 | _ if !self.item_is_public(&item.id, &item.vis) => { |
c34b1796 AL |
805 | return; |
806 | } | |
85aaf69f SL |
807 | |
808 | _ => {} | |
809 | } | |
810 | ||
c34b1796 | 811 | // We've carefully constructed it so that if we're here, then |
85aaf69f SL |
812 | // any `visit_ty`'s will be called on things that are in |
813 | // public signatures, i.e. things that we're interested in for | |
814 | // this visitor. | |
92a42be0 | 815 | intravisit::walk_item(self, item); |
85aaf69f SL |
816 | } |
817 | ||
476ff2be | 818 | fn visit_generics(&mut self, generics: &'tcx hir::Generics) { |
62682a34 SL |
819 | for ty_param in generics.ty_params.iter() { |
820 | for bound in ty_param.bounds.iter() { | |
85aaf69f SL |
821 | self.check_ty_param_bound(bound) |
822 | } | |
823 | } | |
824 | for predicate in &generics.where_clause.predicates { | |
825 | match predicate { | |
e9174d1e | 826 | &hir::WherePredicate::BoundPredicate(ref bound_pred) => { |
62682a34 | 827 | for bound in bound_pred.bounds.iter() { |
85aaf69f SL |
828 | self.check_ty_param_bound(bound) |
829 | } | |
830 | } | |
e9174d1e SL |
831 | &hir::WherePredicate::RegionPredicate(_) => {} |
832 | &hir::WherePredicate::EqPredicate(ref eq_pred) => { | |
32a655c1 | 833 | self.visit_ty(&eq_pred.rhs_ty); |
85aaf69f SL |
834 | } |
835 | } | |
836 | } | |
837 | } | |
838 | ||
476ff2be | 839 | fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { |
92a42be0 SL |
840 | if self.access_levels.is_reachable(item.id) { |
841 | intravisit::walk_foreign_item(self, item) | |
85aaf69f SL |
842 | } |
843 | } | |
844 | ||
476ff2be SL |
845 | fn visit_ty(&mut self, t: &'tcx hir::Ty) { |
846 | if let hir::TyPath(hir::QPath::Resolved(_, ref path)) = t.node { | |
847 | if self.path_is_private_type(path) { | |
9cc50fc6 | 848 | self.old_error_set.insert(t.id); |
85aaf69f SL |
849 | } |
850 | } | |
92a42be0 | 851 | intravisit::walk_ty(self, t) |
85aaf69f SL |
852 | } |
853 | ||
476ff2be SL |
854 | fn visit_variant(&mut self, |
855 | v: &'tcx hir::Variant, | |
856 | g: &'tcx hir::Generics, | |
857 | item_id: ast::NodeId) { | |
92a42be0 | 858 | if self.access_levels.is_reachable(v.node.data.id()) { |
85aaf69f | 859 | self.in_variant = true; |
92a42be0 | 860 | intravisit::walk_variant(self, v, g, item_id); |
85aaf69f SL |
861 | self.in_variant = false; |
862 | } | |
863 | } | |
864 | ||
476ff2be | 865 | fn visit_struct_field(&mut self, s: &'tcx hir::StructField) { |
54a0048b | 866 | if s.vis == hir::Public || self.in_variant { |
92a42be0 | 867 | intravisit::walk_struct_field(self, s); |
85aaf69f SL |
868 | } |
869 | } | |
870 | ||
85aaf69f SL |
871 | // we don't need to introspect into these at all: an |
872 | // expression/block context can't possibly contain exported things. | |
873 | // (Making them no-ops stops us from traversing the whole AST without | |
874 | // having to be super careful about our `walk_...` calls above.) | |
476ff2be SL |
875 | fn visit_block(&mut self, _: &'tcx hir::Block) {} |
876 | fn visit_expr(&mut self, _: &'tcx hir::Expr) {} | |
85aaf69f SL |
877 | } |
878 | ||
9cc50fc6 SL |
879 | /////////////////////////////////////////////////////////////////////////////// |
880 | /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and | |
881 | /// finds any private components in it. | |
882 | /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types | |
883 | /// and traits in public interfaces. | |
884 | /////////////////////////////////////////////////////////////////////////////// | |
885 | ||
886 | struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 887 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
476ff2be SL |
888 | item_def_id: DefId, |
889 | span: Span, | |
54a0048b SL |
890 | /// The visitor checks that each component type is at least this visible |
891 | required_visibility: ty::Visibility, | |
892 | /// The visibility of the least visible component that has been visited | |
893 | min_visibility: ty::Visibility, | |
476ff2be | 894 | has_old_errors: bool, |
9cc50fc6 SL |
895 | } |
896 | ||
897 | impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
476ff2be | 898 | fn generics(&mut self) -> &mut Self { |
8bb4bdeb XL |
899 | for def in &self.tcx.item_generics(self.item_def_id).types { |
900 | if def.has_default { | |
901 | self.tcx.item_type(def.def_id).visit_with(self); | |
902 | } | |
903 | } | |
476ff2be | 904 | self |
54a0048b | 905 | } |
54a0048b | 906 | |
476ff2be SL |
907 | fn predicates(&mut self) -> &mut Self { |
908 | self.tcx.item_predicates(self.item_def_id).visit_with(self); | |
909 | self | |
910 | } | |
911 | ||
912 | fn item_type(&mut self) -> &mut Self { | |
913 | self.tcx.item_type(self.item_def_id).visit_with(self); | |
914 | self | |
915 | } | |
916 | ||
917 | fn impl_trait_ref(&mut self) -> &mut Self { | |
918 | self.tcx.impl_trait_ref(self.item_def_id).visit_with(self); | |
919 | self | |
9cc50fc6 SL |
920 | } |
921 | } | |
922 | ||
476ff2be SL |
923 | impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { |
924 | fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { | |
925 | let ty_def_id = match ty.sty { | |
926 | ty::TyAdt(adt, _) => Some(adt.did), | |
927 | ty::TyDynamic(ref obj, ..) => obj.principal().map(|p| p.def_id()), | |
928 | ty::TyProjection(ref proj) => { | |
32a655c1 | 929 | if self.required_visibility == ty::Visibility::Invisible { |
9cc50fc6 SL |
930 | // Conservatively approximate the whole type alias as public without |
931 | // recursing into its components when determining impl publicity. | |
932 | // For example, `impl <Type as Trait>::Alias {...}` may be a public impl | |
933 | // even if both `Type` and `Trait` are private. | |
934 | // Ideally, associated types should be substituted in the same way as | |
935 | // free type aliases, but this isn't done yet. | |
476ff2be | 936 | return false; |
9cc50fc6 | 937 | } |
9e0c209e | 938 | |
476ff2be SL |
939 | Some(proj.trait_ref.def_id) |
940 | } | |
941 | _ => None | |
942 | }; | |
54a0048b | 943 | |
476ff2be SL |
944 | if let Some(def_id) = ty_def_id { |
945 | // Non-local means public (private items can't leave their crate, modulo bugs) | |
32a655c1 SL |
946 | if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { |
947 | let item = self.tcx.hir.expect_item(node_id); | |
476ff2be SL |
948 | let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); |
949 | ||
32a655c1 | 950 | if !vis.is_at_least(self.min_visibility, self.tcx) { |
476ff2be SL |
951 | self.min_visibility = vis; |
952 | } | |
32a655c1 | 953 | if !vis.is_at_least(self.required_visibility, self.tcx) { |
476ff2be SL |
954 | if self.tcx.sess.features.borrow().pub_restricted || self.has_old_errors { |
955 | let mut err = struct_span_err!(self.tcx.sess, self.span, E0446, | |
956 | "private type `{}` in public interface", ty); | |
957 | err.span_label(self.span, &format!("can't leak private type")); | |
958 | err.emit(); | |
959 | } else { | |
960 | self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC, | |
961 | node_id, | |
962 | self.span, | |
963 | format!("private type `{}` in public \ | |
964 | interface (error E0446)", ty)); | |
9cc50fc6 SL |
965 | } |
966 | } | |
9cc50fc6 SL |
967 | } |
968 | } | |
969 | ||
476ff2be SL |
970 | if let ty::TyProjection(ref proj) = ty.sty { |
971 | // Avoid calling `visit_trait_ref` below on the trait, | |
972 | // as we have already checked the trait itself above. | |
973 | proj.trait_ref.super_visit_with(self) | |
974 | } else { | |
975 | ty.super_visit_with(self) | |
976 | } | |
9cc50fc6 SL |
977 | } |
978 | ||
476ff2be | 979 | fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool { |
9cc50fc6 | 980 | // Non-local means public (private items can't leave their crate, modulo bugs) |
32a655c1 SL |
981 | if let Some(node_id) = self.tcx.hir.as_local_node_id(trait_ref.def_id) { |
982 | let item = self.tcx.hir.expect_item(node_id); | |
a7813a04 | 983 | let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx); |
54a0048b | 984 | |
32a655c1 | 985 | if !vis.is_at_least(self.min_visibility, self.tcx) { |
54a0048b SL |
986 | self.min_visibility = vis; |
987 | } | |
32a655c1 | 988 | if !vis.is_at_least(self.required_visibility, self.tcx) { |
476ff2be SL |
989 | if self.tcx.sess.features.borrow().pub_restricted || self.has_old_errors { |
990 | struct_span_err!(self.tcx.sess, self.span, E0445, | |
991 | "private trait `{}` in public interface", trait_ref) | |
992 | .span_label(self.span, &format!( | |
9e0c209e SL |
993 | "private trait can't be public")) |
994 | .emit(); | |
54a0048b SL |
995 | } else { |
996 | self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC, | |
997 | node_id, | |
476ff2be SL |
998 | self.span, |
999 | format!("private trait `{}` in public \ | |
1000 | interface (error E0445)", trait_ref)); | |
9cc50fc6 | 1001 | } |
9cc50fc6 SL |
1002 | } |
1003 | } | |
1004 | ||
476ff2be | 1005 | trait_ref.super_visit_with(self) |
9cc50fc6 | 1006 | } |
9cc50fc6 SL |
1007 | } |
1008 | ||
1009 | struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 1010 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
9cc50fc6 | 1011 | old_error_set: &'a NodeSet, |
476ff2be | 1012 | inner_visibility: ty::Visibility, |
9cc50fc6 SL |
1013 | } |
1014 | ||
1015 | impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { | |
476ff2be SL |
1016 | fn check(&self, item_id: ast::NodeId, required_visibility: ty::Visibility) |
1017 | -> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { | |
1018 | let mut has_old_errors = false; | |
1019 | ||
1020 | // Slow path taken only if there any errors in the crate. | |
1021 | for &id in self.old_error_set { | |
1022 | // Walk up the nodes until we find `item_id` (or we hit a root). | |
1023 | let mut id = id; | |
1024 | loop { | |
1025 | if id == item_id { | |
1026 | has_old_errors = true; | |
1027 | break; | |
1028 | } | |
32a655c1 | 1029 | let parent = self.tcx.hir.get_parent_node(id); |
476ff2be SL |
1030 | if parent == id { |
1031 | break; | |
1032 | } | |
1033 | id = parent; | |
1034 | } | |
1035 | ||
1036 | if has_old_errors { | |
1037 | break; | |
1038 | } | |
1039 | } | |
9cc50fc6 | 1040 | |
476ff2be SL |
1041 | SearchInterfaceForPrivateItemsVisitor { |
1042 | tcx: self.tcx, | |
32a655c1 SL |
1043 | item_def_id: self.tcx.hir.local_def_id(item_id), |
1044 | span: self.tcx.hir.span(item_id), | |
476ff2be SL |
1045 | min_visibility: ty::Visibility::Public, |
1046 | required_visibility: required_visibility, | |
1047 | has_old_errors: has_old_errors, | |
1048 | } | |
9cc50fc6 SL |
1049 | } |
1050 | } | |
1051 | ||
476ff2be SL |
1052 | impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { |
1053 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
32a655c1 | 1054 | NestedVisitorMap::OnlyBodies(&self.tcx.hir) |
476ff2be SL |
1055 | } |
1056 | ||
1057 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
1058 | let tcx = self.tcx; | |
54a0048b | 1059 | let min = |vis1: ty::Visibility, vis2| { |
32a655c1 | 1060 | if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } |
9cc50fc6 | 1061 | }; |
54a0048b | 1062 | |
476ff2be | 1063 | let item_visibility = ty::Visibility::from_hir(&item.vis, item.id, tcx); |
54a0048b | 1064 | |
9cc50fc6 SL |
1065 | match item.node { |
1066 | // Crates are always public | |
1067 | hir::ItemExternCrate(..) => {} | |
1068 | // All nested items are checked by visit_item | |
1069 | hir::ItemMod(..) => {} | |
1070 | // Checked in resolve | |
1071 | hir::ItemUse(..) => {} | |
1072 | // Subitems of these items have inherited publicity | |
1073 | hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) | | |
476ff2be SL |
1074 | hir::ItemTy(..) => { |
1075 | self.check(item.id, item_visibility).generics().predicates().item_type(); | |
1076 | ||
1077 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1078 | self.inner_visibility = item_visibility; | |
1079 | intravisit::walk_item(self, item); | |
1080 | } | |
32a655c1 | 1081 | hir::ItemTrait(.., ref trait_item_refs) => { |
476ff2be SL |
1082 | self.check(item.id, item_visibility).generics().predicates(); |
1083 | ||
32a655c1 SL |
1084 | for trait_item_ref in trait_item_refs { |
1085 | let mut check = self.check(trait_item_ref.id.node_id, item_visibility); | |
476ff2be SL |
1086 | check.generics().predicates(); |
1087 | ||
32a655c1 SL |
1088 | if trait_item_ref.kind == hir::AssociatedItemKind::Type && |
1089 | !trait_item_ref.defaultness.has_value() { | |
476ff2be SL |
1090 | // No type to visit. |
1091 | } else { | |
1092 | check.item_type(); | |
1093 | } | |
1094 | } | |
1095 | } | |
1096 | hir::ItemEnum(ref def, _) => { | |
1097 | self.check(item.id, item_visibility).generics().predicates(); | |
1098 | ||
1099 | for variant in &def.variants { | |
1100 | for field in variant.node.data.fields() { | |
1101 | self.check(field.id, item_visibility).item_type(); | |
1102 | } | |
1103 | } | |
9cc50fc6 SL |
1104 | } |
1105 | // Subitems of foreign modules have their own publicity | |
1106 | hir::ItemForeignMod(ref foreign_mod) => { | |
1107 | for foreign_item in &foreign_mod.items { | |
476ff2be SL |
1108 | let vis = ty::Visibility::from_hir(&foreign_item.vis, item.id, tcx); |
1109 | self.check(foreign_item.id, vis).generics().predicates().item_type(); | |
9cc50fc6 SL |
1110 | } |
1111 | } | |
9e0c209e | 1112 | // Subitems of structs and unions have their own publicity |
476ff2be SL |
1113 | hir::ItemStruct(ref struct_def, _) | |
1114 | hir::ItemUnion(ref struct_def, _) => { | |
1115 | self.check(item.id, item_visibility).generics().predicates(); | |
54a0048b SL |
1116 | |
1117 | for field in struct_def.fields() { | |
476ff2be SL |
1118 | let field_visibility = ty::Visibility::from_hir(&field.vis, item.id, tcx); |
1119 | self.check(field.id, min(item_visibility, field_visibility)).item_type(); | |
9cc50fc6 SL |
1120 | } |
1121 | } | |
1122 | // The interface is empty | |
1123 | hir::ItemDefaultImpl(..) => {} | |
1124 | // An inherent impl is public when its type is public | |
1125 | // Subitems of inherent impls have their own publicity | |
476ff2be | 1126 | hir::ItemImpl(.., None, _, ref impl_item_refs) => { |
32a655c1 SL |
1127 | let ty_vis = |
1128 | self.check(item.id, ty::Visibility::Invisible).item_type().min_visibility; | |
476ff2be | 1129 | self.check(item.id, ty_vis).generics().predicates(); |
54a0048b | 1130 | |
476ff2be | 1131 | for impl_item_ref in impl_item_refs { |
32a655c1 | 1132 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
54a0048b | 1133 | let impl_item_vis = |
476ff2be SL |
1134 | ty::Visibility::from_hir(&impl_item.vis, item.id, tcx); |
1135 | self.check(impl_item.id, min(impl_item_vis, ty_vis)) | |
1136 | .generics().predicates().item_type(); | |
1137 | ||
1138 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1139 | self.inner_visibility = impl_item_vis; | |
1140 | intravisit::walk_impl_item(self, impl_item); | |
9cc50fc6 SL |
1141 | } |
1142 | } | |
1143 | // A trait impl is public when both its type and its trait are public | |
1144 | // Subitems of trait impls have inherited publicity | |
476ff2be | 1145 | hir::ItemImpl(.., Some(_), _, ref impl_item_refs) => { |
32a655c1 | 1146 | let vis = self.check(item.id, ty::Visibility::Invisible) |
476ff2be SL |
1147 | .item_type().impl_trait_ref().min_visibility; |
1148 | self.check(item.id, vis).generics().predicates(); | |
1149 | for impl_item_ref in impl_item_refs { | |
32a655c1 | 1150 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
476ff2be SL |
1151 | self.check(impl_item.id, vis).generics().predicates().item_type(); |
1152 | ||
1153 | // Recurse for e.g. `impl Trait` (see `visit_ty`). | |
1154 | self.inner_visibility = vis; | |
1155 | intravisit::walk_impl_item(self, impl_item); | |
9cc50fc6 SL |
1156 | } |
1157 | } | |
1158 | } | |
1159 | } | |
476ff2be SL |
1160 | |
1161 | fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem) { | |
1162 | // handled in `visit_item` above | |
1163 | } | |
1164 | ||
1165 | fn visit_ty(&mut self, ty: &'tcx hir::Ty) { | |
1166 | if let hir::TyImplTrait(..) = ty.node { | |
1167 | // Check the traits being exposed, as they're separate, | |
1168 | // e.g. `impl Iterator<Item=T>` has two predicates, | |
1169 | // `X: Iterator` and `<X as Iterator>::Item == T`, | |
1170 | // where `X` is the `impl Iterator<Item=T>` itself, | |
1171 | // stored in `item_predicates`, not in the `Ty` itself. | |
1172 | self.check(ty.id, self.inner_visibility).predicates(); | |
1173 | } | |
1174 | ||
1175 | intravisit::walk_ty(self, ty); | |
1176 | } | |
1177 | ||
1178 | // Don't recurse into expressions in array sizes or const initializers | |
1179 | fn visit_expr(&mut self, _: &'tcx hir::Expr) {} | |
1180 | // Don't recurse into patterns in function arguments | |
1181 | fn visit_pat(&mut self, _: &'tcx hir::Pat) {} | |
9cc50fc6 SL |
1182 | } |
1183 | ||
a7813a04 XL |
1184 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1185 | export_map: &def::ExportMap) | |
1186 | -> AccessLevels { | |
9cc50fc6 SL |
1187 | let _task = tcx.dep_graph.in_task(DepNode::Privacy); |
1188 | ||
32a655c1 | 1189 | let krate = tcx.hir.krate(); |
85aaf69f | 1190 | |
85aaf69f SL |
1191 | // Use the parent map to check the privacy of everything |
1192 | let mut visitor = PrivacyVisitor { | |
32a655c1 | 1193 | curitem: DefId::local(CRATE_DEF_INDEX), |
85aaf69f SL |
1194 | in_foreign: false, |
1195 | tcx: tcx, | |
32a655c1 | 1196 | tables: &ty::TypeckTables::empty(), |
85aaf69f | 1197 | }; |
92a42be0 | 1198 | intravisit::walk_crate(&mut visitor, krate); |
85aaf69f SL |
1199 | |
1200 | tcx.sess.abort_if_errors(); | |
1201 | ||
1202 | // Build up a set of all exported items in the AST. This is a set of all | |
1203 | // items which are reachable from external crates based on visibility. | |
1204 | let mut visitor = EmbargoVisitor { | |
1205 | tcx: tcx, | |
85aaf69f | 1206 | export_map: export_map, |
92a42be0 SL |
1207 | access_levels: Default::default(), |
1208 | prev_level: Some(AccessLevel::Public), | |
1209 | changed: false, | |
85aaf69f SL |
1210 | }; |
1211 | loop { | |
92a42be0 SL |
1212 | intravisit::walk_crate(&mut visitor, krate); |
1213 | if visitor.changed { | |
1214 | visitor.changed = false; | |
1215 | } else { | |
85aaf69f SL |
1216 | break |
1217 | } | |
1218 | } | |
92a42be0 | 1219 | visitor.update(ast::CRATE_NODE_ID, Some(AccessLevel::Public)); |
85aaf69f | 1220 | |
85aaf69f | 1221 | { |
9cc50fc6 | 1222 | let mut visitor = ObsoleteVisiblePrivateTypesVisitor { |
85aaf69f | 1223 | tcx: tcx, |
9cc50fc6 | 1224 | access_levels: &visitor.access_levels, |
85aaf69f | 1225 | in_variant: false, |
9cc50fc6 | 1226 | old_error_set: NodeSet(), |
85aaf69f | 1227 | }; |
92a42be0 | 1228 | intravisit::walk_crate(&mut visitor, krate); |
9cc50fc6 SL |
1229 | |
1230 | // Check for private types and traits in public interfaces | |
1231 | let mut visitor = PrivateItemsInPublicInterfacesVisitor { | |
1232 | tcx: tcx, | |
1233 | old_error_set: &visitor.old_error_set, | |
476ff2be | 1234 | inner_visibility: ty::Visibility::Public, |
9cc50fc6 | 1235 | }; |
476ff2be | 1236 | krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor)); |
85aaf69f | 1237 | } |
92a42be0 | 1238 | |
9cc50fc6 | 1239 | visitor.access_levels |
85aaf69f | 1240 | } |
92a42be0 SL |
1241 | |
1242 | __build_diagnostic_array! { librustc_privacy, DIAGNOSTICS } |