Skip to content

Commit 2d9673f

Browse files
authored
fix(js/ts): throw an error when trying to set non-property memeber inside of jsOptions (#4624)
1 parent d544405 commit 2d9673f

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

src/Fable.Transforms/Replacements.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,24 @@ let makePojoFromLambda com (arg: Expr) =
884884

885885
// This is a nested property
886886
groupByGetter updatedAcc tail
887+
888+
// Assigning to a non-property interface member (a plain abstract method
889+
// declared without `with get, set`) is invalid F#: the compiler rejects it
890+
// with `FS0971: Undefined value 'copyOfStruct'`. Fable's FCS fork doesn't
891+
// surface that diagnostic, instead handing us the address of a throwaway
892+
// local (`&copyOfStruct`) compiled as `let addr = FSharpRef(...) in addr.contents <- value`.
893+
| Let(addrIdent,
894+
Call(Import(importInfo, _, _), callInfo, _, _),
895+
Set(IdentExpr setIdent, ExprSet _, _, _, r)) when
896+
importInfo.Selector = "FSharpRef"
897+
&& List.contains "new" callInfo.Tags
898+
&& setIdent.Name = addrIdent.Name
899+
->
900+
"Cannot set a non-property member in 'jsOptions'. Declare the interface member as a settable property, e.g. `abstract X: T with get, set`."
901+
|> addError com [] r
902+
903+
groupByGetter acc tail
904+
887905
| _ -> groupByGetter acc tail
888906

889907
let root = groupByGetter (MakePojoFromLambdaContext("/")) flattened

tests/Integration/Compiler/CompilerMessagesTests.fs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,36 @@ let tests =
4343
|> Assert.Exists.warningWith "Incomplete pattern matches on this expression"
4444
|> Assert.Exists.warningWith "The result of this expression has type 'int' and is implicitly ignored."
4545
|> ignore
46+
47+
testCase "Setting a non-property member in jsOptions results in specific error" <| fun _ ->
48+
let source =
49+
"""
50+
open Fable.Core.JsInterop
51+
52+
type Response =
53+
abstract fn: int -> int
54+
abstract prop: bool with get, set
55+
56+
let res = jsOptions<Response> (fun o -> o.fn <- (fun i -> i))
57+
"""
58+
compile source
59+
|> Assert.Exists.errorWith "Cannot set a non-property member in 'jsOptions'"
60+
|> ignore
61+
62+
testCase "Setting only settable properties in jsOptions succeeds" <| fun _ ->
63+
let source =
64+
"""
65+
open Fable.Core.JsInterop
66+
67+
type Response =
68+
abstract fnProp: (int -> int) with get, set
69+
abstract prop: bool with get, set
70+
71+
let res = jsOptions<Response> (fun o ->
72+
o.fnProp <- (fun i -> i)
73+
o.prop <- false)
74+
"""
75+
compile source
76+
|> Assert.Is.success
77+
|> ignore
4678
]

0 commit comments

Comments
 (0)