From 37d308ec759cf64f7f1ab4c31b91afcc13054a97 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Thu, 10 Sep 2026 13:15:35 +0200 Subject: [PATCH] Skip union case tester properties in API docs (#1313) The compiler synthesizes an IsCase property for every union case. They cannot carry XML documentation, so they were listed as undocumented members on union pages and FsDocsWarnOnMissingDocs reported FD0001 for each one, about half of all warnings for a union-heavy assembly. Treat IsUnionCaseTester like IsCompilerGenerated when reading members, so the testers are neither documented nor reported as missing. --- RELEASE_NOTES.md | 1 + src/FSharp.Formatting.ApiDocs/SymbolReader.fs | 14 ++++++++++---- tests/FSharp.ApiDocs.Tests/ApiDocsTests.fs | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index abac577c5..f50a9695c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,7 @@ ### Fixed * Fix tooltip not being interactive: moving the mouse from a code token into its tooltip now keeps the tooltip open, allowing users to select and copy the tooltip text. [#949](https://github.com/fsprojects/FSharp.Formatting/issues/949) +* The compiler-generated `IsCase` union case tester properties are no longer listed as members of a union type, and `FsDocsWarnOnMissingDocs` no longer reports `FD0001` for them: they cannot carry XML documentation. [#1313](https://github.com/fsprojects/FSharp.Formatting/issues/1313) * `fsdocs watch` rebuilds a page when a file its script depends on changes, following `#load` transitively and `#r` to local files, wherever those files are (a dot folder, outside the input folder). The directives are read from the syntax tree, so a `#load` in a comment does not count. [#1309](https://github.com/fsprojects/FSharp.Formatting/issues/1309) ## [23.0.0-alpha.1] - 2026-09-08 diff --git a/src/FSharp.Formatting.ApiDocs/SymbolReader.fs b/src/FSharp.Formatting.ApiDocs/SymbolReader.fs index 019226f8e..c250cb56d 100644 --- a/src/FSharp.Formatting.ApiDocs/SymbolReader.fs +++ b/src/FSharp.Formatting.ApiDocs/SymbolReader.fs @@ -836,13 +836,19 @@ module internal SymbolReader = ctx.WarnOnMissingDocs )) + /// Members the compiler synthesizes for the user: compiler-generated members and the + /// IsCase union case tester properties. They cannot carry XML documentation, so they + /// are neither documented nor reported as missing documentation. + let isSynthesizedMember (v: FSharpMemberOrFunctionOrValue) = + v.IsCompilerGenerated || v.IsUnionCaseTester + /// Reads all members in a sequence without filtering out any by kind. let readAllMembers ctx entityUrl kind (members: FSharpMemberOrFunctionOrValue seq) = members |> Seq.choose (fun v -> if checkAccess ctx v.Accessibility - && not v.IsCompilerGenerated + && not (isSynthesizedMember v) && not v.IsPropertyGetterMethod && not v.IsPropertySetterMethod && not v.IsEventAddMethod @@ -858,7 +864,7 @@ module internal SymbolReader = let readMembers ctx entityUrl kind (entity: FSharpEntity) cond = entity.MembersFunctionsAndValues |> Seq.choose (fun v -> - if checkAccess ctx v.Accessibility && not v.IsCompilerGenerated && cond v then + if checkAccess ctx v.Accessibility && not (isSynthesizedMember v) && cond v then tryReadMember ctx entityUrl kind v else None) @@ -1053,7 +1059,7 @@ module internal SymbolReader = bdef.MembersFunctionsAndValues |> Seq.filter (fun v -> checkAccess ctx v.Accessibility - && not v.IsCompilerGenerated + && not (isSynthesizedMember v) && not v.IsOverrideOrExplicitInterfaceImplementation && not v.IsEventAddMethod && not v.IsEventRemoveMethod @@ -1088,7 +1094,7 @@ module internal SymbolReader = getMembers typ |> Seq.filter (fun v -> checkAccess ctx v.Accessibility - && not v.IsCompilerGenerated + && not (isSynthesizedMember v) && not v.IsOverrideOrExplicitInterfaceImplementation && not v.IsEventAddMethod && not v.IsEventRemoveMethod diff --git a/tests/FSharp.ApiDocs.Tests/ApiDocsTests.fs b/tests/FSharp.ApiDocs.Tests/ApiDocsTests.fs index 05ba18177..3d41ec98f 100644 --- a/tests/FSharp.ApiDocs.Tests/ApiDocsTests.fs +++ b/tests/FSharp.ApiDocs.Tests/ApiDocsTests.fs @@ -302,6 +302,10 @@ let ``ApiDocs works on two sample F# assemblies`` (format: OutputFormat) = files.[(sprintf "fslib-union.%s" format.Extension)] |> shouldContainText "Hello of int" + // The compiler-generated `IsCase` union case testers are not documented (#1313) + files.[(sprintf "fslib-union.%s" format.Extension)] + |> shouldNotContainText "IsHello" + files.[(sprintf "fslib.%s" format.Extension)] |> shouldContainText "Sample class"