Skip to content

Commit 1a5065e

Browse files
charlesroddieclaude
andcommitted
Add reflection-free ToString tests for field shapes, structs, anon records and recursion
Covers DU field shapes (multiple fields vs a single tuple field), explicit vs unnamed field names rendering identically, struct unions/records, anonymous and struct anonymous records, and finite recursive/nesting types. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent dd3d75c commit 1a5065e

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/reflectionfree.fs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,106 @@ let main _ =
145145
|> withStdOutContains "custom-du"
146146
|> withStdOutContains "custom-record"
147147

148+
[<Fact>]
149+
let ``Union field shapes: multiple fields versus a single tuple field`` () =
150+
FSharp """
151+
module Test
152+
type TwoFields = | Two of int * int
153+
type OneTupleField = | OneTup of (int * int)
154+
type NamedFields = | Named of x: int * y: int
155+
156+
[<EntryPoint>]
157+
let main _ =
158+
Two (1, 2) |> string |> System.Console.WriteLine
159+
OneTup (1, 2) |> string |> System.Console.WriteLine // a single tuple field keeps its own parens
160+
Named (1, 2) |> string |> System.Console.WriteLine // named fields render positionally, names are not shown
161+
0
162+
"""
163+
|> asExe
164+
|> withOptions [ "--reflectionfree" ]
165+
|> compileExeAndRun
166+
|> shouldSucceed
167+
|> withStdOutContains "Two(1, 2)"
168+
|> withStdOutContains "OneTup((1, 2))"
169+
|> withStdOutContains "Named(1, 2)"
170+
171+
[<Fact>]
172+
let ``Explicit field names do not change the rendering`` () =
173+
FSharp """
174+
module Test
175+
type Labelled = | WithNames of first: int * second: string
176+
type Plain = | WithoutNames of int * string
177+
178+
[<EntryPoint>]
179+
let main _ =
180+
WithNames (1, "a") |> string |> System.Console.WriteLine
181+
WithoutNames (1, "a") |> string |> System.Console.WriteLine // unnamed fields render the same way as named ones
182+
0
183+
"""
184+
|> asExe
185+
|> withOptions [ "--reflectionfree" ]
186+
|> compileExeAndRun
187+
|> shouldSucceed
188+
|> withStdOutContains "WithNames(1, a)"
189+
|> withStdOutContains "WithoutNames(1, a)"
190+
191+
[<Fact>]
192+
let ``Struct unions and struct records get a generated ToString`` () =
193+
FSharp """
194+
module Test
195+
[<Struct>] type StructUnion = | SA of a: int
196+
[<Struct>] type StructRecord = { SX: int; SY: int }
197+
198+
[<EntryPoint>]
199+
let main _ =
200+
SA 7 |> string |> System.Console.WriteLine
201+
{ SX = 1; SY = 2 } |> string |> System.Console.WriteLine
202+
0
203+
"""
204+
|> asExe
205+
|> withOptions [ "--reflectionfree" ]
206+
|> compileExeAndRun
207+
|> shouldSucceed
208+
|> withStdOutContains "SA(7)"
209+
|> withStdOutContains "{ SX = 1; SY = 2 }"
210+
211+
[<Fact>]
212+
let ``Anonymous records get a generated single-line ToString`` () =
213+
FSharp """
214+
module Test
215+
[<EntryPoint>]
216+
let main _ =
217+
{| A = 1; B = "hi" |} |> string |> System.Console.WriteLine
218+
(struct {| A = 1; B = "hi" |}) |> string |> System.Console.WriteLine // a struct anonymous record renders identically
219+
0
220+
"""
221+
|> asExe
222+
|> withOptions [ "--reflectionfree" ]
223+
|> compileExeAndRun
224+
|> shouldSucceed
225+
|> withStdOutContains "{| A = 1; B = hi |}"
226+
227+
[<Fact>]
228+
let ``Recursively defined types render when the data is finite`` () =
229+
FSharp """
230+
module Test
231+
type Tree = | Leaf | Node of Tree * int * Tree
232+
type TreeNode = { Value: int; Parent: TreeNode option } // an upward-only parent pointer stays finite
233+
234+
[<EntryPoint>]
235+
let main _ =
236+
Node (Node (Leaf, 1, Leaf), 2, Leaf) |> string |> System.Console.WriteLine
237+
let root = { Value = 0; Parent = None }
238+
{ Value = 1; Parent = Some root } |> string |> System.Console.WriteLine
239+
0
240+
"""
241+
|> asExe
242+
|> withOptions [ "--reflectionfree" ]
243+
|> compileExeAndRun
244+
|> shouldSucceed
245+
|> withStdOutContains "Node(Node(Leaf, 1, Leaf), 2, Leaf)"
246+
|> withStdOutContains "{ Value = 1; Parent = Some({ Value = 0; Parent = null }) }"
247+
148248
[<Fact>]
149249
let ``No debug display attribute`` () =
150250
someCode

0 commit comments

Comments
 (0)