|
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,344 @@ 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 | + // Take node ID of an expression or pattern and check its type for privacy. |
| 594 | + fn check_expr_pat_type(&mut self, id: ast::NodeId, span: Span) -> bool { |
| 595 | + self.span = span; |
| 596 | + if let Some(ty) = self.tables.node_id_to_type_opt(id) { |
| 597 | + if ty.visit_with(self) { |
| 598 | + return true; |
| 599 | + } |
| 600 | + } |
| 601 | + if self.tables.node_substs(id).visit_with(self) { |
| 602 | + return true; |
| 603 | + } |
| 604 | + if let Some(adjustments) = self.tables.adjustments.get(&id) { |
| 605 | + for adjustment in adjustments { |
| 606 | + if adjustment.target.visit_with(self) { |
| 607 | + return true; |
| 608 | + } |
| 609 | + } |
| 610 | + } |
| 611 | + false |
| 612 | + } |
| 613 | + |
| 614 | + fn check_item(&mut self, item_id: ast::NodeId) -> &mut Self { |
| 615 | + self.current_item = self.tcx.hir.local_def_id(item_id); |
| 616 | + self.span = self.tcx.hir.span(item_id); |
| 617 | + self |
| 618 | + } |
| 619 | + |
| 620 | + // Convenience methods for checking item interfaces |
| 621 | + fn ty(&mut self) -> &mut Self { |
| 622 | + self.tcx.type_of(self.current_item).visit_with(self); |
| 623 | + self |
| 624 | + } |
| 625 | + |
| 626 | + fn generics(&mut self) -> &mut Self { |
| 627 | + for def in &self.tcx.generics_of(self.current_item).types { |
| 628 | + if def.has_default { |
| 629 | + self.tcx.type_of(def.def_id).visit_with(self); |
| 630 | + } |
| 631 | + } |
| 632 | + self |
| 633 | + } |
| 634 | + |
| 635 | + fn predicates(&mut self) -> &mut Self { |
| 636 | + self.tcx.predicates_of(self.current_item).visit_with(self); |
| 637 | + self |
| 638 | + } |
| 639 | + |
| 640 | + fn impl_trait_ref(&mut self) -> &mut Self { |
| 641 | + self.tcx.impl_trait_ref(self.current_item).visit_with(self); |
| 642 | + self |
| 643 | + } |
| 644 | +} |
| 645 | + |
| 646 | +impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { |
| 647 | + /// We want to visit items in the context of their containing |
| 648 | + /// module and so forth, so supply a crate for doing a deep walk. |
| 649 | + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
| 650 | + NestedVisitorMap::All(&self.tcx.hir) |
| 651 | + } |
| 652 | + |
| 653 | + fn visit_nested_body(&mut self, body: hir::BodyId) { |
| 654 | + let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body)); |
| 655 | + let body = self.tcx.hir.body(body); |
| 656 | + self.visit_body(body); |
| 657 | + self.tables = orig_tables; |
| 658 | + } |
| 659 | + |
| 660 | + // Check types of expressions |
| 661 | + fn visit_expr(&mut self, expr: &'tcx hir::Expr) { |
| 662 | + if self.check_expr_pat_type(expr.id, expr.span) { |
| 663 | + // Do not check nested expressions if the error already happened. |
| 664 | + return; |
| 665 | + } |
| 666 | + match expr.node { |
| 667 | + hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => { |
| 668 | + // Do not report duplicate errors for `x = y` and `match x { ... }`. |
| 669 | + if self.check_expr_pat_type(rhs.id, rhs.span) { |
| 670 | + return; |
| 671 | + } |
| 672 | + } |
| 673 | + hir::ExprMethodCall(name, ..) => { |
| 674 | + // Method calls have to be checked specially. |
| 675 | + let def_id = self.tables.type_dependent_defs[&expr.id].def_id(); |
| 676 | + self.span = name.span; |
| 677 | + if self.tcx.type_of(def_id).visit_with(self) { |
| 678 | + return; |
| 679 | + } |
| 680 | + } |
| 681 | + _ => {} |
| 682 | + } |
| 683 | + |
| 684 | + intravisit::walk_expr(self, expr); |
| 685 | + } |
| 686 | + |
| 687 | + fn visit_qpath(&mut self, qpath: &'tcx hir::QPath, id: ast::NodeId, span: Span) { |
| 688 | + // Inherent associated constants don't have self type in substs, |
| 689 | + // we have to check it additionally. |
| 690 | + if let hir::QPath::TypeRelative(..) = *qpath { |
| 691 | + if let Some(def) = self.tables.type_dependent_defs.get(&id).cloned() { |
| 692 | + if let Some(assoc_item) = self.tcx.opt_associated_item(def.def_id()) { |
| 693 | + if let ty::ImplContainer(impl_def_id) = assoc_item.container { |
| 694 | + if self.tcx.type_of(impl_def_id).visit_with(self) { |
| 695 | + return; |
| 696 | + } |
| 697 | + } |
| 698 | + } |
| 699 | + } |
| 700 | + } |
| 701 | + |
| 702 | + intravisit::walk_qpath(self, qpath, id, span); |
| 703 | + } |
| 704 | + |
| 705 | + // Check types of patterns |
| 706 | + fn visit_pat(&mut self, pattern: &'tcx hir::Pat) { |
| 707 | + if self.check_expr_pat_type(pattern.id, pattern.span) { |
| 708 | + // Do not check nested patterns if the error already happened. |
| 709 | + return; |
| 710 | + } |
| 711 | + |
| 712 | + intravisit::walk_pat(self, pattern); |
| 713 | + } |
| 714 | + |
| 715 | + fn visit_local(&mut self, local: &'tcx hir::Local) { |
| 716 | + if let Some(ref init) = local.init { |
| 717 | + if self.check_expr_pat_type(init.id, init.span) { |
| 718 | + // Do not report duplicate errors for `let x = y`. |
| 719 | + return; |
| 720 | + } |
| 721 | + } |
| 722 | + |
| 723 | + intravisit::walk_local(self, local); |
| 724 | + } |
| 725 | + |
| 726 | + // Check types in item interfaces |
| 727 | + fn visit_item(&mut self, item: &'tcx hir::Item) { |
| 728 | + let orig_current_item = self.current_item; |
| 729 | + |
| 730 | + match item.node { |
| 731 | + hir::ItemExternCrate(..) | hir::ItemMod(..) | |
| 732 | + hir::ItemUse(..) | hir::ItemGlobalAsm(..) => {} |
| 733 | + hir::ItemConst(..) | hir::ItemStatic(..) | |
| 734 | + hir::ItemTy(..) | hir::ItemFn(..) => { |
| 735 | + self.check_item(item.id).generics().predicates().ty(); |
| 736 | + } |
| 737 | + hir::ItemTrait(.., ref trait_item_refs) => { |
| 738 | + self.check_item(item.id).generics().predicates(); |
| 739 | + for trait_item_ref in trait_item_refs { |
| 740 | + let mut check = self.check_item(trait_item_ref.id.node_id); |
| 741 | + check.generics().predicates(); |
| 742 | + if trait_item_ref.kind != hir::AssociatedItemKind::Type || |
| 743 | + trait_item_ref.defaultness.has_value() { |
| 744 | + check.ty(); |
| 745 | + } |
| 746 | + } |
| 747 | + } |
| 748 | + hir::ItemEnum(ref def, _) => { |
| 749 | + self.check_item(item.id).generics().predicates(); |
| 750 | + for variant in &def.variants { |
| 751 | + for field in variant.node.data.fields() { |
| 752 | + self.check_item(field.id).ty(); |
| 753 | + } |
| 754 | + } |
| 755 | + } |
| 756 | + hir::ItemForeignMod(ref foreign_mod) => { |
| 757 | + for foreign_item in &foreign_mod.items { |
| 758 | + self.check_item(foreign_item.id).generics().predicates().ty(); |
| 759 | + } |
| 760 | + } |
| 761 | + hir::ItemStruct(ref struct_def, _) | |
| 762 | + hir::ItemUnion(ref struct_def, _) => { |
| 763 | + self.check_item(item.id).generics().predicates(); |
| 764 | + for field in struct_def.fields() { |
| 765 | + self.check_item(field.id).ty(); |
| 766 | + } |
| 767 | + } |
| 768 | + hir::ItemDefaultImpl(..) => { |
| 769 | + self.check_item(item.id).impl_trait_ref(); |
| 770 | + } |
| 771 | + hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => { |
| 772 | + { |
| 773 | + let mut check = self.check_item(item.id); |
| 774 | + check.ty().generics().predicates(); |
| 775 | + if trait_ref.is_some() { |
| 776 | + check.impl_trait_ref(); |
| 777 | + } |
| 778 | + } |
| 779 | + for impl_item_ref in impl_item_refs { |
| 780 | + let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
| 781 | + self.check_item(impl_item.id).generics().predicates().ty(); |
| 782 | + } |
| 783 | + } |
| 784 | + } |
| 785 | + |
| 786 | + self.current_item = self.tcx.hir.local_def_id(item.id); |
| 787 | + intravisit::walk_item(self, item); |
| 788 | + self.current_item = orig_current_item; |
| 789 | + } |
| 790 | +} |
| 791 | + |
| 792 | +impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { |
| 793 | + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { |
| 794 | + match ty.sty { |
| 795 | + ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => { |
| 796 | + if !self.item_is_accessible(def_id) { |
| 797 | + let msg = format!("type `{}` is private", ty); |
| 798 | + self.tcx.sess.span_err(self.span, &msg); |
| 799 | + return true; |
| 800 | + } |
| 801 | + if let ty::TyFnDef(..) = ty.sty { |
| 802 | + if self.tcx.fn_sig(def_id).visit_with(self) { |
| 803 | + return true; |
| 804 | + } |
| 805 | + } |
| 806 | + // Inherent static methods don't have self type in substs, |
| 807 | + // we have to check it additionally. |
| 808 | + if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) { |
| 809 | + if let ty::ImplContainer(impl_def_id) = assoc_item.container { |
| 810 | + if self.tcx.type_of(impl_def_id).visit_with(self) { |
| 811 | + return true; |
| 812 | + } |
| 813 | + } |
| 814 | + } |
| 815 | + } |
| 816 | + ty::TyDynamic(ref predicates, ..) => { |
| 817 | + let is_private = predicates.skip_binder().iter().any(|predicate| { |
| 818 | + let def_id = match *predicate { |
| 819 | + ty::ExistentialPredicate::Trait(trait_ref) => trait_ref.def_id, |
| 820 | + ty::ExistentialPredicate::Projection(proj) => proj.trait_ref.def_id, |
| 821 | + ty::ExistentialPredicate::AutoTrait(def_id) => def_id, |
| 822 | + }; |
| 823 | + !self.item_is_accessible(def_id) |
| 824 | + }); |
| 825 | + if is_private { |
| 826 | + let msg = format!("type `{}` is private", ty); |
| 827 | + self.tcx.sess.span_err(self.span, &msg); |
| 828 | + return true; |
| 829 | + } |
| 830 | + } |
| 831 | + ty::TyAnon(def_id, ..) => { |
| 832 | + for predicate in &self.tcx.predicates_of(def_id).predicates { |
| 833 | + let trait_ref = match *predicate { |
| 834 | + ty::Predicate::Trait(ref poly_trait_predicate) => { |
| 835 | + Some(poly_trait_predicate.skip_binder().trait_ref) |
| 836 | + } |
| 837 | + ty::Predicate::Projection(ref poly_projection_predicate) => { |
| 838 | + if poly_projection_predicate.skip_binder().ty.visit_with(self) { |
| 839 | + return true; |
| 840 | + } |
| 841 | + Some(poly_projection_predicate.skip_binder().projection_ty.trait_ref) |
| 842 | + } |
| 843 | + ty::Predicate::TypeOutlives(..) => None, |
| 844 | + _ => bug!("unexpected predicate: {:?}", predicate), |
| 845 | + }; |
| 846 | + if let Some(trait_ref) = trait_ref { |
| 847 | + if !self.item_is_accessible(trait_ref.def_id) { |
| 848 | + let msg = format!("trait `{}` is private", trait_ref); |
| 849 | + self.tcx.sess.span_err(self.span, &msg); |
| 850 | + return true; |
| 851 | + } |
| 852 | + // `Self` here is the same `TyAnon`, so skip it to avoid infinite recursion |
| 853 | + for subst in trait_ref.substs.iter().skip(1) { |
| 854 | + if subst.visit_with(self) { |
| 855 | + return true; |
| 856 | + } |
| 857 | + } |
| 858 | + } |
| 859 | + } |
| 860 | + } |
| 861 | + _ => {} |
| 862 | + } |
| 863 | + |
| 864 | + ty.super_visit_with(self) |
| 865 | + } |
| 866 | + |
| 867 | + fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool { |
| 868 | + if !self.item_is_accessible(trait_ref.def_id) { |
| 869 | + let msg = format!("trait `{}` is private", trait_ref); |
| 870 | + self.tcx.sess.span_err(self.span, &msg); |
| 871 | + return true; |
| 872 | + } |
| 873 | + |
| 874 | + trait_ref.super_visit_with(self) |
| 875 | + } |
| 876 | +} |
| 877 | + |
540 | 878 | ///////////////////////////////////////////////////////////////////////////////
|
541 | 879 | /// Obsolete visitors for checking for private items in public interfaces.
|
542 | 880 | /// These visitors are supposed to be kept in frozen state and produce an
|
@@ -1225,6 +1563,16 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
1225 | 1563 | };
|
1226 | 1564 | intravisit::walk_crate(&mut visitor, krate);
|
1227 | 1565 |
|
| 1566 | + // Check privacy of explicitly written types and traits as well as |
| 1567 | + // inferred types of expressions and patterns. |
| 1568 | + let mut visitor = TypePrivacyVisitor { |
| 1569 | + tcx: tcx, |
| 1570 | + tables: &ty::TypeckTables::empty(), |
| 1571 | + current_item: DefId::local(CRATE_DEF_INDEX), |
| 1572 | + span: krate.span, |
| 1573 | + }; |
| 1574 | + intravisit::walk_crate(&mut visitor, krate); |
| 1575 | + |
1228 | 1576 | // Build up a set of all exported items in the AST. This is a set of all
|
1229 | 1577 | // items which are reachable from external crates based on visibility.
|
1230 | 1578 | let mut visitor = EmbargoVisitor {
|
|
0 commit comments