|
18 | 18 |
|
19 | 19 | #![feature(rustc_diagnostic_macros)]
|
20 | 20 |
|
21 |
| -extern crate rustc; |
| 21 | +#[macro_use] extern crate rustc; |
22 | 22 | #[macro_use] extern crate syntax;
|
23 | 23 | extern crate syntax_pos;
|
24 | 24 |
|
25 | 25 | use rustc::hir::{self, PatKind};
|
26 | 26 | use rustc::hir::def::Def;
|
27 |
| -use rustc::hir::def_id::{LOCAL_CRATE, CrateNum, DefId}; |
| 27 | +use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId}; |
28 | 28 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
|
29 | 29 | use rustc::hir::itemlikevisit::DeepVisitor;
|
30 | 30 | use rustc::lint;
|
@@ -537,6 +537,324 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
|
537 | 537 | }
|
538 | 538 | }
|
539 | 539 |
|
| 540 | +//////////////////////////////////////////////////////////////////////////////////////////// |
| 541 | +/// Type privacy visitor, checks types for privacy and reports violations. |
| 542 | +/// Both explicitly written types and inferred types of expressions and patters are checked. |
| 543 | +/// Checks are performed on "semantic" types regardless of names and their hygiene. |
| 544 | +//////////////////////////////////////////////////////////////////////////////////////////// |
| 545 | + |
| 546 | +struct TypePrivacyVisitor<'a, 'tcx: 'a> { |
| 547 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 548 | + tables: &'a ty::TypeckTables<'tcx>, |
| 549 | + current_item: DefId, |
| 550 | + span: Span, |
| 551 | +} |
| 552 | + |
| 553 | +impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { |
| 554 | + fn def_id_visibility(&self, did: DefId) -> ty::Visibility { |
| 555 | + match self.tcx.hir.as_local_node_id(did) { |
| 556 | + Some(node_id) => { |
| 557 | + let vis = match self.tcx.hir.get(node_id) { |
| 558 | + hir::map::NodeItem(item) => &item.vis, |
| 559 | + hir::map::NodeForeignItem(foreign_item) => &foreign_item.vis, |
| 560 | + hir::map::NodeImplItem(impl_item) => &impl_item.vis, |
| 561 | + hir::map::NodeTraitItem(..) | |
| 562 | + hir::map::NodeVariant(..) => { |
| 563 | + return self.def_id_visibility(self.tcx.hir.get_parent_did(node_id)); |
| 564 | + } |
| 565 | + hir::map::NodeStructCtor(vdata) => { |
| 566 | + let struct_node_id = self.tcx.hir.get_parent(node_id); |
| 567 | + let struct_vis = match self.tcx.hir.get(struct_node_id) { |
| 568 | + hir::map::NodeItem(item) => &item.vis, |
| 569 | + node => bug!("unexpected node kind: {:?}", node), |
| 570 | + }; |
| 571 | + let mut ctor_vis |
| 572 | + = ty::Visibility::from_hir(struct_vis, struct_node_id, self.tcx); |
| 573 | + for field in vdata.fields() { |
| 574 | + let field_vis = ty::Visibility::from_hir(&field.vis, node_id, self.tcx); |
| 575 | + if ctor_vis.is_at_least(field_vis, self.tcx) { |
| 576 | + ctor_vis = field_vis; |
| 577 | + } |
| 578 | + } |
| 579 | + return ctor_vis; |
| 580 | + } |
| 581 | + node => bug!("unexpected node kind: {:?}", node) |
| 582 | + }; |
| 583 | + ty::Visibility::from_hir(vis, node_id, self.tcx) |
| 584 | + } |
| 585 | + None => self.tcx.sess.cstore.visibility(did), |
| 586 | + } |
| 587 | + } |
| 588 | + |
| 589 | + fn item_is_accessible(&self, did: DefId) -> bool { |
| 590 | + self.def_id_visibility(did).is_accessible_from(self.current_item, self.tcx) |
| 591 | + } |
| 592 | + |
| 593 | + fn check_expr_pat_type(&mut self, id: ast::NodeId, span: Span) -> bool { |
| 594 | + if let Some(ty) = self.tables.node_id_to_type_opt(id) { |
| 595 | + self.span = span; |
| 596 | + ty.visit_with(self) |
| 597 | + } else { |
| 598 | + false |
| 599 | + } |
| 600 | + } |
| 601 | + |
| 602 | + fn check_item(&mut self, item_id: ast::NodeId) -> &mut Self { |
| 603 | + self.current_item = self.tcx.hir.local_def_id(item_id); |
| 604 | + self.span = self.tcx.hir.span(item_id); |
| 605 | + self |
| 606 | + } |
| 607 | + |
| 608 | + // Convenience methods for checking item interfaces |
| 609 | + fn ty(&mut self) -> &mut Self { |
| 610 | + self.tcx.type_of(self.current_item).visit_with(self); |
| 611 | + self |
| 612 | + } |
| 613 | + |
| 614 | + fn generics(&mut self) -> &mut Self { |
| 615 | + for def in &self.tcx.generics_of(self.current_item).types { |
| 616 | + if def.has_default { |
| 617 | + self.tcx.type_of(def.def_id).visit_with(self); |
| 618 | + } |
| 619 | + } |
| 620 | + self |
| 621 | + } |
| 622 | + |
| 623 | + fn predicates(&mut self) -> &mut Self { |
| 624 | + self.tcx.predicates_of(self.current_item).visit_with(self); |
| 625 | + self |
| 626 | + } |
| 627 | + |
| 628 | + fn impl_trait_ref(&mut self) -> &mut Self { |
| 629 | + self.tcx.impl_trait_ref(self.current_item).visit_with(self); |
| 630 | + self |
| 631 | + } |
| 632 | +} |
| 633 | + |
| 634 | +impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { |
| 635 | + /// We want to visit items in the context of their containing |
| 636 | + /// module and so forth, so supply a crate for doing a deep walk. |
| 637 | + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
| 638 | + NestedVisitorMap::All(&self.tcx.hir) |
| 639 | + } |
| 640 | + |
| 641 | + fn visit_nested_body(&mut self, body: hir::BodyId) { |
| 642 | + let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body)); |
| 643 | + let body = self.tcx.hir.body(body); |
| 644 | + self.visit_body(body); |
| 645 | + self.tables = orig_tables; |
| 646 | + } |
| 647 | + |
| 648 | + // Check types of expressions |
| 649 | + fn visit_expr(&mut self, expr: &'tcx hir::Expr) { |
| 650 | + if self.check_expr_pat_type(expr.id, expr.span) { |
| 651 | + // Do not check nested expressions if the error already happened. |
| 652 | + return; |
| 653 | + } |
| 654 | + match expr.node { |
| 655 | + hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => { |
| 656 | + // Do not report duplicate errors for `x = y` and `match x { ... }`. |
| 657 | + if self.check_expr_pat_type(rhs.id, rhs.span) { |
| 658 | + return; |
| 659 | + } |
| 660 | + } |
| 661 | + hir::ExprMethodCall(name, ..) => { |
| 662 | + // Method calls have to be checked specially. |
| 663 | + if let Some(method) = self.tables.method_map.get(&ty::MethodCall::expr(expr.id)) { |
| 664 | + self.span = name.span; |
| 665 | + if method.ty.visit_with(self) || method.substs.visit_with(self) { |
| 666 | + return; |
| 667 | + } |
| 668 | + } |
| 669 | + } |
| 670 | + _ => {} |
| 671 | + } |
| 672 | + |
| 673 | + intravisit::walk_expr(self, expr); |
| 674 | + } |
| 675 | + |
| 676 | + // Check types of patterns |
| 677 | + fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { |
| 678 | + if self.check_expr_pat_type(pattern.id, pattern.span) { |
| 679 | + // Do not check nested patterns if the error already happened. |
| 680 | + return; |
| 681 | + } |
| 682 | + |
| 683 | + intravisit::walk_pat(self, pattern); |
| 684 | + } |
| 685 | + |
| 686 | + fn visit_local(&mut self, local: &'tcx hir::Local) { |
| 687 | + if let Some(ref init) = local.init { |
| 688 | + if self.check_expr_pat_type(init.id, init.span) { |
| 689 | + // Do not report duplicate errors for `let x = y`. |
| 690 | + return; |
| 691 | + } |
| 692 | + } |
| 693 | + |
| 694 | + intravisit::walk_local(self, local); |
| 695 | + } |
| 696 | + |
| 697 | + // Check types in item interfaces |
| 698 | + fn visit_item(&mut self, item: &'tcx hir::Item) { |
| 699 | + let orig_current_item = self.current_item; |
| 700 | + |
| 701 | + match item.node { |
| 702 | + hir::ItemExternCrate(..) | hir::ItemMod(..) | |
| 703 | + hir::ItemUse(..) | hir::ItemGlobalAsm(..) => {} |
| 704 | + hir::ItemConst(..) | hir::ItemStatic(..) | |
| 705 | + hir::ItemTy(..) | hir::ItemFn(..) => { |
| 706 | + self.check_item(item.id).generics().predicates().ty(); |
| 707 | + } |
| 708 | + hir::ItemTrait(.., ref trait_item_refs) => { |
| 709 | + self.check_item(item.id).generics().predicates(); |
| 710 | + for trait_item_ref in trait_item_refs { |
| 711 | + let mut check = self.check_item(trait_item_ref.id.node_id); |
| 712 | + check.generics().predicates(); |
| 713 | + if trait_item_ref.kind != hir::AssociatedItemKind::Type || |
| 714 | + trait_item_ref.defaultness.has_value() { |
| 715 | + check.ty(); |
| 716 | + } |
| 717 | + } |
| 718 | + } |
| 719 | + hir::ItemEnum(ref def, _) => { |
| 720 | + self.check_item(item.id).generics().predicates(); |
| 721 | + for variant in &def.variants { |
| 722 | + for field in variant.node.data.fields() { |
| 723 | + self.check_item(field.id).ty(); |
| 724 | + } |
| 725 | + } |
| 726 | + } |
| 727 | + hir::ItemForeignMod(ref foreign_mod) => { |
| 728 | + for foreign_item in &foreign_mod.items { |
| 729 | + self.check_item(foreign_item.id).generics().predicates().ty(); |
| 730 | + } |
| 731 | + } |
| 732 | + hir::ItemStruct(ref struct_def, _) | |
| 733 | + hir::ItemUnion(ref struct_def, _) => { |
| 734 | + self.check_item(item.id).generics().predicates(); |
| 735 | + for field in struct_def.fields() { |
| 736 | + self.check_item(field.id).ty(); |
| 737 | + } |
| 738 | + } |
| 739 | + hir::ItemDefaultImpl(..) => { |
| 740 | + self.check_item(item.id).impl_trait_ref(); |
| 741 | + } |
| 742 | + hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => { |
| 743 | + { |
| 744 | + let mut check = self.check_item(item.id); |
| 745 | + check.ty().generics().predicates(); |
| 746 | + if trait_ref.is_some() { |
| 747 | + check.impl_trait_ref(); |
| 748 | + } |
| 749 | + } |
| 750 | + for impl_item_ref in impl_item_refs { |
| 751 | + let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
| 752 | + self.check_item(impl_item.id).generics().predicates().ty(); |
| 753 | + } |
| 754 | + } |
| 755 | + } |
| 756 | + |
| 757 | + self.current_item = self.tcx.hir.local_def_id(item.id); |
| 758 | + intravisit::walk_item(self, item); |
| 759 | + self.current_item = orig_current_item; |
| 760 | + } |
| 761 | +} |
| 762 | + |
| 763 | +impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { |
| 764 | + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { |
| 765 | + match ty.sty { |
| 766 | + ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => { |
| 767 | + if !self.item_is_accessible(def_id) { |
| 768 | + let msg = format!("type `{}` is private", ty); |
| 769 | + self.tcx.sess.span_err(self.span, &msg); |
| 770 | + return true; |
| 771 | + } |
| 772 | + if let ty::TyFnDef(..) = ty.sty { |
| 773 | + // Inherent static methods don't have self type in substs, |
| 774 | + // we have to check it additionally. |
| 775 | + let mut impl_def_id = None; |
| 776 | + if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { |
| 777 | + if let hir::map::NodeImplItem(..) = self.tcx.hir.get(node_id) { |
| 778 | + impl_def_id = Some(self.tcx.hir.get_parent_did(node_id)); |
| 779 | + } |
| 780 | + } else if let Some(Def::Method(..)) = self.tcx.describe_def(def_id) { |
| 781 | + let candidate_impl_def_id = self.tcx.parent_def_id(def_id) |
| 782 | + .expect("no parent for method def_id"); |
| 783 | + // `is_none` means it's an impl, not a trait |
| 784 | + if self.tcx.describe_def(candidate_impl_def_id).is_none() { |
| 785 | + impl_def_id = Some(candidate_impl_def_id) |
| 786 | + } |
| 787 | + } |
| 788 | + if let Some(impl_def_id) = impl_def_id { |
| 789 | + let self_ty = self.tcx.type_of(impl_def_id); |
| 790 | + if self_ty.visit_with(self) { |
| 791 | + return true; |
| 792 | + } |
| 793 | + } |
| 794 | + } |
| 795 | + } |
| 796 | + ty::TyDynamic(ref predicates, ..) => { |
| 797 | + let is_private = predicates.skip_binder().iter().any(|predicate| { |
| 798 | + let def_id = match *predicate { |
| 799 | + ty::ExistentialPredicate::Trait(trait_ref) => trait_ref.def_id, |
| 800 | + ty::ExistentialPredicate::Projection(proj) => proj.trait_ref.def_id, |
| 801 | + ty::ExistentialPredicate::AutoTrait(def_id) => def_id, |
| 802 | + }; |
| 803 | + !self.item_is_accessible(def_id) |
| 804 | + }); |
| 805 | + if is_private { |
| 806 | + let msg = format!("type `{}` is private", ty); |
| 807 | + self.tcx.sess.span_err(self.span, &msg); |
| 808 | + return true; |
| 809 | + } |
| 810 | + } |
| 811 | + ty::TyAnon(def_id, ..) => { |
| 812 | + for predicate in &self.tcx.predicates_of(def_id).predicates { |
| 813 | + let trait_ref = match *predicate { |
| 814 | + ty::Predicate::Trait(ref poly_trait_predicate) => { |
| 815 | + Some(poly_trait_predicate.skip_binder().trait_ref) |
| 816 | + } |
| 817 | + ty::Predicate::Projection(ref poly_projection_predicate) => { |
| 818 | + if poly_projection_predicate.skip_binder().ty.visit_with(self) { |
| 819 | + return true; |
| 820 | + } |
| 821 | + Some(poly_projection_predicate.skip_binder().projection_ty.trait_ref) |
| 822 | + } |
| 823 | + ty::Predicate::TypeOutlives(..) => None, |
| 824 | + _ => bug!("unexpected predicate: {:?}", predicate), |
| 825 | + }; |
| 826 | + if let Some(trait_ref) = trait_ref { |
| 827 | + if !self.item_is_accessible(trait_ref.def_id) { |
| 828 | + let msg = format!("trait `{}` is private", trait_ref); |
| 829 | + self.tcx.sess.span_err(self.span, &msg); |
| 830 | + return true; |
| 831 | + } |
| 832 | + // Skip `Self` to avoid infinite recursion |
| 833 | + for subst in trait_ref.substs.iter().skip(1) { |
| 834 | + if subst.visit_with(self) { |
| 835 | + return true; |
| 836 | + } |
| 837 | + } |
| 838 | + } |
| 839 | + } |
| 840 | + } |
| 841 | + _ => {} |
| 842 | + } |
| 843 | + |
| 844 | + ty.super_visit_with(self) |
| 845 | + } |
| 846 | + |
| 847 | + fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool { |
| 848 | + if !self.item_is_accessible(trait_ref.def_id) { |
| 849 | + let msg = format!("trait `{}` is private", trait_ref); |
| 850 | + self.tcx.sess.span_err(self.span, &msg); |
| 851 | + return true; |
| 852 | + } |
| 853 | + |
| 854 | + trait_ref.super_visit_with(self) |
| 855 | + } |
| 856 | +} |
| 857 | + |
540 | 858 | ///////////////////////////////////////////////////////////////////////////////
|
541 | 859 | /// Obsolete visitors for checking for private items in public interfaces.
|
542 | 860 | /// These visitors are supposed to be kept in frozen state and produce an
|
@@ -1225,6 +1543,16 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
1225 | 1543 | };
|
1226 | 1544 | intravisit::walk_crate(&mut visitor, krate);
|
1227 | 1545 |
|
| 1546 | + // Check privacy of explicitly written types and traits as well as |
| 1547 | + // inferred types of expressions and patterns. |
| 1548 | + let mut visitor = TypePrivacyVisitor { |
| 1549 | + tcx: tcx, |
| 1550 | + tables: &ty::TypeckTables::empty(), |
| 1551 | + current_item: DefId::local(CRATE_DEF_INDEX), |
| 1552 | + span: krate.span, |
| 1553 | + }; |
| 1554 | + intravisit::walk_crate(&mut visitor, krate); |
| 1555 | + |
1228 | 1556 | // Build up a set of all exported items in the AST. This is a set of all
|
1229 | 1557 | // items which are reachable from external crates based on visibility.
|
1230 | 1558 | let mut visitor = EmbargoVisitor {
|
|
0 commit comments