From cfe672b21d422b8254b3d5539df50c66c1e8078c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 30 Jul 2025 15:07:05 +0000
Subject: [PATCH 1/4] Initial plan
From d30ea01983b59e6c40a063abcf93a6ae4f3cddf5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 30 Jul 2025 15:33:44 +0000
Subject: [PATCH 2/4] Add automated XML documentation validation test for
FSharp.Core .fsi files
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.../FSharp.Core.UnitTests.fsproj | 1 +
.../FSharp.Core/XmlDocumentationValidation.fs | 110 ++++++++++++++++++
2 files changed, 111 insertions(+)
create mode 100644 tests/FSharp.Core.UnitTests/FSharp.Core/XmlDocumentationValidation.fs
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj
index b51337c3530..16e45542174 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj
@@ -88,6 +88,7 @@
+
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/XmlDocumentationValidation.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/XmlDocumentationValidation.fs
new file mode 100644
index 00000000000..3010ef7e95f
--- /dev/null
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core/XmlDocumentationValidation.fs
@@ -0,0 +1,110 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+module FSharp.Core.UnitTests.XmlDocumentationValidation
+
+open System
+open System.IO
+open System.Text.RegularExpressions
+open System.Xml
+open Xunit
+
+/// Extracts XML documentation blocks from F# signature files
+let extractXmlDocBlocks (content: string) =
+ // Regex to match XML documentation comments (/// followed by XML content)
+ let xmlDocPattern = @"^\s*///\s*(.*)$"
+ let regex = Regex(xmlDocPattern, RegexOptions.Multiline)
+
+ let lines = content.Split([|'\n'; '\r'|], StringSplitOptions.RemoveEmptyEntries)
+ let mutable xmlBlocks = []
+ let mutable currentBlock = []
+ let mutable lineNumber = 0
+
+ for line in lines do
+ lineNumber <- lineNumber + 1
+ let trimmedLine = line.Trim()
+ if trimmedLine.StartsWith("///") then
+ let xmlContent = trimmedLine.Substring(3).Trim()
+ currentBlock <- (xmlContent, lineNumber) :: currentBlock
+ else
+ if not (List.isEmpty currentBlock) then
+ xmlBlocks <- List.rev currentBlock :: xmlBlocks
+ currentBlock <- []
+
+ // Don't forget the last block if file ends with XML comments
+ if not (List.isEmpty currentBlock) then
+ xmlBlocks <- List.rev currentBlock :: xmlBlocks
+
+ List.rev xmlBlocks
+
+/// Validates that XML content is well-formed
+let validateXmlBlock (xmlLines: (string * int) list) =
+ if List.isEmpty xmlLines then
+ Ok ()
+ else
+ let xmlContent = xmlLines |> List.map fst |> String.concat "\n"
+ let firstLineNumber = xmlLines |> List.head |> snd
+
+ // Skip empty or whitespace-only blocks
+ if String.IsNullOrWhiteSpace(xmlContent) then
+ Ok ()
+ else
+ try
+ // Wrap content in a root element to make it valid XML document
+ let wrappedXml = sprintf "%s" xmlContent
+ let doc = XmlDocument()
+ doc.LoadXml(wrappedXml)
+ Ok ()
+ with
+ | :? XmlException as ex ->
+ Error (sprintf "Line %d: Invalid XML - %s" firstLineNumber ex.Message)
+ | ex ->
+ Error (sprintf "Line %d: XML parsing error - %s" firstLineNumber ex.Message)
+
+/// Gets all .fsi files in FSharp.Core directory
+let getFSharpCoreFsiFiles () =
+ let coreDir = Path.Combine(__SOURCE_DIRECTORY__, "..", "..", "..", "src", "FSharp.Core")
+ let fullPath = Path.GetFullPath(coreDir)
+ if Directory.Exists(fullPath) then
+ Directory.GetFiles(fullPath, "*.fsi", SearchOption.AllDirectories)
+ |> Array.toList
+ else
+ []
+
+[]
+let ``XML documentation in FSharp.Core fsi files should be well-formed`` () =
+ let fsiFiles = getFSharpCoreFsiFiles()
+
+ Assert.False(List.isEmpty fsiFiles, "No .fsi files found in FSharp.Core directory")
+
+ let mutable errors = []
+ let mutable totalBlocks = 0
+
+ for fsiFile in fsiFiles do
+ let relativePath = Path.GetFileName(fsiFile)
+ try
+ let content = File.ReadAllText(fsiFile)
+ let xmlBlocks = extractXmlDocBlocks content
+
+ for xmlBlock in xmlBlocks do
+ totalBlocks <- totalBlocks + 1
+ match validateXmlBlock xmlBlock with
+ | Ok () -> ()
+ | Error errorMsg ->
+ let error = sprintf "%s: %s" relativePath errorMsg
+ errors <- error :: errors
+ with
+ | ex ->
+ let error = sprintf "%s: Failed to read file - %s" relativePath ex.Message
+ errors <- error :: errors
+
+ // Report statistics
+ let validBlocks = totalBlocks - List.length errors
+ let message = sprintf "Validated %d XML documentation blocks in %d .fsi files. %d valid, %d invalid."
+ totalBlocks (List.length fsiFiles) validBlocks (List.length errors)
+
+ if not (List.isEmpty errors) then
+ let errorDetails = errors |> List.rev |> String.concat "\n"
+ Assert.Fail(sprintf "%s\n\nErrors:\n%s" message errorDetails)
+ else
+ // This will show in test output for successful runs
+ Assert.True(true, message)
\ No newline at end of file
From c8f0f09c69642cf0d6cbe95670238325ee78bf09 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 31 Jul 2025 09:11:20 +0000
Subject: [PATCH 3/4] Add intentional XML documentation error to demonstrate
test failure
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
src/FSharp.Core/option.fsi | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/FSharp.Core/option.fsi b/src/FSharp.Core/option.fsi
index db381aec8ad..37385b92164 100644
--- a/src/FSharp.Core/option.fsi
+++ b/src/FSharp.Core/option.fsi
@@ -64,7 +64,12 @@ module Option =
/// The input option.
///
/// The option if the option is Some, else the result of evaluating .
- /// is not evaluated unless is None.
+ /// is not evaluated unless is None.
+ ///
+ /// Additional text here
+ ///
+ /// More text
+ ///
///
///
///
From 84e20ecdb279f2b001d9d807f414d9be11c01cb4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 31 Jul 2025 10:28:32 +0000
Subject: [PATCH 4/4] Revert intentional XML documentation error in option.fsi
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
src/FSharp.Core/option.fsi | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/FSharp.Core/option.fsi b/src/FSharp.Core/option.fsi
index 37385b92164..db381aec8ad 100644
--- a/src/FSharp.Core/option.fsi
+++ b/src/FSharp.Core/option.fsi
@@ -64,12 +64,7 @@ module Option =
/// The input option.
///
/// The option if the option is Some, else the result of evaluating .
- /// is not evaluated unless is None.
- ///
- /// Additional text here
- ///
- /// More text
- ///
+ /// is not evaluated unless is None.
///
///
///