Skip to content

Commit

Permalink
Merge pull request #3576 from ncave/rust
Browse files Browse the repository at this point in the history
[Rust] Support multiple namespaces sharing a prefix in the same file
  • Loading branch information
ncave authored Oct 30, 2023
2 parents c850c8c + 1d25ad1 commit 25f6bf6
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 21 deletions.
6 changes: 6 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Fix #3571: `[<AttachMembers>]` 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
Expand Down
46 changes: 43 additions & 3 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -4093,13 +4126,15 @@ 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
| Fable.ModuleDeclaration _ -> () // delay module decl transform
| _ -> 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
Expand Down Expand Up @@ -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) =
Expand Down Expand Up @@ -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
27 changes: 9 additions & 18 deletions tests/Rust/tests/common/Util3.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit 25f6bf6

Please sign in to comment.