@@ -18,13 +18,14 @@ struct GenMacroVistor {
1818// marks everything as pub(crate) because proc-macros cannot actually export anything
1919impl < ' a > Visit < ' a > for GenMacroVistor {
2020 fn visit_item_mod ( & mut self , i : & ' a syn:: ItemMod ) {
21- let old_mod = self . current_mod . clone ( ) ;
22- let i_ident = & i. ident ;
23- self . current_mod = parse_quote ! ( #old_mod:: #i_ident) ;
21+ // Push
22+ self . current_mod . segments . push ( i. ident . clone ( ) . into ( ) ) ;
2423
2524 syn:: visit:: visit_item_mod ( self , i) ;
2625
27- self . current_mod = old_mod;
26+ // Pop
27+ self . current_mod . segments . pop ( ) . unwrap ( ) ;
28+ self . current_mod . segments . pop_punct ( ) . unwrap ( ) ; // Remove trailing `::`.
2829 }
2930
3031 fn visit_item_fn ( & mut self , i : & ' a syn:: ItemFn ) {
@@ -102,9 +103,47 @@ pub fn gen_macro(staged_path: &Path, crate_name: &str) {
102103}
103104
104105struct GenFinalPubVisitor {
105- current_mod : Option < syn:: Path > ,
106- all_macros : Vec < syn:: Ident > ,
106+ /// The current module path, starting with `crate`.
107+ current_mod : syn:: Path ,
108+ /// Stack of if each segment of `current_mod` is pub.
109+ stack_is_pub : Vec < bool > ,
110+
111+ /// If `Some("FEATURE")`, `#[cfg(test)]` modules will be gated with `#[cfg(feature = "FEATURE")]` instead of being
112+ /// fully removed.
107113 test_mode_feature : Option < String > ,
114+
115+ /// Whether the staged crate will be included in a separate crate (instead of the original crate as is usual). If
116+ /// true, then disables `pub use` [re-exporting from non-`pub` ancestor modules](https://doc.rust-lang.org/reference/visibility-and-privacy.html#r-vis.access).
117+ is_staged_separate : bool ,
118+
119+ /// All `#[macro_export]` declarative macros encountered, to be re-exported at the top `__staged` module due to the
120+ /// strange way `#[macro_export]` works.
121+ all_macros : Vec < syn:: Ident > ,
122+ }
123+ impl GenFinalPubVisitor {
124+ pub fn new (
125+ orig_crate_ident : syn:: Path ,
126+ test_mode_feature : Option < String > ,
127+ is_staged_separate : bool ,
128+ ) -> Self {
129+ Self {
130+ current_mod : orig_crate_ident,
131+ stack_is_pub : Vec :: new ( ) ,
132+ test_mode_feature,
133+ is_staged_separate,
134+ all_macros : Vec :: new ( ) ,
135+ }
136+ }
137+
138+ /// If items in the current module path ([`Self::current_mod`]) are accessible, for `pub use` re-exporting.
139+ fn can_access_current ( & self ) -> bool {
140+ self . stack_is_pub
141+ . iter ( )
142+ // If the staged crate is included in the original crate, the innermost module may be private due to the
143+ // ancestor rule: https://doc.rust-lang.org/reference/visibility-and-privacy.html#r-vis.access
144+ . skip ( if self . is_staged_separate { 0 } else { 1 } )
145+ . all ( |& x| x)
146+ }
108147}
109148
110149fn get_cfg_attrs ( attrs : & [ syn:: Attribute ] ) -> impl Iterator < Item = & syn:: Attribute > + ' _ {
@@ -159,8 +198,10 @@ fn item_visibility_ident(item: &syn::Item) -> Option<(&syn::Visibility, &syn::Id
159198 syn:: Item :: Const ( i) => Some ( ( & i. vis , & i. ident ) ) ,
160199 syn:: Item :: Enum ( i) => Some ( ( & i. vis , & i. ident ) ) ,
161200 syn:: Item :: Fn ( i) => Some ( ( & i. vis , & i. sig . ident ) ) ,
201+ syn:: Item :: Static ( i) => Some ( ( & i. vis , & i. ident ) ) ,
162202 syn:: Item :: Struct ( i) => Some ( ( & i. vis , & i. ident ) ) ,
163203 syn:: Item :: Trait ( i) => Some ( ( & i. vis , & i. ident ) ) ,
204+ syn:: Item :: TraitAlias ( i) => Some ( ( & i. vis , & i. ident ) ) ,
164205 syn:: Item :: Type ( i) => Some ( ( & i. vis , & i. ident ) ) ,
165206 syn:: Item :: Union ( i) => Some ( ( & i. vis , & i. ident ) ) ,
166207 _ => None ,
@@ -238,18 +279,20 @@ impl VisitMut for GenFinalPubVisitor {
238279 }
239280
240281 fn visit_item_mod_mut ( & mut self , i : & mut syn:: ItemMod ) {
241- let old_mod = self . current_mod . clone ( ) ;
242- let i_ident = & i. ident ;
243- self . current_mod = self
244- . current_mod
245- . as_ref ( )
246- . map ( |old_mod| parse_quote ! ( #old_mod:: #i_ident) ) ;
247-
248- i. vis = parse_quote ! ( pub ) ;
282+ // Push
283+ self . current_mod . segments . push ( i. ident . clone ( ) . into ( ) ) ;
284+ self . stack_is_pub
285+ . push ( matches ! ( i. vis, Visibility :: Public ( _) ) ) ;
249286
250287 syn:: visit_mut:: visit_item_mod_mut ( self , i) ;
251288
252- self . current_mod = old_mod;
289+ // Pop
290+ self . current_mod . segments . pop ( ) . unwrap ( ) ;
291+ self . current_mod . segments . pop_punct ( ) . unwrap ( ) ; // Remove trailing `::`.
292+ self . stack_is_pub . pop ( ) . unwrap ( ) ;
293+
294+ // Make module pub.
295+ i. vis = parse_quote ! ( pub ) ;
253296 }
254297
255298 fn visit_item_fn_mut ( & mut self , i : & mut syn:: ItemFn ) {
@@ -260,7 +303,7 @@ impl VisitMut for GenFinalPubVisitor {
260303 fn visit_item_mut ( & mut self , i : & mut syn:: Item ) {
261304 // TODO(shadaj): warn if a pub struct or enum has private fields
262305 // and is not marked for runtime
263- let cur_path = self . current_mod . as_ref ( ) . unwrap ( ) ;
306+ let cur_path = & self . current_mod ;
264307
265308 // Remove if marked with `#[cfg(stageleft_runtime)]`
266309 if is_runtime ( item_attributes ( i) ) {
@@ -270,6 +313,7 @@ impl VisitMut for GenFinalPubVisitor {
270313
271314 match i {
272315 syn:: Item :: Macro ( m) => {
316+ // TODO(mingwei): Handle if `can_access_current()` is false
273317 if let Some ( exported_items) = get_stageleft_export_items ( & m. attrs ) {
274318 * i = parse_quote ! {
275319 pub use #cur_path:: { #( #exported_items ) , * } ;
@@ -284,12 +328,14 @@ impl VisitMut for GenFinalPubVisitor {
284328 // Re-export macro at top-level later.
285329 self . all_macros . push ( m. ident . as_ref ( ) . unwrap ( ) . clone ( ) ) ;
286330 * i = syn:: Item :: Verbatim ( Default :: default ( ) ) ;
331+ return ;
287332 }
288333 }
289334 syn:: Item :: Impl ( _e) => {
290- // TODO(shadaj): emit impls if the struct is private
335+ // TODO(shadaj): emit impls if the ** struct** is private
291336 // currently, we just skip all impls
292337 * i = syn:: Item :: Verbatim ( Default :: default ( ) ) ;
338+ return ;
293339 }
294340 syn:: Item :: Mod ( m) => {
295341 let is_test_mod = m
@@ -331,8 +377,10 @@ impl VisitMut for GenFinalPubVisitor {
331377 _ => { }
332378 }
333379
334- // If a named item has pub visibility, simply re-export from original crate.
335- if let Some ( ( Visibility :: Public ( _) , name_ident) ) = item_visibility_ident ( i) {
380+ // If a named item can be accessed (mod can be accessed and item is pub), simply re-export from original crate.
381+ if self . can_access_current ( )
382+ && let Some ( ( Visibility :: Public ( _) , name_ident) ) = item_visibility_ident ( i)
383+ {
336384 let cfg_attrs = get_cfg_attrs ( item_attributes ( i) ) ;
337385 * i = parse_quote ! ( #( #cfg_attrs) * pub use #cur_path:: #name_ident; ) ;
338386 return ;
@@ -420,18 +468,33 @@ fn gen_deps_module(stageleft_name: syn::Ident, manifest_path: &Path) -> syn::Ite
420468
421469/// Generates the contents of `mod __staged`, which contains a copy of the crate's code but with
422470/// all APIs made public so they can be resolved when quoted code is spliced.
471+ ///
472+ /// # Arguments
473+ /// * `lib_path` - path to the root Rust file, usually to `lib.rs`.
474+ /// * `orig_crate_path` - Rust module path to the staged crate. Usually `crate`, but may be the staged crate name if
475+ /// the entry and staged crate/target are different.
476+ /// * `is_staged_separate` - Whether the staged crate will be included in a separate crate (instead of the original
477+ /// crate as is usual). If true, then disables `pub use` [re-exporting from non-`pub` ancestor modules](https://doc.rust-lang.org/reference/visibility-and-privacy.html#r-vis.access).
478+ /// * `test_mode_feature` - If `Some("FEATURE")`, `#[cfg(test)]` modules will be gated with
479+ /// `#[cfg(feature = "FEATURE")]` instead of being fully removed.
423480fn gen_staged_mod (
424481 lib_path : & Path ,
425- orig_crate_ident : syn:: Path ,
482+ orig_crate_path : syn:: Path ,
426483 test_mode_feature : Option < String > ,
484+ is_staged_separate : bool ,
427485) -> syn:: File {
486+ assert ! (
487+ !orig_crate_path. segments. trailing_punct( ) ,
488+ "`orig_crate_path` may not have trailing `::`"
489+ ) ;
490+
428491 let mut flow_lib_pub = syn_inline_mod:: parse_and_inline_modules ( lib_path) ;
429492
430- let mut final_pub_visitor = GenFinalPubVisitor {
431- current_mod : Some ( parse_quote ! ( #orig_crate_ident ) ) ,
493+ let mut final_pub_visitor = GenFinalPubVisitor :: new (
494+ orig_crate_path . clone ( ) ,
432495 test_mode_feature,
433- all_macros : vec ! [ ] ,
434- } ;
496+ is_staged_separate ,
497+ ) ;
435498 final_pub_visitor. visit_file_mut ( & mut flow_lib_pub) ;
436499
437500 // macros exported with `#[macro_export]` are placed at the top-level of the crate,
@@ -440,7 +503,7 @@ fn gen_staged_mod(
440503 for exported_macro in final_pub_visitor. all_macros {
441504 flow_lib_pub
442505 . items
443- . push ( parse_quote ! ( pub use #orig_crate_ident :: #exported_macro; ) ) ;
506+ . push ( parse_quote ! ( pub use #orig_crate_path :: #exported_macro; ) ) ;
444507 }
445508
446509 flow_lib_pub
@@ -449,14 +512,23 @@ fn gen_staged_mod(
449512/// Generates the contents for `__staged` when it will be emitted in "trybuild mode", which means that
450513/// it is included inline next to the spliced code that uses it, with the original crate available as
451514/// a dependency.
515+ ///
516+ /// # Arguments
517+ /// * `lib_path` - path to the root Rust file, usually to `lib.rs`.
518+ /// * `manifest_path` - path to the package `Cargo.toml`.
519+ /// * `orig_crate_path` - Rust module path to the staged crate. Usually `crate`, but may be the staged crate name if
520+ /// the entry and staged crate/target are different.
521+ /// * `test_mode_feature` - If `Some("FEATURE")`, `#[cfg(test)]` modules will be gated with
522+ /// `#[cfg(feature = "FEATURE")]` instead of being fully removed.
452523pub fn gen_staged_trybuild (
453524 lib_path : & Path ,
454525 manifest_path : & Path ,
455- orig_crate_name : String ,
526+ orig_crate_path : & str ,
456527 test_mode_feature : Option < String > ,
457528) -> syn:: File {
458- let crate_name = syn:: Ident :: new ( & orig_crate_name, Span :: call_site ( ) ) ;
459- let mut flow_lib_pub = gen_staged_mod ( lib_path, parse_quote ! ( #crate_name) , test_mode_feature) ;
529+ let orig_crate_path = syn:: parse_str ( orig_crate_path)
530+ . expect ( "Failed to parse `orig_crate_path` as `crate`, crate name, or module path." ) ;
531+ let mut flow_lib_pub = gen_staged_mod ( lib_path, orig_crate_path, test_mode_feature, true ) ;
460532
461533 let deps_mod = gen_deps_module ( parse_quote ! ( stageleft) , manifest_path) ;
462534
@@ -484,6 +556,7 @@ pub fn gen_staged_pub() {
484556 . unwrap_or_else ( || Path :: new ( "src/lib.rs" ) ) ,
485557 parse_quote ! ( crate ) ,
486558 None ,
559+ false ,
487560 ) ;
488561
489562 fs:: write (
0 commit comments