Skip to content

Commit

Permalink
Function decorated with [<NamedParams>] without arguments provided sh…
Browse files Browse the repository at this point in the history
…ould take an empty object

Fix #3480
  • Loading branch information
Maxime Mangel committed Aug 9, 2023
1 parent f7196af commit 3662dfc
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 6 deletions.
11 changes: 6 additions & 5 deletions src/Fable.AST/Fable.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ type EntityRef =
| PrecompiledLib(p,_) -> Some p
| AssemblyPath _ | CoreAssemblyName _ -> None

type Attribute =
abstract Entity: EntityRef
abstract ConstructorArgs: obj list

type MemberRefInfo =
{ IsInstance: bool
CompiledName: string
NonCurriedArgTypes: Type list option }
NonCurriedArgTypes: Type list option
Attributes : Attribute seq }

type MemberRef =
| MemberRef of declaringEntity: EntityRef * info: MemberRefInfo
Expand All @@ -44,10 +49,6 @@ type DeclaredType =
abstract Entity: EntityRef
abstract GenericArgs: Type list

type Attribute =
abstract Entity: EntityRef
abstract ConstructorArgs: obj list

type Field =
abstract Name: string
abstract FieldType: Type
Expand Down
4 changes: 4 additions & 0 deletions src/Fable.Cli/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Unreleased

* Fix #3480: Function decorated with `[<NamedParams>]` without arguments provided should take an empty object

### 4.1.4

* Fix #3438: Source maps
Expand Down
7 changes: 7 additions & 0 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,10 +1694,14 @@ module Util =
|> Seq.mapToList (fun p -> makeType Map.empty p.Type)
|> Some
else None

let fableMemberFunctionOrValue = FsMemberFunctionOrValue(memb) :> Fable.MemberFunctionOrValue

Fable.MemberRef(FsEnt.Ref(ent), {
CompiledName = memb.CompiledName
IsInstance = memb.IsInstanceMember
NonCurriedArgTypes = nonCurriedArgTypes
Attributes = fableMemberFunctionOrValue.Attributes
})
| ent ->
let entRef = ent |> Option.map FsEnt.Ref
Expand All @@ -1712,10 +1716,13 @@ module Util =
match memb.DeclaringEntity with
// We cannot retrieve compiler generated members from the entity
| Some ent when not memb.IsCompilerGenerated ->
let fableMemberFunctionOrValue = FsMemberFunctionOrValue(memb) :> Fable.MemberFunctionOrValue

Fable.MemberRef(FsEnt.Ref(ent), {
CompiledName = memb.CompiledName
IsInstance = memb.IsInstanceMember
NonCurriedArgTypes = None
Attributes = fableMemberFunctionOrValue.Attributes
})
| ent ->
let entRef = ent |> Option.map FsEnt.Ref
Expand Down
21 changes: 20 additions & 1 deletion src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,26 @@ module Util =
|> Option.map (splitNamedArgs args)
|> function
| None -> args, None
| Some(args, []) -> args, None
| Some(args, []) ->
// Detect if the method has a ParamObject attribute
// If yes and no argument is passed, pass an empty object
// See https://github.com/fable-compiler/Fable/issues/3480
match callInfo.MemberRef with
| Some (Fable.MemberRef (_, info)) ->
let hasParamObjectAttribute =
info.Attributes
|> Seq.tryFind (fun attr ->
attr.Entity.FullName = Atts.paramObject
)
|> Option.isSome

if hasParamObjectAttribute then
args, Some (makeJsObject [])
else
args, None
| _ ->
// Here detect empty named args
args, None
| Some(args, namedArgs) ->
let objArg =
namedArgs
Expand Down
7 changes: 7 additions & 0 deletions tests/Js/Main/OptionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ type VeryOptionalClass () =
type StaticClass =
[<NamedParams>]
static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
[<NamedParams>]
static member NamedParams(?name : string, ?age: int) =
let name = defaultArg name "John"
let age = defaultArg age 30

$"%s{name} is %d{age} years old"
static member FSharpOptionalParam(?value: bool) = defaultArg value true
static member FSharpOptionalParam2(?value: unit) = Option.isSome value
static member DefaultParam([<Optional; DefaultParameterValue(true)>] value: bool) = value
Expand Down Expand Up @@ -142,6 +148,7 @@ let tests =
StaticClass.NamedParams(foo="5", bar=4) |> equal 9
StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
StaticClass.NamedParams(foo="14") |> equal 11
StaticClass.NamedParams() |> equal "John is 30 years old"

testCase "F# optional param works" <| fun () ->
let mutable f1 = fun (v: bool) ->
Expand Down

0 comments on commit 3662dfc

Please sign in to comment.