From 01e46b0df294ed54c173cfead142c44d727b082e Mon Sep 17 00:00:00 2001 From: Mateusz Kowalski Date: Wed, 2 Oct 2024 09:28:08 +0200 Subject: [PATCH] Doc/support reexported modules (#1620) --- extensions/scarb-doc/src/types.rs | 241 ++++++-- .../scarb-doc/tests/data/json_reexports.json | 534 ++++++++++++++++++ extensions/scarb-doc/tests/reexports.rs | 129 +++++ 3 files changed, 844 insertions(+), 60 deletions(-) create mode 100644 extensions/scarb-doc/tests/data/json_reexports.json create mode 100644 extensions/scarb-doc/tests/reexports.rs diff --git a/extensions/scarb-doc/src/types.rs b/extensions/scarb-doc/src/types.rs index 6fefc37f8..b329884e0 100644 --- a/extensions/scarb-doc/src/types.rs +++ b/extensions/scarb-doc/src/types.rs @@ -1,15 +1,19 @@ -use cairo_lang_semantic::items::visibility; +use cairo_lang_semantic::items::functions::GenericFunctionId; +use cairo_lang_semantic::items::us::SemanticUseEx; +use cairo_lang_semantic::items::visibility::{self, Visibility}; +use cairo_lang_semantic::resolve::ResolvedGenericItem; use cairo_lang_syntax::node::helpers::QueryAttrs; use cairo_lang_utils::Upcast; +use itertools::chain; use serde::Serialize; use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::ids::{ - ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, ImplAliasId, + ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, GenericTypeId, ImplAliasId, ImplConstantDefId, ImplDefId, ImplFunctionId, ImplItemId, ImplTypeDefId, LanguageElementId, - LookupItemId, MemberId, ModuleId, ModuleItemId, ModuleTypeAliasId, StructId, - TopLevelLanguageElementId, TraitConstantId, TraitFunctionId, TraitId, TraitItemId, TraitTypeId, - VariantId, + LookupItemId, MemberId, ModuleId, ModuleItemId, ModuleTypeAliasId, NamedLanguageElementId, + StructId, SubmoduleId, TopLevelLanguageElementId, TraitConstantId, TraitFunctionId, TraitId, + TraitItemId, TraitTypeId, VariantId, }; use cairo_lang_doc::db::DocGroup; use cairo_lang_doc::documentable_item::DocumentableItemId; @@ -72,6 +76,95 @@ pub struct Module { pub extern_functions: Vec, } +struct ModulePubUses { + pub use_constants: Vec, + pub use_free_functions: Vec, + pub use_structs: Vec, + pub use_enums: Vec, + pub use_module_type_aliases: Vec, + pub use_impl_aliases: Vec, + pub use_traits: Vec, + pub use_impl_defs: Vec, + pub use_extern_types: Vec, + pub use_extern_functions: Vec, + pub use_submodules: Vec, + pub use_crates: Vec, +} + +impl ModulePubUses { + pub fn new(db: &ScarbDocDatabase, module_id: ModuleId) -> Self { + let module_use_items: Vec = db + .module_uses(module_id) + .unwrap() + .iter() + .filter_map(|(use_id, _)| { + let visibility = db + .module_item_info_by_name(module_id, use_id.name(db)) + .unwrap() + .unwrap() + .visibility; + if visibility == Visibility::Public { + Some(db.use_resolved_item(*use_id).unwrap()) + } else { + None + } + }) + .collect(); + + let mut use_constants = Vec::new(); + let mut use_free_functions = Vec::new(); + let mut use_structs = Vec::new(); + let mut use_enums = Vec::new(); + let mut use_module_type_aliases = Vec::new(); + let mut use_impl_aliases = Vec::new(); + let mut use_traits = Vec::new(); + let mut use_impl_defs = Vec::new(); + let mut use_extern_types = Vec::new(); + let mut use_extern_functions = Vec::new(); + let mut use_submodules = Vec::new(); + let mut use_crates = Vec::new(); + + for item in module_use_items { + match item { + ResolvedGenericItem::GenericConstant(id) => use_constants.push(id), + ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(id)) => { + use_free_functions.push(id) + } + ResolvedGenericItem::GenericType(GenericTypeId::Struct(id)) => use_structs.push(id), + ResolvedGenericItem::GenericType(GenericTypeId::Enum(id)) => use_enums.push(id), + ResolvedGenericItem::GenericTypeAlias(id) => use_module_type_aliases.push(id), + ResolvedGenericItem::GenericImplAlias(id) => use_impl_aliases.push(id), + ResolvedGenericItem::Trait(id) => use_traits.push(id), + ResolvedGenericItem::Impl(id) => use_impl_defs.push(id), + ResolvedGenericItem::GenericType(GenericTypeId::Extern(id)) => { + use_extern_types.push(id) + } + ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(id)) => { + use_extern_functions.push(id) + } + ResolvedGenericItem::Module(ModuleId::Submodule(id)) => use_submodules.push(id), + ResolvedGenericItem::Module(ModuleId::CrateRoot(id)) => use_crates.push(id), + _ => (), + } + } + + Self { + use_constants, + use_free_functions, + use_structs, + use_enums, + use_module_type_aliases, + use_impl_aliases, + use_traits, + use_impl_defs, + use_extern_types, + use_extern_functions, + use_submodules, + use_crates, + } + } +} + impl Module { pub fn new( db: &ScarbDocDatabase, @@ -80,12 +173,7 @@ impl Module { include_private_items: bool, ) -> Self { let item_data = match module_id { - ModuleId::CrateRoot(crate_id) => ItemData { - name: crate_id.name(db).to_string(), - doc: db.get_item_documentation(DocumentableItemId::Crate(crate_id)), - signature: None, - full_path: module_id.full_path(db), - }, + ModuleId::CrateRoot(crate_id) => ItemData::new_crate(db, crate_id), ModuleId::Submodule(submodule_id) => ItemData::new_without_signature( db, submodule_id, @@ -100,90 +188,114 @@ impl Module { && !is_doc_hidden_attr(db, &syntax_node) }; + let module_pubuses = ModulePubUses::new(db, module_id); + let module_constants = db.module_constants(module_id).unwrap(); - let constants = module_constants - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| Constant::new(db, *id)) + let constants = chain!(module_constants.keys(), module_pubuses.use_constants.iter()) + .filter(|id| should_include_item(*id)) + .map(|id| Constant::new(db, *id)) .collect(); let module_free_functions = db.module_free_functions(module_id).unwrap(); - let free_functions = module_free_functions - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| FreeFunction::new(db, *id)) - .collect(); + let free_functions = chain!( + module_free_functions.keys(), + module_pubuses.use_free_functions.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| FreeFunction::new(db, *id)) + .collect(); let module_structs = db.module_structs(module_id).unwrap(); - let structs = module_structs - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| Struct::new(db, *id, root_module_id, include_private_items)) + let structs = chain!(module_structs.keys(), module_pubuses.use_structs.iter()) + .filter(|id| should_include_item(*id)) + .map(|id| Struct::new(db, *id, root_module_id, include_private_items)) .collect(); let module_enums = db.module_enums(module_id).unwrap(); - let enums = module_enums - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| Enum::new(db, *id)) + let enums = chain!(module_enums.keys(), module_pubuses.use_enums.iter()) + .filter(|id| should_include_item(*id)) + .map(|id| Enum::new(db, *id)) .collect(); let module_type_aliases = db.module_type_aliases(module_id).unwrap(); - let type_aliases = module_type_aliases - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| TypeAlias::new(db, *id)) - .collect(); + let type_aliases = chain!( + module_type_aliases.keys(), + module_pubuses.use_module_type_aliases.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| TypeAlias::new(db, *id)) + .collect(); let module_impl_aliases = db.module_impl_aliases(module_id).unwrap(); - let impl_aliases = module_impl_aliases - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| ImplAlias::new(db, *id)) - .collect(); + let impl_aliases = chain!( + module_impl_aliases.keys(), + module_pubuses.use_impl_aliases.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| ImplAlias::new(db, *id)) + .collect(); let module_traits = db.module_traits(module_id).unwrap(); - let traits = module_traits - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| Trait::new(db, *id)) + let traits = chain!(module_traits.keys(), module_pubuses.use_traits.iter()) + .filter(|id| should_include_item(*id)) + .map(|id| Trait::new(db, *id)) .collect(); let module_impls = db.module_impls(module_id).unwrap(); - let impls = module_impls - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| Impl::new(db, *id)) + let impls = chain!(module_impls.keys(), module_pubuses.use_impl_defs.iter()) + .filter(|id| should_include_item(*id)) + .map(|id| Impl::new(db, *id)) .collect(); let module_extern_types = db.module_extern_types(module_id).unwrap(); - let extern_types = module_extern_types - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| ExternType::new(db, *id)) - .collect(); + let extern_types = chain!( + module_extern_types.keys(), + module_pubuses.use_extern_types.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| ExternType::new(db, *id)) + .collect(); let module_extern_functions = db.module_extern_functions(module_id).unwrap(); - let extern_functions = module_extern_functions - .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| ExternFunction::new(db, *id)) - .collect(); + let extern_functions = chain!( + module_extern_functions.keys(), + module_pubuses.use_extern_functions.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| ExternFunction::new(db, *id)) + .collect(); let module_submodules = db.module_submodules(module_id).unwrap(); - let submodules = module_submodules + let mut submodules: Vec = chain!( + module_submodules.keys(), + module_pubuses.use_submodules.iter() + ) + .filter(|id| should_include_item(*id)) + .map(|id| { + Self::new( + db, + root_module_id, + ModuleId::Submodule(*id), + include_private_items, + ) + }) + .collect(); + + let reexported_crates_as_modules: Vec = module_pubuses + .use_crates .iter() - .filter(|(id, _)| should_include_item(*id)) - .map(|(id, _)| { + .map(|id| { Self::new( db, root_module_id, - ModuleId::Submodule(*id), + ModuleId::CrateRoot(*id), include_private_items, ) }) .collect(); + submodules.extend(reexported_crates_as_modules); + Self { module_id, item_data, @@ -240,6 +352,15 @@ impl ItemData { full_path: id.full_path(db), } } + + pub fn new_crate(db: &ScarbDocDatabase, id: CrateId) -> Self { + Self { + name: id.name(db).into(), + doc: db.get_item_documentation(DocumentableItemId::Crate(id)), + signature: None, + full_path: ModuleId::CrateRoot(id).full_path(db), + } + } } #[derive(Serialize, Clone)] diff --git a/extensions/scarb-doc/tests/data/json_reexports.json b/extensions/scarb-doc/tests/data/json_reexports.json new file mode 100644 index 000000000..3934a7254 --- /dev/null +++ b/extensions/scarb-doc/tests/data/json_reexports.json @@ -0,0 +1,534 @@ +{ + "format_version": 1, + "packages_information": [ + { + "crate_": { + "root_module": { + "item_data": { + "name": "hello_world", + "doc": null, + "signature": null, + "full_path": "hello_world" + }, + "submodules": [ + { + "item_data": { + "name": "sub_module", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module" + }, + "submodules": [ + { + "item_data": { + "name": "inner_module", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module" + }, + "submodules": [ + { + "item_data": { + "name": "inside_inner_module", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::inside_inner_module" + }, + "submodules": [], + "constants": [], + "free_functions": [], + "structs": [], + "enums": [], + "type_aliases": [], + "impl_aliases": [], + "traits": [], + "impls": [], + "extern_types": [], + "extern_functions": [] + } + ], + "constants": [ + { + "item_data": { + "name": "ABC", + "doc": null, + "signature": "pub const ABC: u32 = 44;", + "full_path": "hello_world::sub_module::inner_module::ABC" + } + } + ], + "free_functions": [ + { + "item_data": { + "name": "display", + "doc": null, + "signature": "pub fn display()", + "full_path": "hello_world::sub_module::inner_module::display" + } + } + ], + "structs": [ + { + "members": [ + { + "item_data": { + "name": "abc", + "doc": null, + "signature": " abc: u32", + "full_path": "hello_world::sub_module::inner_module::TestStruct::abc" + } + } + ], + "item_data": { + "name": "TestStruct", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::TestStruct" + } + } + ], + "enums": [ + { + "variants": [ + { + "item_data": { + "name": "Var1", + "doc": null, + "signature": "", + "full_path": "hello_world::sub_module::inner_module::TestEnum::Var1" + } + } + ], + "item_data": { + "name": "TestEnum", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::TestEnum" + } + } + ], + "type_aliases": [ + { + "item_data": { + "name": "Type", + "doc": null, + "signature": "pub type Type = u32;", + "full_path": "hello_world::sub_module::inner_module::Type" + } + } + ], + "impl_aliases": [], + "traits": [ + { + "trait_constants": [], + "trait_types": [], + "trait_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test() -> ()", + "full_path": "hello_world::sub_module::inner_module::TestTrait::test" + } + } + ], + "item_data": { + "name": "TestTrait", + "doc": null, + "signature": "pub trait TestTrait", + "full_path": "hello_world::sub_module::inner_module::TestTrait" + } + } + ], + "impls": [ + { + "impl_types": [], + "impl_constants": [], + "impl_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test()", + "full_path": "hello_world::sub_module::inner_module::TestImpl::test" + } + } + ], + "item_data": { + "name": "TestImpl", + "doc": null, + "signature": "pub impl TestImpl of TestTrait", + "full_path": "hello_world::sub_module::inner_module::TestImpl" + } + } + ], + "extern_types": [ + { + "item_data": { + "name": "ExternalType", + "doc": null, + "signature": "pub extern type ExternalType;", + "full_path": "hello_world::sub_module::inner_module::ExternalType" + } + } + ], + "extern_functions": [ + { + "item_data": { + "name": "extern_function", + "doc": null, + "signature": "pub extern fn extern_function() -> u32 nopanic;", + "full_path": "hello_world::sub_module::inner_module::extern_function" + } + } + ] + }, + { + "item_data": { + "name": "inside_inner_module", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::inside_inner_module" + }, + "submodules": [], + "constants": [], + "free_functions": [], + "structs": [], + "enums": [], + "type_aliases": [], + "impl_aliases": [], + "traits": [], + "impls": [], + "extern_types": [], + "extern_functions": [] + } + ], + "constants": [ + { + "item_data": { + "name": "ABC", + "doc": null, + "signature": "pub const ABC: u32 = 44;", + "full_path": "hello_world::sub_module::inner_module::ABC" + } + } + ], + "free_functions": [ + { + "item_data": { + "name": "display", + "doc": null, + "signature": "pub fn display()", + "full_path": "hello_world::sub_module::inner_module::display" + } + } + ], + "structs": [ + { + "members": [ + { + "item_data": { + "name": "abc", + "doc": null, + "signature": " abc: u32", + "full_path": "hello_world::sub_module::inner_module::TestStruct::abc" + } + } + ], + "item_data": { + "name": "TestStruct", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::TestStruct" + } + } + ], + "enums": [ + { + "variants": [ + { + "item_data": { + "name": "Var1", + "doc": null, + "signature": "", + "full_path": "hello_world::sub_module::inner_module::TestEnum::Var1" + } + } + ], + "item_data": { + "name": "TestEnum", + "doc": null, + "signature": null, + "full_path": "hello_world::sub_module::inner_module::TestEnum" + } + } + ], + "type_aliases": [ + { + "item_data": { + "name": "Type", + "doc": null, + "signature": "pub type Type = u32;", + "full_path": "hello_world::sub_module::inner_module::Type" + } + } + ], + "impl_aliases": [], + "traits": [ + { + "trait_constants": [], + "trait_types": [], + "trait_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test() -> ()", + "full_path": "hello_world::sub_module::inner_module::TestTrait::test" + } + } + ], + "item_data": { + "name": "TestTrait", + "doc": null, + "signature": "pub trait TestTrait", + "full_path": "hello_world::sub_module::inner_module::TestTrait" + } + } + ], + "impls": [ + { + "impl_types": [], + "impl_constants": [], + "impl_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test()", + "full_path": "hello_world::sub_module::inner_module::TestImpl::test" + } + } + ], + "item_data": { + "name": "TestImpl", + "doc": null, + "signature": "pub impl TestImpl of TestTrait", + "full_path": "hello_world::sub_module::inner_module::TestImpl" + } + } + ], + "extern_types": [ + { + "item_data": { + "name": "ExternalType", + "doc": null, + "signature": "pub extern type ExternalType;", + "full_path": "hello_world::sub_module::inner_module::ExternalType" + } + } + ], + "extern_functions": [ + { + "item_data": { + "name": "extern_function", + "doc": null, + "signature": "pub extern fn extern_function() -> u32 nopanic;", + "full_path": "hello_world::sub_module::inner_module::extern_function" + } + } + ] + }, + { + "item_data": { + "name": "sub_package", + "doc": null, + "signature": null, + "full_path": "sub_package" + }, + "submodules": [ + { + "item_data": { + "name": "inside_sub_module", + "doc": null, + "signature": null, + "full_path": "sub_package::inside_sub_module" + }, + "submodules": [], + "constants": [], + "free_functions": [], + "structs": [], + "enums": [], + "type_aliases": [], + "impl_aliases": [], + "traits": [], + "impls": [], + "extern_types": [], + "extern_functions": [] + } + ], + "constants": [ + { + "item_data": { + "name": "ABC", + "doc": null, + "signature": "pub const ABC: u32 = 44;", + "full_path": "sub_package::ABC" + } + } + ], + "free_functions": [ + { + "item_data": { + "name": "display", + "doc": null, + "signature": "pub fn display()", + "full_path": "sub_package::display" + } + } + ], + "structs": [ + { + "members": [ + { + "item_data": { + "name": "abc", + "doc": null, + "signature": " abc: u32", + "full_path": "sub_package::TestStruct::abc" + } + } + ], + "item_data": { + "name": "TestStruct", + "doc": null, + "signature": null, + "full_path": "sub_package::TestStruct" + } + } + ], + "enums": [ + { + "variants": [ + { + "item_data": { + "name": "Var1", + "doc": null, + "signature": "", + "full_path": "sub_package::TestEnum::Var1" + } + } + ], + "item_data": { + "name": "TestEnum", + "doc": null, + "signature": null, + "full_path": "sub_package::TestEnum" + } + } + ], + "type_aliases": [ + { + "item_data": { + "name": "Type", + "doc": null, + "signature": "pub type Type = u32;", + "full_path": "sub_package::Type" + } + } + ], + "impl_aliases": [], + "traits": [ + { + "trait_constants": [], + "trait_types": [], + "trait_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test() -> ()", + "full_path": "sub_package::TestTrait::test" + } + } + ], + "item_data": { + "name": "TestTrait", + "doc": null, + "signature": "pub trait TestTrait", + "full_path": "sub_package::TestTrait" + } + } + ], + "impls": [ + { + "impl_types": [], + "impl_constants": [], + "impl_functions": [ + { + "item_data": { + "name": "test", + "doc": null, + "signature": "fn test()", + "full_path": "sub_package::TestImpl::test" + } + } + ], + "item_data": { + "name": "TestImpl", + "doc": null, + "signature": "pub impl TestImpl of TestTrait", + "full_path": "sub_package::TestImpl" + } + } + ], + "extern_types": [ + { + "item_data": { + "name": "ExternalType", + "doc": null, + "signature": "pub extern type ExternalType;", + "full_path": "sub_package::ExternalType" + } + } + ], + "extern_functions": [ + { + "item_data": { + "name": "extern_function", + "doc": null, + "signature": "pub extern fn extern_function() -> u32 nopanic;", + "full_path": "sub_package::extern_function" + } + } + ] + } + ], + "constants": [], + "free_functions": [ + { + "item_data": { + "name": "main", + "doc": null, + "signature": "fn main()", + "full_path": "hello_world::main" + } + } + ], + "structs": [], + "enums": [], + "type_aliases": [], + "impl_aliases": [], + "traits": [], + "impls": [], + "extern_types": [], + "extern_functions": [] + } + }, + "metadata": { + "name": "hello_world", + "authors": null + } + } + ] +} diff --git a/extensions/scarb-doc/tests/reexports.rs b/extensions/scarb-doc/tests/reexports.rs new file mode 100644 index 000000000..648bf5c5d --- /dev/null +++ b/extensions/scarb-doc/tests/reexports.rs @@ -0,0 +1,129 @@ +use assert_fs::prelude::PathChild; +use assert_fs::TempDir; +use indoc::indoc; +use scarb_test_support::command::Scarb; +use scarb_test_support::project_builder::ProjectBuilder; +use scarb_test_support::workspace_builder::WorkspaceBuilder; + +mod json_target; +use json_target::JsonTargetChecker; + +#[test] +fn test_reexports() { + let root_dir = TempDir::new().unwrap(); + let child_dir = root_dir.child("sub_package"); + + ProjectBuilder::start() + .name("sub_package") + .lib_cairo(indoc! {r#" + pub fn display() { + println!("Hello from the inner module!"); + } + pub const ABC: u32 = 44; + + pub type Type = u32; + + pub struct TestStruct { + abc: u32 + } + + pub enum TestEnum { + Var1 + } + + pub trait TestTrait { + fn test() -> (); + } + + pub impl TestImpl of TestTrait { + fn test() { + println!("test"); + } + } + + pub extern fn extern_function() -> u32 nopanic; + + pub extern type ExternalType; + + pub mod inside_sub_module {} + "#}) + .build(&child_dir); + + let root = ProjectBuilder::start() + .name("hello_world") + .dep("sub_package", &child_dir) + .lib_cairo(indoc! {r#" + pub use sub_package as package; + + mod sub_module; + + fn main() { + println!("main"); + } + "#}) + .src( + "src/sub_module.cairo", + indoc! {r#" + mod inner_module { + pub fn display() { + println!("Hello from the inner module!"); + } + pub const ABC: u32 = 44; + + pub type Type = u32; + + pub struct TestStruct { + abc: u32 + } + + pub enum TestEnum { + Var1 + } + + pub trait TestTrait { + fn test() -> (); + } + + pub impl TestImpl of TestTrait { + fn test() { + println!("test"); + } + } + + pub extern fn extern_function() -> u32 nopanic; + + pub extern type ExternalType; + + pub mod inside_inner_module {} + } + + pub use inner_module::display; + pub use inner_module::ABC; + pub use inner_module::Type; + pub use inner_module::TestStruct; + pub use inner_module::TestEnum; + pub use inner_module::TestTrait; + pub use inner_module::TestImpl; + pub use inner_module::extern_function; + pub use inner_module::ExternalType; + pub use inner_module::inside_inner_module; + "#}, + ); + + WorkspaceBuilder::start() + .add_member("sub_package") + .package(root) + .build(&root_dir); + + Scarb::quick_snapbox() + .arg("doc") + .args(["--output-format", "json"]) + .current_dir(&root_dir) + .assert() + .success(); + + JsonTargetChecker::default() + .actual(&root_dir.path().join("target/doc/output.json")) + .expected("./data/json_reexports.json") + .assert_files_match(); +}