Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ce6fd0d
Initial plan
Copilot Jan 2, 2026
20291f3
WIP: Add XML documentation infrastructure (incomplete - needs more work)
Copilot Jan 2, 2026
0916227
Add initial infrastructure for `<inheritdoc>` XML documentation support
Copilot Jan 2, 2026
8882d88
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 6, 2026
f0471a5
Fix build warnings: parameter name mismatch and unused parameter
Copilot Jan 6, 2026
d01033e
Fix unused parameter error: mark infoReader as intentionally unused
Copilot Jan 6, 2026
7fb1835
Fix FS3218 warning: update signature to match implementation with _m …
Copilot Jan 7, 2026
0d42ef3
Add detailed SPEC-TODO.MD with implementation checklist based on MS L…
Copilot Jan 7, 2026
1cf9a6c
Impl added
T-Gro Jan 8, 2026
5aa295b
next batch of impls
T-Gro Jan 12, 2026
ddee485
inherits impl
T-Gro Jan 12, 2026
1b8e242
Deduplicate with go to definition
T-Gro Jan 13, 2026
14a1f05
fixing warnings
T-Gro Jan 13, 2026
e1fb343
Apply patch from /run xlf
actions-user Jan 13, 2026
c685017
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 13, 2026
5ab6249
Apply suggestions from code review
T-Gro Jan 13, 2026
b06ccdd
Apply suggestions from code review
T-Gro Jan 14, 2026
d509cb6
Update Range.Zero to Range.range0 in XmlDoc tests
T-Gro Jan 15, 2026
b525f97
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 15, 2026
5c8f7e3
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 16, 2026
f79cbcd
Apply patch from /run fantomas
actions-user Jan 16, 2026
d96f3d2
Apply patch from /run test-baseline
actions-user Jan 16, 2026
2846e4e
Apply patch from /run ilverify
actions-user Jan 16, 2026
6a1b5da
Implement `<inheritdoc>` XML documentation support for F#
Copilot Jan 16, 2026
22f088c
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 19, 2026
39a9cdb
Non hardcoded resolution of external assembly ref sigs
T-Gro Jan 19, 2026
1bce792
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 19, 2026
b368155
FIx resolution of external .xml files for .dll imports
T-Gro Jan 19, 2026
34be62d
Adjust tests
T-Gro Jan 20, 2026
ac0806f
Apply patch from /run fantomas
actions-user Jan 20, 2026
c1b885a
Merge branch 'main' into copilot/support-xmldoc-inherit-element
T-Gro Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/skills/honest-planner/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
name: honest-planner
description: Triggers when summarizing actions, claiming completion, reporting what is done vs what is missing, responding to "what's missing?" or "what was implemented?" questions, providing progress reports, submitting subtasks, individual items, or any form of status update. Also triggers when thinking work is done or about to declare victory.
---

# Honest Planner

## Core Principle

**Absolute honesty. Zero bullshit. Full disclosure.**

Partial success honestly told is infinitely more valuable than a pile of decorated lies.

## Before Reporting Progress

STOP. Ask yourself:

1. What ACTUALLY works right now? (Tested, verified, not assumed)
2. What is COMPLETELY missing? (Not started, not even stubbed)
3. What is PARTIALLY done? (Started but broken, untested, or incomplete)
4. What did I CLAIM would work but haven't verified?

## Progress Reporting Rules

### DO

- Show a single, honest progress bar for THE ENTIRE FEATURE
- List MISSING items FIRST, prominently, with clear ❌ markers
- Be specific: "Method X does not resolve cref Y" not "mostly works"
- Quantify: "3 of 7 scenarios pass" not "good progress"
- Admit unknowns: "I haven't tested Z" or "I don't know if W works"

### DO NOT

- Celebrate phases when the overall feature is incomplete
- Bury missing items in walls of text
- Use green checkboxes for things that are merely "started"
- Say "works" without having run a test
- Claim something is "low priority" to hide that it's missing
- Use weasel words: "mostly", "generally", "should work"

## Required Format for Status Reports

```
OVERALL: X% Complete
[visual progress bar]

MISSING (Critical):
❌ Feature A - not implemented
❌ Feature B - started but fails test X

MISSING (Lower Priority):
❌ Feature C - edge case
❌ Feature D - nice to have

WORKING (Verified):
✅ Feature E - tested with Y
✅ Feature F - 3 tests pass
```

## Red Flags - If You Catch Yourself Doing These, STOP

- Writing more than 2 lines about what works before mentioning what's missing
- Using phrases like "the main use cases work" without defining what's missing
- Putting ✅ next to something you haven't tested
- Saying "implementation complete" when there are known gaps
- Celebrating "20 tests pass" without mentioning the 5 that fail

## The Honesty Test

Before submitting any progress report, answer:

> "If someone read ONLY the first 3 lines of my response, would they have an accurate picture of the overall state?"

If no, rewrite. Lead with the truth.

## Examples

### BAD (Dishonest)

```
Great progress! ✅ Types work ✅ Methods work ✅ Properties work
The implementation is nearly complete. Just a few edge cases remaining.
```

### GOOD (Honest)

```
OVERALL: 60% Complete
████████████░░░░░░░░

MISSING:
❌ Methods with implicit inheritdoc - NOT IMPLEMENTED (most common use case)
❌ Property cref resolution - NOT IMPLEMENTED
❌ XML file output for members - NOT IMPLEMENTED

WORKING:
✅ Types with explicit cref - 5 tests pass
✅ Types with implicit - 3 tests pass
```

## Remember

The user is not stupid. They will find out.
Lying now just wastes everyone's time later.
Respect them with the truth.
102 changes: 102 additions & 0 deletions SPEC-TODO.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# `<inheritdoc>` XML Documentation Implementation

## OVERALL PROGRESS: 98% Complete

```
Core InheritDoc Logic: ████████████████████ 100% (48 inherit tests passing)
Tooltip Integration: ████████████████████ 100% (54 tests passing)
XmlDoc Tests: ████████████████████ 100% (21 tests passing)
Build (macOS): ████████████████████ 100% (0 warnings, 0 errors)
Nullness Warnings: ████████████████████ 100% (fixed)
Windows Build Verification: ░░░░░░░░░░░░░░░░░░░░ 0% (cannot verify on macOS)
```

**VERIFIED ON macOS (2025-01-13):**
- Build: 0 warnings, 0 errors
- InheritDoc tests: 48 passing
- Tooltip tests: 54 passing
- XmlDoc tests: 21 passing

### REMAINING WORK
1. ❌ **Windows verification needed**: FSharp.Editor.Tests requires .NET Framework 4.7.2
2. ⚠️ **PR review**: Code needs review before merge

---

## FEATURE SUMMARY

### What Works

| Feature | Status | Test Coverage |
|---------|--------|---------------|
| `<inheritdoc cref="T:Namespace.Type"/>` on types | ✅ Works | Yes |
| `<inheritdoc/>` on type with base class | ✅ Works | Yes |
| `<inheritdoc/>` on type implementing interface | ✅ Works | Yes |
| `<inheritdoc/>` on method implementing interface | ✅ Works | Yes |
| `<inheritdoc/>` on override method | ✅ Works | Yes |
| `<inheritdoc/>` on property implementing interface | ✅ Works | Yes |
| `<inheritdoc cref="M:..."/>` explicit method cref | ✅ Works | Yes |
| `<inheritdoc cref="P:..."/>` explicit property cref | ✅ Works | Yes |
| Generic type cref `T:Foo\`1` | ✅ Works | Yes |
| Nested type cref `T:Outer+Inner` | ✅ Works | Yes |
| `path` attribute XPath filtering | ✅ Works | Yes |
| Cycle detection | ✅ Works | Yes |
| Error warnings on resolution failure | ✅ Works | Implicit |
| External assembly types (System.*) | ✅ Works | Yes |
| FSharp.Core types | ✅ Works | Yes |
| Same-compilation types | ✅ Works | Yes |
| Cross-module resolution | ✅ Works | Yes |
| Record types | ✅ Works | Yes |
| Discriminated unions | ✅ Works | Yes |
| Nested inheritance chains | ✅ Works | Yes |

### Performance Profile

- **Zero overhead when not using `<inheritdoc>`**: Early exit on `doc.IsEmpty` and `hasInheritDoc` string check
- **Lazy expansion**: Only expands when XmlDoc is accessed
- **Cycle detection**: Prevents infinite recursion with visited set

---

## FILES CHANGED

| File | Purpose |
|------|---------|
| `src/Compiler/Symbols/XmlDocInheritance.fs` | Core expansion logic, cref parsing, resolution |
| `src/Compiler/Symbols/XmlDocInheritance.fsi` | Public API signature |
| `src/Compiler/Symbols/XmlDocSigParser.fs` | Doc comment ID parsing (shared module) |
| `src/Compiler/Symbols/XmlDocSigParser.fsi` | Parser signature |
| `src/Compiler/Symbols/Symbols.fs` | FSharpEntity/FSharpMemberOrFunctionOrValue XmlDoc expansion |
| `src/Compiler/Symbols/SymbolHelpers.fs` | Tooltip text expansion |
| `src/Compiler/Driver/XmlDocFileWriter.fs` | XML file output with expansion |
| `src/Compiler/Checking/InfoReader.fs` | TryFindXmlDocByAssemblyNameAndSig helper |
| `src/Compiler/Checking/InfoReader.fsi` | InfoReader signature |
| `src/Compiler/Checking/PostInferenceChecks.fs` | Member implicit target resolution |
| `src/Compiler/FSComp.txt` | Error messages |
| `tests/FSharp.Compiler.Service.Tests/XmlDocTests.fs` | 23 comprehensive tests |

---

## COMPLETED DEDUPLICATION

✅ **GoToDefinition.fs now uses XmlDocSigParser**: The `DocCommentIdToPath` method in `vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs` has been refactored to use the shared `XmlDocSigParser.parseDocCommentId` from `FSharp.Compiler.Symbols`. This eliminated ~80 lines of duplicated regex logic.

## KNOWN LIMITATIONS / FUTURE WORK

1. **XML file generation for implicit members**: The XML file writer expands docs at the entity level. Member-level implicit expansion is handled but relies on tooltip path.

---

## VERIFICATION COMMANDS

```bash
# Build
export BUILDING_USING_DOTNET=true
dotnet build src/Compiler/FSharp.Compiler.Service.fsproj -c Debug -f net10.0

# Run all InheritDoc tests
dotnet test tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj -c Debug -f net10.0 --filter "FullyQualifiedName~InheritDoc"

# Run all XML tests
dotnet test tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj -c Debug -f net10.0 --filter "FullyQualifiedName~Xml"
```
69 changes: 65 additions & 4 deletions src/Compiler/Driver/XmlDocFileWriter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ module internal FSharp.Compiler.XmlDocFileWriter

open System.IO
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.InfoReader
open FSharp.Compiler.IO
open FSharp.Compiler.Syntax
open FSharp.Compiler.XmlDocInheritance
open FSharp.Compiler.Text
open FSharp.Compiler.Xml
open FSharp.Compiler.TypedTree
Expand Down Expand Up @@ -77,18 +80,76 @@ module XmlDocWriter =

doModuleSig None generatedCcu.Contents

let WriteXmlDocFile (g, assemblyName, generatedCcu: CcuThunk, xmlFile) =
let WriteXmlDocFile (g, infoReader: InfoReader, assemblyName, generatedCcu: CcuThunk, xmlFile) =
if not (FileSystemUtils.checkSuffix xmlFile "xml") then
error (Error(FSComp.SR.docfileNoXmlSuffix (), Range.rangeStartup))

let mutable members = []

let addMember id xmlDoc =
/// Compute implicit target cref for a member from its ImplementedSlotSigs
/// Returns something like "M:Namespace.IInterface.MethodName" for interface implementations
let computeImplicitTargetCref (v: Val) : string option =
match v.ImplementedSlotSigs with
| slotSig :: _ ->
// Get the declaring interface/base class type
let declaringType = slotSig.DeclaringType
let methodName = slotSig.Name

// Try to get the type reference
match tryTcrefOfAppTy g declaringType with
| ValueSome tcref ->
// Build the full type path
let typePath =
match tcref.CompilationPathOpt with
| Some cp ->
let accessPath = cp.AccessPath |> List.map fst |> String.concat "."

if accessPath.Length = 0 then
tcref.CompiledName
else
accessPath + "." + tcref.CompiledName
| None -> tcref.CompiledName
// Determine if this is a method or property
match v.MemberInfo with
| Some memberInfo ->
match memberInfo.MemberFlags.MemberKind with
| SynMemberKind.PropertyGet
| SynMemberKind.PropertySet
| SynMemberKind.PropertyGetSet ->
// For properties, use P: prefix and the property name
Some("P:" + typePath + "." + v.PropertyName)
| _ ->
// For methods, use M: prefix
Some("M:" + typePath + "." + methodName)
| None -> Some("M:" + typePath + "." + methodName)
| ValueNone -> None
| [] -> None

let addMemberWithImplicitTarget id xmlDoc implicitTargetOpt =
if hasDoc xmlDoc then
let doc = xmlDoc.GetXmlText()
// Expand <inheritdoc> elements before writing to XML file
// Pass the generatedCcu for same-compilation type resolution
let ccuMtyp = generatedCcu.Contents.ModuleOrNamespaceType

let expandedDoc =
XmlDocInheritance.expandInheritDoc
(Some infoReader)
(Some generatedCcu)
(Some ccuMtyp)
implicitTargetOpt
Range.rangeStartup
Set.empty
xmlDoc

let doc = expandedDoc.GetXmlText()
members <- (id, doc) :: members

let doVal (v: Val) = addMember v.XmlDocSig v.XmlDoc
let addMember id xmlDoc =
addMemberWithImplicitTarget id xmlDoc None

let doVal (v: Val) =
let implicitTarget = computeImplicitTargetCref v
addMemberWithImplicitTarget v.XmlDocSig v.XmlDoc implicitTarget

let doField (rf: RecdField) = addMember rf.XmlDocSig rf.XmlDoc

Expand Down
3 changes: 2 additions & 1 deletion src/Compiler/Driver/XmlDocFileWriter.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

module internal FSharp.Compiler.XmlDocFileWriter

open FSharp.Compiler.InfoReader
open FSharp.Compiler.TypedTree
open FSharp.Compiler.TcGlobals

Expand All @@ -15,4 +16,4 @@ module XmlDocWriter =

/// Writes the XmlDocSig property of each element (field, union case, etc)
/// of the specified compilation unit to an XML document in a new text file.
val WriteXmlDocFile: g: TcGlobals * assemblyName: string * generatedCcu: CcuThunk * xmlFile: string -> unit
val WriteXmlDocFile: g: TcGlobals * infoReader: InfoReader * assemblyName: string * generatedCcu: CcuThunk * xmlFile: string -> unit
3 changes: 2 additions & 1 deletion src/Compiler/Driver/fsc.fs
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,8 @@ let main2
tcConfig.xmlDocOutputFile
|> Option.iter (fun xmlFile ->
let xmlFile = tcConfig.MakePathAbsolute xmlFile
XmlDocWriter.WriteXmlDocFile(tcGlobals, assemblyName, generatedCcu, xmlFile))
let infoReader = InfoReader(tcGlobals, tcImports.GetImportMap())
XmlDocWriter.WriteXmlDocFile(tcGlobals, infoReader, assemblyName, generatedCcu, xmlFile))

// Pass on only the minimum information required for the next phase
Args(
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,7 @@ forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable
3390,xmlDocDuplicateParameter,"This XML comment is invalid: multiple documentation entries for parameter '%s'"
3390,xmlDocUnresolvedCrossReference,"This XML comment is invalid: unresolved cross-reference '%s'"
3390,xmlDocMissingParameter,"This XML comment is incomplete: no documentation for parameter '%s'"
3390,xmlDocInheritDocError,"This XML comment is invalid: inheritdoc error: %s"
3391,tcImplicitConversionUsedForNonMethodArg,"This expression uses the implicit conversion '%s' to convert type '%s' to type '%s'. See https://aka.ms/fsharp-implicit-convs. This warning may be disabled using '#nowarn \"3391\"."
3392,containerDeprecated,"The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead."
3393,containerSigningUnsupportedOnThisPlatform,"Key container signing is not supported on this platform."
Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@
<Compile Include="Driver\CompilerOptions.fs" />
<Compile Include="Driver\OptimizeInputs.fsi" />
<Compile Include="Driver\OptimizeInputs.fs" />
<Compile Include="Symbols\XmlDocSigParser.fsi" />
<Compile Include="Symbols\XmlDocSigParser.fs" />
<Compile Include="Symbols\XmlDocInheritance.fsi" />
<Compile Include="Symbols\XmlDocInheritance.fs" />
<Compile Include="Driver\XmlDocFileWriter.fsi" />
<Compile Include="Driver\XmlDocFileWriter.fs" />
<Compile Include="Driver\BinaryResourceFormats.fsi" />
Expand Down
8 changes: 7 additions & 1 deletion src/Compiler/Symbols/SymbolHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ open FSharp.Compiler.AbstractIL.Diagnostics
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.InfoReader
open FSharp.Compiler.Infos
open FSharp.Compiler.XmlDocInheritance
open FSharp.Compiler.IO
open FSharp.Compiler.NameResolution
open FSharp.Compiler.Syntax.PrettyNaming
Expand Down Expand Up @@ -343,7 +344,12 @@ module internal SymbolHelpers =
let GetXmlCommentForItemAux (xmlDoc: XmlDoc option) (infoReader: InfoReader) m d =
match xmlDoc with
| Some xmlDoc when not xmlDoc.IsEmpty ->
FSharpXmlDoc.FromXmlText xmlDoc
// Get the CCU of the item for same-compilation resolution
let ccuOpt = ccuOfItem infoReader.g d
// Expand <inheritdoc> elements for tooltips (design-time)
// Pass None for currentModuleType and implicitTargetCref
let expandedDoc = expandInheritDoc (Some infoReader) ccuOpt None None m Set.empty xmlDoc
FSharpXmlDoc.FromXmlText expandedDoc
| _ -> GetXmlDocHelpSigOfItemForLookup infoReader m d

let GetXmlCommentForMethInfoItem infoReader m d (minfo: MethInfo) =
Expand Down
Loading
Loading