From 1d25ad1f57942bac73d8be4b4c12dcde7d31c406 Mon Sep 17 00:00:00 2001 From: ncave <777696+ncave@users.noreply.github.com> Date: Mon, 30 Oct 2023 08:37:15 -0700 Subject: [PATCH] [Rust] Support multiple namespaces sharing a prefix in the same file --- src/Fable.Cli/CHANGELOG.md | 6 ++++ src/Fable.Transforms/Rust/Fable2Rust.fs | 46 +++++++++++++++++++++++-- tests/Rust/tests/common/Util3.fs | 27 +++++---------- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index c0136d6922..66e475c87f 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fix #3571: `[]` not compatible with f# member `this.Item` (by @ncave) +### Changed + +#### Rust + +* Support multiple namespaces sharing a prefix in the same file (by @ncave) + ## 4.4.1 - 2023-10-25 ### Changed diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index acf32ed08a..5cc7d4e81b 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -4085,6 +4085,39 @@ module Util = let isInternal, isPrivate = getVis com ctx memb.DeclaringEntity memb.IsInternal memb.IsPrivate memberAssocItem |> mkAssocItemWithVis isInternal isPrivate + let mergeNamespaceDecls (com: IRustCompiler) ctx decls = + // separate namespace decls from the others + let namespaceDecls, otherDecls = + decls + |> List.partition (function + | Fable.ModuleDeclaration d -> + let ent = com.GetEntity(d.Entity) + ent.IsNamespace + | _ -> false) + // merge namespace decls with the same name into a single decl + let namespaceDecls = + namespaceDecls + |> List.groupBy (function + | Fable.ModuleDeclaration d -> d.Name + | _ -> failwith "unreachable") + |> List.map (fun (key, decls) -> + match decls with + | [d] -> d // no merge needed + | _ -> + let members = + decls + |> List.map (function + | Fable.ModuleDeclaration d -> d.Members + | _ -> []) + |> List.concat + match List.head decls with + | Fable.ModuleDeclaration d -> + Fable.ModuleDeclaration { d with Members = members } + | d -> d + ) + // return merged decls + List.append namespaceDecls otherDecls + let transformModuleDecl (com: IRustCompiler) ctx (decl: Fable.ModuleDecl) = let ctx = { ctx with ModuleDepth = ctx.ModuleDepth + 1 } let memberDecls = @@ -4093,6 +4126,7 @@ module Util = // this prioritizes non-module declaration transforms first, // so module imports can be properly deduped top to bottom. decl.Members + |> mergeNamespaceDecls com ctx |> List.map (fun decl -> let lazyDecl = lazy (transformDecl com ctx decl) match decl with @@ -4100,6 +4134,7 @@ module Util = | _ -> lazyDecl.Force() |> ignore // transform other decls first lazyDecl) |> List.collect (fun lazyDecl -> lazyDecl.Force()) + if List.isEmpty memberDecls then [] // don't output empty modules else @@ -4141,6 +4176,11 @@ module Util = | Fable.ClassDeclaration decl -> transformClassDecl com ctx decl + let transformDeclarations (com: IRustCompiler) ctx decls = + decls + |> mergeNamespaceDecls com ctx + |> List.collect (transformDecl com ctx) + // F# hash function is unstable and gives different results in different runs // Taken from fable-library/Util.ts. Possible variant in https://stackoverflow.com/a/1660613 let stableStringHash (s: string) = @@ -4370,12 +4410,12 @@ module Compiler = // mkInnerAttr "feature" ["destructuring_assignment"] ] - let entryPointItems = getEntryPointItems com ctx file.Declarations + let entryPointItems = file.Declarations |> getEntryPointItems com ctx let importItems = com.GetAllImports(ctx) |> transformImports com ctx - let declItems = List.collect (transformDecl com ctx) file.Declarations + let declItems = file.Declarations |> transformDeclarations com ctx let moduleItems = getModuleItems com ctx // global module imports let crateItems = importItems @ declItems @ moduleItems @ entryPointItems - let innerAttrs = getInnerAttributes com ctx file.Declarations + let innerAttrs = file.Declarations |> getInnerAttributes com ctx let crateAttrs = topAttrs @ innerAttrs let crate = mkCrate crateAttrs crateItems crate diff --git a/tests/Rust/tests/common/Util3.fs b/tests/Rust/tests/common/Util3.fs index e62ee898eb..579a84bde9 100644 --- a/tests/Rust/tests/common/Util3.fs +++ b/tests/Rust/tests/common/Util3.fs @@ -7,23 +7,14 @@ // Duplicating namespaces doesn't work // namespace Fable.Tests.A.C -// // Empty namespaces work -// namespace Fable.Tests.A.D +// Empty namespaces work +namespace Fable.Tests.A.D -// // Multiple mamespaces sharing prefix work -// namespace Fable.Tests.A.B -// type Helper = -// static member Add2(x) = x + 2 +// Multiple mamespaces sharing prefix work +namespace Fable.Tests.A.B +type Helper = + static member Add2(x) = x + 2 -// namespace Fable.Tests.A.C -// type Helper = -// static member Add5(x) = Fable.Tests.A.B.Helper.Add2(x + 3) - -//TODO: Support multiple namespaces in the same file -namespace Fable.Tests.A -module B = - type Helper = - static member Add2(x) = x + 2 -module C = - type Helper = - static member Add5(x) = B.Helper.Add2(x + 3) +namespace Fable.Tests.A.C +type Helper = + static member Add5(x) = Fable.Tests.A.B.Helper.Add2(x + 3)