@@ -229,6 +229,13 @@ fn def_id_visibility<'tcx>(
229229 let vis = match tcx. hir ( ) . get ( hir_id) {
230230 Node :: Item ( item) => & item. vis ,
231231 Node :: ForeignItem ( foreign_item) => & foreign_item. vis ,
232+ Node :: MacroDef ( macro_def) => {
233+ if attr:: contains_name ( & macro_def. attrs , sym:: macro_export) {
234+ return ( ty:: Visibility :: Public , macro_def. span , "public" ) ;
235+ } else {
236+ & macro_def. vis
237+ }
238+ } ,
232239 Node :: TraitItem ( ..) | Node :: Variant ( ..) => {
233240 return def_id_visibility ( tcx, tcx. hir ( ) . get_parent_did ( hir_id) ) ;
234241 }
@@ -433,11 +440,24 @@ impl VisibilityLike for Option<AccessLevel> {
433440struct EmbargoVisitor < ' tcx > {
434441 tcx : TyCtxt < ' tcx > ,
435442
436- // Accessibility levels for reachable nodes.
443+ /// Accessibility levels for reachable nodes.
437444 access_levels : AccessLevels ,
438- // Previous accessibility level; `None` means unreachable.
445+ /// A set of pairs corresponding to modules, where the first module is
446+ /// reachable via a macro that's defined in the second module. This cannot
447+ /// be represented as reachable because it can't handle the following case:
448+ ///
449+ /// pub mod n { // Should be `Public`
450+ /// pub(crate) mod p { // Should *not* be accessible
451+ /// pub fn f() -> i32 { 12 } // Must be `Reachable`
452+ /// }
453+ /// }
454+ /// pub macro m() {
455+ /// n::p::f()
456+ /// }
457+ macro_reachable : FxHashSet < ( hir:: HirId , DefId ) > ,
458+ /// Previous accessibility level; `None` means unreachable.
439459 prev_level : Option < AccessLevel > ,
440- // Has something changed in the level map?
460+ /// Has something changed in the level map?
441461 changed : bool ,
442462}
443463
@@ -452,7 +472,7 @@ impl EmbargoVisitor<'tcx> {
452472 self . access_levels . map . get ( & id) . cloned ( )
453473 }
454474
455- // Updates node level and returns the updated level.
475+ /// Updates node level and returns the updated level.
456476 fn update ( & mut self , id : hir:: HirId , level : Option < AccessLevel > ) -> Option < AccessLevel > {
457477 let old_level = self . get ( id) ;
458478 // Accessibility levels can only grow.
@@ -477,6 +497,127 @@ impl EmbargoVisitor<'tcx> {
477497 }
478498 }
479499
500+ /// Updates the item as being reachable through a macro defined in the given
501+ /// module. Returns `true` if the level has changed.
502+ fn update_macro_reachable ( & mut self , reachable_mod : hir:: HirId , defining_mod : DefId ) -> bool {
503+ if self . macro_reachable . insert ( ( reachable_mod, defining_mod) ) {
504+ self . update_macro_reachable_mod ( reachable_mod, defining_mod) ;
505+ true
506+ } else {
507+ false
508+ }
509+ }
510+
511+ fn update_macro_reachable_mod (
512+ & mut self ,
513+ reachable_mod : hir:: HirId ,
514+ defining_mod : DefId ,
515+ ) {
516+ let module_def_id = self . tcx . hir ( ) . local_def_id ( reachable_mod) ;
517+ let module = self . tcx . hir ( ) . get_module ( module_def_id) . 0 ;
518+ for item_id in & module. item_ids {
519+ let hir_id = item_id. id ;
520+ let item_def_id = self . tcx . hir ( ) . local_def_id ( hir_id) ;
521+ if let Some ( def_kind) = self . tcx . def_kind ( item_def_id) {
522+ let item = self . tcx . hir ( ) . expect_item ( hir_id) ;
523+ let vis = ty:: Visibility :: from_hir ( & item. vis , hir_id, self . tcx ) ;
524+ self . update_macro_reachable_def ( hir_id, def_kind, vis, defining_mod) ;
525+ }
526+ }
527+
528+ if let Some ( exports) = self . tcx . module_exports ( module_def_id) {
529+ for export in exports {
530+ if export. vis . is_accessible_from ( defining_mod, self . tcx ) {
531+ if let Res :: Def ( def_kind, def_id) = export. res {
532+ let vis = def_id_visibility ( self . tcx , def_id) . 0 ;
533+ if let Some ( hir_id) = self . tcx . hir ( ) . as_local_hir_id ( def_id) {
534+ self . update_macro_reachable_def (
535+ hir_id,
536+ def_kind,
537+ vis,
538+ defining_mod,
539+ ) ;
540+ }
541+ }
542+ }
543+ }
544+ }
545+ }
546+
547+ fn update_macro_reachable_def (
548+ & mut self ,
549+ hir_id : hir:: HirId ,
550+ def_kind : DefKind ,
551+ vis : ty:: Visibility ,
552+ module : DefId ,
553+ ) {
554+ let level = Some ( AccessLevel :: Reachable ) ;
555+ if let ty:: Visibility :: Public = vis {
556+ self . update ( hir_id, level) ;
557+ }
558+ match def_kind {
559+ // No type privacy, so can be directly marked as reachable.
560+ DefKind :: Const
561+ | DefKind :: Macro ( _)
562+ | DefKind :: Static
563+ | DefKind :: TraitAlias
564+ | DefKind :: TyAlias => {
565+ if vis. is_accessible_from ( module, self . tcx ) {
566+ self . update ( hir_id, level) ;
567+ }
568+ } ,
569+
570+ // We can't use a module name as the final segment of a path, except
571+ // in use statements. Since re-export checking doesn't consider
572+ // hygiene these don't need to be marked reachable. The contents of
573+ // the module, however may be reachable.
574+ DefKind :: Mod => {
575+ if vis. is_accessible_from ( module, self . tcx ) {
576+ self . update_macro_reachable ( hir_id, module) ;
577+ }
578+ }
579+
580+ DefKind :: Struct | DefKind :: Union => {
581+ // While structs and unions have type privacy, their fields do
582+ // not.
583+ if let ty:: Visibility :: Public = vis {
584+ let item = self . tcx . hir ( ) . expect_item ( hir_id) ;
585+ if let hir:: ItemKind :: Struct ( ref struct_def, _)
586+ | hir:: ItemKind :: Union ( ref struct_def, _) = item. node
587+ {
588+ for field in struct_def. fields ( ) {
589+ let field_vis = ty:: Visibility :: from_hir (
590+ & field. vis ,
591+ field. hir_id ,
592+ self . tcx ,
593+ ) ;
594+ if field_vis. is_accessible_from ( module, self . tcx ) {
595+ self . reach ( field. hir_id , level) . ty ( ) ;
596+ }
597+ }
598+ } else {
599+ bug ! ( "item {:?} with DefKind {:?}" , item, def_kind) ;
600+ }
601+ }
602+ }
603+
604+ // These have type privacy, so are not reachable unless they're
605+ // public
606+ DefKind :: AssocConst
607+ | DefKind :: AssocTy
608+ | DefKind :: AssocOpaqueTy
609+ | DefKind :: ConstParam
610+ | DefKind :: Ctor ( _, _)
611+ | DefKind :: Enum
612+ | DefKind :: ForeignTy
613+ | DefKind :: Fn
614+ | DefKind :: OpaqueTy
615+ | DefKind :: Method
616+ | DefKind :: Trait
617+ | DefKind :: TyParam
618+ | DefKind :: Variant => ( ) ,
619+ }
620+ }
480621
481622 /// Given the path segments of a `ItemKind::Use`, then we need
482623 /// to update the visibility of the intermediate use so that it isn't linted
@@ -746,40 +887,21 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
746887 return
747888 }
748889
749- let module_did = ty:: DefIdTree :: parent (
890+ let macro_module_def_id = ty:: DefIdTree :: parent (
750891 self . tcx ,
751892 self . tcx . hir ( ) . local_def_id ( md. hir_id )
752893 ) . unwrap ( ) ;
753- let mut module_id = self . tcx . hir ( ) . as_local_hir_id ( module_did ) . unwrap ( ) ;
894+ let mut module_id = self . tcx . hir ( ) . as_local_hir_id ( macro_module_def_id ) . unwrap ( ) ;
754895 let level = if md. vis . node . is_pub ( ) { self . get ( module_id) } else { None } ;
755- let level = self . update ( md. hir_id , level) ;
756- if level . is_none ( ) {
896+ let new_level = self . update ( md. hir_id , level) ;
897+ if new_level . is_none ( ) {
757898 return
758899 }
759900
760901 loop {
761- let module = if module_id == hir:: CRATE_HIR_ID {
762- & self . tcx . hir ( ) . krate ( ) . module
763- } else if let hir:: ItemKind :: Mod ( ref module) =
764- self . tcx . hir ( ) . expect_item ( module_id) . node {
765- module
766- } else {
767- unreachable ! ( )
768- } ;
769- for id in & module. item_ids {
770- self . update ( id. id , level) ;
771- }
772- let def_id = self . tcx . hir ( ) . local_def_id ( module_id) ;
773- if let Some ( exports) = self . tcx . module_exports ( def_id) {
774- for export in exports. iter ( ) {
775- if let Some ( hir_id) = self . tcx . hir ( ) . as_local_hir_id ( export. res . def_id ( ) ) {
776- self . update ( hir_id, level) ;
777- }
778- }
779- }
780-
781- if module_id == hir:: CRATE_HIR_ID {
782- break
902+ let changed_reachability = self . update_macro_reachable ( module_id, macro_module_def_id) ;
903+ if changed_reachability || module_id == hir:: CRATE_HIR_ID {
904+ break ;
783905 }
784906 module_id = self . tcx . hir ( ) . get_parent_node ( module_id) ;
785907 }
@@ -826,7 +948,12 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
826948 fn tcx ( & self ) -> TyCtxt < ' tcx > { self . ev . tcx }
827949 fn visit_def_id ( & mut self , def_id : DefId , _kind : & str , _descr : & dyn fmt:: Display ) -> bool {
828950 if let Some ( hir_id) = self . ev . tcx . hir ( ) . as_local_hir_id ( def_id) {
829- self . ev . update ( hir_id, self . access_level ) ;
951+ if let ( ( ty:: Visibility :: Public , ..) , _)
952+ | ( _, Some ( AccessLevel :: ReachableFromImplTrait ) )
953+ = ( def_id_visibility ( self . tcx ( ) , def_id) , self . access_level )
954+ {
955+ self . ev . update ( hir_id, self . access_level ) ;
956+ }
830957 }
831958 false
832959 }
@@ -1860,6 +1987,7 @@ fn privacy_access_levels(tcx: TyCtxt<'_>, krate: CrateNum) -> &AccessLevels {
18601987 let mut visitor = EmbargoVisitor {
18611988 tcx,
18621989 access_levels : Default :: default ( ) ,
1990+ macro_reachable : Default :: default ( ) ,
18631991 prev_level : Some ( AccessLevel :: Public ) ,
18641992 changed : false ,
18651993 } ;
0 commit comments