/
privacy.rs
883 lines (810 loc) · 36.1 KB
/
privacy.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A pass that checks to make sure private fields and methods aren't used
//! outside their scopes. This pass will also generate a set of exported items
//! which are available for use externally when compiled as a library.
use std::hashmap::{HashSet, HashMap};
use middle::resolve;
use middle::ty;
use middle::typeck::{method_map, method_origin, method_param};
use middle::typeck::{method_static, method_object};
use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::{is_local, def_id_of_def};
use syntax::attr;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::opt_vec;
use syntax::visit;
use syntax::visit::Visitor;
type Context<'self> = (&'self method_map, &'self resolve::ExportMap2);
// A set of all nodes in the ast which can be considered "publicly exported" in
// the sense that they are accessible from anywhere in any hierarchy.
pub type ExportedItems = HashSet<ast::NodeId>;
// This visitor is used to determine the parent of all nodes in question when it
// comes to privacy. This is used to determine later on if a usage is actually
// valid or not.
struct ParentVisitor<'self> {
parents: &'self mut HashMap<ast::NodeId, ast::NodeId>,
curparent: ast::NodeId,
}
impl<'self> Visitor<()> for ParentVisitor<'self> {
fn visit_item(&mut self, item: @ast::item, _: ()) {
self.parents.insert(item.id, self.curparent);
let prev = self.curparent;
match item.node {
ast::item_mod(*) => { self.curparent = item.id; }
// Enum variants are parented to the enum definition itself beacuse
// they inherit privacy
ast::item_enum(ref def, _) => {
for variant in def.variants.iter() {
// If variants are private, then their logical "parent" is
// the enclosing module because everyone in the enclosing
// module can still use the private variant
if variant.node.vis == ast::private {
self.parents.insert(variant.node.id, self.curparent);
// Otherwise, if the variant is public, then the parent is
// considered the enclosing enum because the enum will
// dictate the privacy visibility of this variant instead.
} else {
self.parents.insert(variant.node.id, item.id);
}
}
}
// Trait methods are always considered "public", but if the trait is
// private then we need some private item in the chain from the
// method to the root. In this case, if the trait is private, then
// parent all the methods to the trait to indicate that they're
// private.
ast::item_trait(_, _, ref methods) if item.vis != ast::public => {
for m in methods.iter() {
match *m {
ast::provided(ref m) => self.parents.insert(m.id, item.id),
ast::required(ref m) => self.parents.insert(m.id, item.id),
};
}
}
_ => {}
}
visit::walk_item(self, item, ());
self.curparent = prev;
}
fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) {
self.parents.insert(a.id, self.curparent);
visit::walk_foreign_item(self, a, ());
}
fn visit_fn(&mut self, a: &visit::fn_kind, b: &ast::fn_decl,
c: &ast::Block, d: Span, id: ast::NodeId, _: ()) {
// We already took care of some trait methods above, otherwise things
// like impl methods and pub trait methods are parented to the
// containing module, not the containing trait.
if !self.parents.contains_key(&id) {
self.parents.insert(id, self.curparent);
}
visit::walk_fn(self, a, b, c, d, id, ());
}
fn visit_struct_def(&mut self, s: @ast::struct_def, i: ast::Ident,
g: &ast::Generics, n: ast::NodeId, _: ()) {
// Struct constructors are parented to their struct definitions because
// they essentially are the struct definitions.
match s.ctor_id {
Some(id) => { self.parents.insert(id, n); }
None => {}
}
// While we have the id of the struct definition, go ahead and parent
// all the fields.
for field in s.fields.iter() {
let vis = match field.node.kind {
ast::named_field(_, vis) => vis,
ast::unnamed_field => continue
};
// Private fields are scoped to this module, so parent them directly
// to the module instead of the struct. This is similar to the case
// of private enum variants.
if vis == ast::private {
self.parents.insert(field.node.id, self.curparent);
// Otherwise public fields are scoped to the visibility of the
// struct itself
} else {
self.parents.insert(field.node.id, n);
}
}
visit::walk_struct_def(self, s, i, g, n, ())
}
}
// This visitor is used to determine which items of the ast are embargoed,
// otherwise known as not exported.
struct EmbargoVisitor<'self> {
exported_items: &'self mut ExportedItems,
exp_map2: &'self resolve::ExportMap2,
path_all_public: bool,
}
impl<'self> Visitor<()> for EmbargoVisitor<'self> {
fn visit_item(&mut self, item: @ast::item, _: ()) {
let orig_all_pub = self.path_all_public;
match item.node {
// impls/extern blocks do not break the "public chain" because they
// cannot have visibility qualifiers on them anyway
ast::item_impl(*) | ast::item_foreign_mod(*) => {}
// Private by default, hence we only retain the "public chain" if
// `pub` is explicitly listed.
_ => {
self.path_all_public = orig_all_pub && item.vis == ast::public;
}
}
if self.path_all_public {
self.exported_items.insert(item.id);
}
match item.node {
// Enum variants inherit from their parent, so if the enum is
// public all variants are public unless they're explicitly priv
ast::item_enum(ref def, _) if self.path_all_public => {
for variant in def.variants.iter() {
if variant.node.vis != ast::private {
self.exported_items.insert(variant.node.id);
}
}
}
// Methods which are public at the source are totally public.
ast::item_impl(_, None, _, ref methods) => {
for method in methods.iter() {
let public = match method.explicit_self.node {
ast::sty_static => self.path_all_public,
_ => true,
} && method.vis == ast::public;
if public {
self.exported_items.insert(method.id);
}
}
}
// Trait implementation methods are all completely public
ast::item_impl(_, Some(*), _, ref methods) => {
for method in methods.iter() {
debug!("exporting: {}", method.id);
self.exported_items.insert(method.id);
}
}
// Default methods on traits are all public so long as the trait is
// public
ast::item_trait(_, _, ref methods) if self.path_all_public => {
for method in methods.iter() {
match *method {
ast::provided(ref m) => {
debug!("provided {}", m.id);
self.exported_items.insert(m.id);
}
ast::required(ref m) => {
debug!("required {}", m.id);
self.exported_items.insert(m.id);
}
}
}
}
// Default methods on traits are all public so long as the trait is
// public
ast::item_struct(ref def, _) if self.path_all_public => {
match def.ctor_id {
Some(id) => { self.exported_items.insert(id); }
None => {}
}
}
_ => {}
}
visit::walk_item(self, item, ());
self.path_all_public = orig_all_pub;
}
fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) {
if self.path_all_public && a.vis == ast::public {
self.exported_items.insert(a.id);
}
}
}
struct PrivacyVisitor<'self> {
tcx: ty::ctxt,
curitem: ast::NodeId,
// Results of previous analyses necessary for privacy checking.
exported_items: &'self ExportedItems,
method_map: &'self method_map,
parents: &'self HashMap<ast::NodeId, ast::NodeId>,
external_exports: resolve::ExternalExports,
last_private_map: resolve::LastPrivateMap,
}
enum PrivacyResult {
Allowable,
ExternallyDenied,
DisallowedBy(ast::NodeId),
}
impl<'self> PrivacyVisitor<'self> {
// used when debugging
fn nodestr(&self, id: ast::NodeId) -> ~str {
ast_map::node_id_to_str(self.tcx.items, id, token::get_ident_interner())
}
// Determines whether the given definition is public from the point of view
// of the current item.
fn def_privacy(&self, did: ast::DefId) -> PrivacyResult {
if !is_local(did) {
if self.external_exports.contains(&did) {
debug!("privacy - {:?} was externally exported", did);
return Allowable;
}
debug!("privacy - is {:?} a public method", did);
return match self.tcx.methods.find(&did) {
Some(meth) => {
debug!("privacy - well at least it's a method: {:?}", meth);
match meth.container {
ty::TraitContainer(id) => {
debug!("privacy - recursing on trait {:?}", id);
self.def_privacy(id)
}
ty::ImplContainer(id) => {
match ty::impl_trait_ref(self.tcx, id) {
Some(t) => {
debug!("privacy - impl of trait {:?}", id);
self.def_privacy(t.def_id)
}
None => {
debug!("privacy - found a method {:?}",
meth.vis);
if meth.vis == ast::public {
Allowable
} else {
ExternallyDenied
}
}
}
}
}
}
None => {
debug!("privacy - nope, not even a method");
ExternallyDenied
}
};
} else if self.exported_items.contains(&did.node) {
debug!("privacy - exported item {}", self.nodestr(did.node));
return Allowable;
}
debug!("privacy - local {:?} not public all the way down", did);
// return quickly for things in the same module
if self.parents.find(&did.node) == self.parents.find(&self.curitem) {
debug!("privacy - same parent, we're done here");
return Allowable;
}
// We now know that there is at least one private member between the
// destination and the root.
let mut closest_private_id = did.node;
loop {
debug!("privacy - examining {}", self.nodestr(closest_private_id));
let vis = match self.tcx.items.find(&closest_private_id) {
Some(&ast_map::node_item(it, _)) => it.vis,
Some(&ast_map::node_method(ref m, _, _)) => m.vis,
Some(&ast_map::node_foreign_item(_, _, v, _)) => v,
Some(&ast_map::node_variant(ref v, _, _)) => {
// sadly enum variants still inherit visibility, so only
// break out of this is explicitly private
if v.node.vis == ast::private { break }
ast::public // need to move up a level (to the enum)
}
_ => ast::public,
};
if vis != ast::public { break }
closest_private_id = *self.parents.get(&closest_private_id);
// If we reached the top, then we should have been public all the
// way down in the first place...
assert!(closest_private_id != ast::DUMMY_NODE_ID);
}
debug!("privacy - closest priv {}", self.nodestr(closest_private_id));
if self.private_accessible(closest_private_id) {
Allowable
} else {
DisallowedBy(closest_private_id)
}
}
/// For a local private node in the AST, this function will determine
/// whether the node is accessible by the current module that iteration is
/// inside.
fn private_accessible(&self, id: ast::NodeId) -> bool {
let parent = *self.parents.get(&id);
debug!("privacy - accessible parent {}", self.nodestr(parent));
// After finding `did`'s closest private member, we roll ourselves back
// to see if this private member's parent is anywhere in our ancestry.
// By the privacy rules, we can access all of our ancestor's private
// members, so that's why we test the parent, and not the did itself.
let mut cur = self.curitem;
loop {
debug!("privacy - questioning {}", self.nodestr(cur));
match cur {
// If the relevant parent is in our history, then we're allowed
// to look inside any of our ancestor's immediate private items,
// so this access is valid.
x if x == parent => return true,
// If we've reached the root, then we couldn't access this item
// in the first place
ast::DUMMY_NODE_ID => return false,
// Keep going up
_ => {}
}
cur = *self.parents.get(&cur);
}
}
/// Guarantee that a particular definition is public, possibly emitting an
/// error message if it's not.
fn ensure_public(&self, span: Span, to_check: ast::DefId,
source_did: Option<ast::DefId>, msg: &str) -> bool {
match self.def_privacy(to_check) {
ExternallyDenied => {
self.tcx.sess.span_err(span, format!("{} is private", msg))
}
DisallowedBy(id) => {
if id == source_did.unwrap_or(to_check).node {
self.tcx.sess.span_err(span, format!("{} is private", msg));
return false;
} else {
self.tcx.sess.span_err(span, format!("{} is inaccessible",
msg));
}
match self.tcx.items.find(&id) {
Some(&ast_map::node_item(item, _)) => {
let desc = match item.node {
ast::item_mod(*) => "module",
ast::item_trait(*) => "trait",
_ => return false,
};
let msg = format!("{} `{}` is private", desc,
token::ident_to_str(&item.ident));
self.tcx.sess.span_note(span, msg);
}
Some(*) | None => {}
}
}
Allowable => return true
}
return false;
}
// Checks that a dereference of a univariant enum can occur.
fn check_variant(&self, span: Span, enum_id: ast::DefId) {
let variant_info = ty::enum_variants(self.tcx, enum_id)[0];
match self.def_privacy(variant_info.id) {
Allowable => {}
ExternallyDenied | DisallowedBy(*) => {
self.tcx.sess.span_err(span, "can only dereference enums \
with a single, public variant");
}
}
}
// Checks that a field is in scope.
// FIXME #6993: change type (and name) from Ident to Name
fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) {
let fields = ty::lookup_struct_fields(self.tcx, id);
for field in fields.iter() {
if field.name != ident.name { continue; }
// public fields are public everywhere
if field.vis != ast::private { break }
if !is_local(field.id) ||
!self.private_accessible(field.id.node) {
self.tcx.sess.span_err(span, format!("field `{}` is private",
token::ident_to_str(&ident)));
}
break;
}
}
// Given the ID of a method, checks to ensure it's in scope.
fn check_static_method(&mut self, span: Span, method_id: ast::DefId,
name: &ast::Ident) {
// If the method is a default method, we need to use the def_id of
// the default implementation.
let method_id = ty::method(self.tcx, method_id).provided_source
.unwrap_or(method_id);
self.ensure_public(span, method_id, None,
format!("method `{}`", token::ident_to_str(name)));
}
// Checks that a path is in scope.
fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) {
debug!("privacy - path {}", self.nodestr(path_id));
let def = self.tcx.def_map.get_copy(&path_id);
let ck = |tyname: &str| {
let origdid = def_id_of_def(def);
match *self.last_private_map.get(&path_id) {
resolve::AllPublic => {},
resolve::DependsOn(def) => {
let name = token::ident_to_str(&path.segments.last()
.identifier);
self.ensure_public(span, def, Some(origdid),
format!("{} `{}`", tyname, name));
}
}
};
match self.tcx.def_map.get_copy(&path_id) {
ast::DefStaticMethod(*) => ck("static method"),
ast::DefFn(*) => ck("function"),
ast::DefStatic(*) => ck("static"),
ast::DefVariant(*) => ck("variant"),
ast::DefTy(*) => ck("type"),
ast::DefTrait(*) => ck("trait"),
ast::DefStruct(*) => ck("struct"),
ast::DefMethod(_, Some(*)) => ck("trait method"),
ast::DefMethod(*) => ck("method"),
ast::DefMod(*) => ck("module"),
_ => {}
}
}
// Checks that a method is in scope.
fn check_method(&mut self, span: Span, origin: &method_origin,
ident: ast::Ident) {
match *origin {
method_static(method_id) => {
self.check_static_method(span, method_id, &ident)
}
method_param(method_param {
trait_id: trait_id,
method_num: method_num,
_
}) |
method_object(method_object {
trait_id: trait_id,
method_num: method_num,
_
}) => {
if !self.ensure_public(span, trait_id, None, "source trait") {
return
}
match self.tcx.items.find(&trait_id.node) {
Some(&ast_map::node_item(item, _)) => {
match item.node {
ast::item_trait(_, _, ref methods) => {
match methods[method_num] {
ast::provided(ref method) => {
let def = ast::DefId {
node: method.id,
crate: trait_id.crate,
};
self.ensure_public(span, def, None,
format!("method `{}`",
token::ident_to_str(
&method.ident)));
}
ast::required(_) => {
// Required methods can't be private.
}
}
}
_ => self.tcx.sess.span_bug(span, "trait wasn't \
actually a trait?!"),
}
}
Some(_) => self.tcx.sess.span_bug(span, "trait wasn't an \
item?!"),
None => self.tcx.sess.span_bug(span, "trait item wasn't \
found in the AST \
map?!"),
}
}
}
}
/// Validates all of the visibility qualifers placed on the item given. This
/// ensures that there are no extraneous qualifiers that don't actually do
/// anything. In theory these qualifiers wouldn't parse, but that may happen
/// later on down the road...
fn check_sane_privacy(&self, item: @ast::item) {
let tcx = self.tcx;
let check_inherited = |sp: Span, vis: ast::visibility, note: &str| {
if vis != ast::inherited {
tcx.sess.span_err(sp, "unnecessary visibility qualifier");
if note.len() > 0 {
tcx.sess.span_note(sp, note);
}
}
};
let check_not_priv = |sp: Span, vis: ast::visibility, note: &str| {
if vis == ast::private {
tcx.sess.span_err(sp, "unnecessary `priv` qualifier");
if note.len() > 0 {
tcx.sess.span_note(sp, note);
}
}
};
let check_struct = |def: &@ast::struct_def| {
for f in def.fields.iter() {
match f.node.kind {
ast::named_field(_, ast::public) => {
tcx.sess.span_err(f.span, "unnecessary `pub` \
visibility");
}
ast::named_field(_, ast::private) => {
// Fields should really be private by default...
}
ast::named_field(*) | ast::unnamed_field => {}
}
}
};
match item.node {
// implementations of traits don't need visibility qualifiers because
// that's controlled by having the trait in scope.
ast::item_impl(_, Some(*), _, ref methods) => {
check_inherited(item.span, item.vis,
"visibility qualifiers have no effect on trait \
impls");
for m in methods.iter() {
check_inherited(m.span, m.vis, "");
}
}
ast::item_impl(_, _, _, ref methods) => {
check_inherited(item.span, item.vis,
"place qualifiers on individual methods instead");
for i in methods.iter() {
check_not_priv(i.span, i.vis, "functions are private by \
default");
}
}
ast::item_foreign_mod(ref fm) => {
check_inherited(item.span, item.vis,
"place qualifiers on individual functions \
instead");
for i in fm.items.iter() {
check_not_priv(i.span, i.vis, "functions are private by \
default");
}
}
ast::item_enum(ref def, _) => {
for v in def.variants.iter() {
match v.node.vis {
ast::public => {
if item.vis == ast::public {
tcx.sess.span_err(v.span, "unnecessary `pub` \
visibility");
}
}
ast::private => {
if item.vis != ast::public {
tcx.sess.span_err(v.span, "unnecessary `priv` \
visibility");
}
}
ast::inherited => {}
}
match v.node.kind {
ast::struct_variant_kind(ref s) => check_struct(s),
ast::tuple_variant_kind(*) => {}
}
}
}
ast::item_struct(ref def, _) => check_struct(def),
ast::item_trait(_, _, ref methods) => {
for m in methods.iter() {
match *m {
ast::provided(ref m) => {
check_inherited(m.span, m.vis,
"unnecessary visibility");
}
ast::required(*) => {}
}
}
}
ast::item_static(*) |
ast::item_fn(*) | ast::item_mod(*) | ast::item_ty(*) |
ast::item_mac(*) => {
check_not_priv(item.span, item.vis, "items are private by \
default");
}
}
}
}
impl<'self> Visitor<()> for PrivacyVisitor<'self> {
fn visit_item(&mut self, item: @ast::item, _: ()) {
// Do not check privacy inside items with the resolve_unexported
// attribute. This is used for the test runner.
if attr::contains_name(item.attrs, "!resolve_unexported") {
return;
}
// Disallow unnecessary visibility qualifiers
self.check_sane_privacy(item);
let orig_curitem = self.curitem;
self.curitem = item.id;
visit::walk_item(self, item, ());
self.curitem = orig_curitem;
}
fn visit_expr(&mut self, expr: @ast::Expr, _: ()) {
match expr.node {
ast::ExprField(base, ident, _) => {
// Method calls are now a special syntactic form,
// so `a.b` should always be a field.
assert!(!self.method_map.contains_key(&expr.id));
// With type_autoderef, make sure we don't
// allow pointers to violate privacy
let t = ty::type_autoderef(self.tcx,
ty::expr_ty(self.tcx, base));
match ty::get(t).sty {
ty::ty_struct(id, _) => self.check_field(expr.span, id, ident),
_ => {}
}
}
ast::ExprMethodCall(_, base, ident, _, _, _) => {
// see above
let t = ty::type_autoderef(self.tcx,
ty::expr_ty(self.tcx, base));
match ty::get(t).sty {
ty::ty_enum(_, _) | ty::ty_struct(_, _) => {
let entry = match self.method_map.find(&expr.id) {
None => {
self.tcx.sess.span_bug(expr.span,
"method call not in \
method map");
}
Some(entry) => entry
};
debug!("(privacy checking) checking impl method");
self.check_method(expr.span, &entry.origin, ident);
}
_ => {}
}
}
ast::ExprPath(ref path) => {
self.check_path(expr.span, expr.id, path);
}
ast::ExprStruct(_, ref fields, _) => {
match ty::get(ty::expr_ty(self.tcx, expr)).sty {
ty::ty_struct(id, _) => {
for field in (*fields).iter() {
self.check_field(expr.span, id, field.ident.node);
}
}
ty::ty_enum(_, _) => {
match self.tcx.def_map.get_copy(&expr.id) {
ast::DefVariant(_, variant_id, _) => {
for field in fields.iter() {
self.check_field(expr.span, variant_id,
field.ident.node);
}
}
_ => self.tcx.sess.span_bug(expr.span,
"resolve didn't \
map enum struct \
constructor to a \
variant def"),
}
}
_ => self.tcx.sess.span_bug(expr.span, "struct expr \
didn't have \
struct type?!"),
}
}
ast::ExprUnary(_, ast::UnDeref, operand) => {
// In *e, we need to check that if e's type is an
// enum type t, then t's first variant is public or
// privileged. (We can assume it has only one variant
// since typeck already happened.)
match ty::get(ty::expr_ty(self.tcx, operand)).sty {
ty::ty_enum(id, _) => {
self.check_variant(expr.span, id);
}
_ => { /* No check needed */ }
}
}
_ => {}
}
visit::walk_expr(self, expr, ());
}
fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
match t.node {
ast::ty_path(ref path, _, id) => self.check_path(t.span, id, path),
_ => {}
}
visit::walk_ty(self, t, ());
}
fn visit_view_item(&mut self, a: &ast::view_item, _: ()) {
match a.node {
ast::view_item_extern_mod(*) => {}
ast::view_item_use(ref uses) => {
for vpath in uses.iter() {
match vpath.node {
ast::view_path_simple(_, ref path, id) |
ast::view_path_glob(ref path, id) => {
debug!("privacy - glob/simple {}", id);
self.check_path(vpath.span, id, path);
}
ast::view_path_list(_, ref list, _) => {
for pid in list.iter() {
debug!("privacy - list {}", pid.node.id);
let seg = ast::PathSegment {
identifier: pid.node.name,
lifetime: None,
types: opt_vec::Empty,
};
let segs = ~[seg];
let path = ast::Path {
global: false,
span: pid.span,
segments: segs,
};
self.check_path(pid.span, pid.node.id, &path);
}
}
}
}
}
}
}
fn visit_pat(&mut self, pattern: @ast::Pat, _: ()) {
match pattern.node {
ast::PatStruct(_, ref fields, _) => {
match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
ty::ty_struct(id, _) => {
for field in fields.iter() {
self.check_field(pattern.span, id, field.ident);
}
}
ty::ty_enum(_, _) => {
match self.tcx.def_map.find(&pattern.id) {
Some(&ast::DefVariant(_, variant_id, _)) => {
for field in fields.iter() {
self.check_field(pattern.span, variant_id,
field.ident);
}
}
_ => self.tcx.sess.span_bug(pattern.span,
"resolve didn't \
map enum struct \
pattern to a \
variant def"),
}
}
_ => self.tcx.sess.span_bug(pattern.span,
"struct pattern didn't have \
struct type?!"),
}
}
_ => {}
}
visit::walk_pat(self, pattern, ());
}
}
pub fn check_crate(tcx: ty::ctxt,
method_map: &method_map,
exp_map2: &resolve::ExportMap2,
external_exports: resolve::ExternalExports,
last_private_map: resolve::LastPrivateMap,
crate: &ast::Crate) -> ExportedItems {
let mut parents = HashMap::new();
let mut exported_items = HashSet::new();
// First, figure out who everyone's parent is
{
let mut visitor = ParentVisitor {
parents: &mut parents,
curparent: ast::DUMMY_NODE_ID,
};
visit::walk_crate(&mut visitor, crate, ());
}
// Next, build up the list of all exported items from this crate
{
// Initialize the exported items with resolve's id for the "root crate"
// to resolve references to `super` leading to the root and such.
exported_items.insert(ast::CRATE_NODE_ID);
let mut visitor = EmbargoVisitor {
exported_items: &mut exported_items,
exp_map2: exp_map2,
path_all_public: true, // start out as public
};
visit::walk_crate(&mut visitor, crate, ());
}
// And then actually check the privacy of everything.
{
let mut visitor = PrivacyVisitor {
curitem: ast::DUMMY_NODE_ID,
tcx: tcx,
exported_items: &exported_items,
parents: &parents,
method_map: method_map,
external_exports: external_exports,
last_private_map: last_private_map,
};
visit::walk_crate(&mut visitor, crate, ());
}
return exported_items;
}