Skip to content

Add support for <include> XML documentation tag - #19186

Merged
T-Gro merged 42 commits into
mainfrom
copilot/add-include-xml-support
Aug 6, 2026
Merged

Add support for <include> XML documentation tag#19186
T-Gro merged 42 commits into
mainfrom
copilot/add-include-xml-support

Conversation

CopilotAI commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

Support for the <include> XML documentation tag

Implements the C#-style <include file="..." path="..."/> XML documentation tag for F# (part of #19175). At compile time, when the XML documentation file is generated (--doc), each <include> directive is replaced by the XML element(s) that its XPath query selects from the referenced external file. This is a text-in / text-out transformation on the generated documentation; it does not affect IDE tooltips (unlike <inheritdoc>, which remains unsupported and out of scope).

Design: RFC FS-1341.

Behaviour

  • Resolves file (absolute, or relative to the source file, then the compiler working directory), evaluates the path XPath against the loaded document, and substitutes the matched elements.
  • Recursive: included fragments may themselves contain <include> directives (chains and diamond DAGs), expanded depth-first.
  • Cycle detection keyed on the (resolved file, xpath) pair: a self/mutual cycle warns (FS3905) and keeps the unexpanded tag instead of looping.
  • Bounded: a nesting-depth cap (64) and a per-documentation-comment expansion budget (10000) guard against runaway/adversarial expansion; each warns at most once per comment.
  • Zero matches: Roslyn parity, a comment is emitted and the tag is kept, with no warning.
  • Diagnostics (FS3905) name both the file and the xpath, using a Roslyn CS1589-style frame, so a comment with several includes is diagnosable.
  • Security: external files are loaded with DTD processing prohibited and no external resolver (blocks billion-laughs / XXE / external DTD subsets); whitespace is preserved.
  • Runs only on the --doc writer path (and, when --warnon:3390 is set, the quiet doc-checking validation path). The default type-check / IDE path performs no file IO.

CopilotAI changed the title [WIP] Add support for <include> XML documentation tagAdd support for <include> XML documentation tagDec 31, 2025
CopilotAI requested a review from T-GroDecember 31, 2025 16:59
@github-actions

github-actionsBot commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change pathRelease notes pathDescription
`src/Compiler`docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

T-Groand others added 13 commits July 24, 2026 16:37
Add depth and budget guards for XML doc include expansion, share a per-pass expansion environment across doc-file generation, and memoize resolved include fragments with cloning.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use ordinal include path comparison, remove fragment memoization, and reset include budgets per XML doc comment while preserving the shared per-pass file cache.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Emit the per-document include expansion-budget warning at most once
instead of once per over-budget include, avoiding warning spam when a
single doc comment contains many more than the budgeted number of
includes. Every unexpanded include tag is still preserved.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…tic tests
Address Phase 8 review gate:
- Move the Roslyn CS1589-style include-error frame into a proper localizable
FSComp message (xmlDocIncludeError2, %s %s %s) instead of interpolating it in
code, matching the F# diagnostic convention and Roslyn's own localized frame;
add matching trans-units to all FSComp .xlf files. Rendered text is unchanged.
- Drop the throwaway Result.Error payload in resolveSingleInclude in favour of
a plain Some/None resolution.
- Assert the 'names both file and xpath' guarantee across the remaining failure
classes (circular, depth cap, budget, empty xpath, DTD).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The two 'Fixed' entries described hardening of behavior that never
shipped; they are part of delivering the brand-new <include> feature,
not fixes to released behavior. Fold into the single Added entry and
note that <inheritdoc> remains unsupported.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Fills the last Task 10.1 error-class gap: an external file that is not
well-formed XML must not crash the compiler. Asserts a single FS3887 that
names both file and xpath, that the unexpanded <include> tag is kept, and
that no partial malformed content leaks into the output.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Phase 10 review-gauntlet remediation.
Correctness (blocking): classifyInclude matched on LocalName only, so an
element named 'include' in a foreign XML namespace (e.g. <include
xmlns="urn:...">) was hijacked and expanded. It is now required to be
unqualified, matching Roslyn's ElementNameIs (which checks the namespace
is empty). A foreign-namespace element is ordinary content and is left
untouched. Added a RED->GREEN test; real unqualified includes are
unaffected.
Compactness:
* resolveFilePath collapses to GetFullFilePathInDirectoryShim over
GetDirectoryNameShim: the shim already passes rooted paths through and
maps an empty/dir-less/"unknown" base name to "." (the current
directory), so the hand-rolled IsPathRooted + CurrentDirectory branches
were redundant. Drops the now-unused open System.IO.
* Removed three unused test-framework helpers (withParamChecking,
memberExists, memberXmlAbsent).
Include suite 60/60, XmlCommentChecking 15/15.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Convergence-review follow-up. The prior one-line simplification called
GetDirectoryNameShim on the base file name unconditionally, which throws
for a virtual/sentinel range filename containing an illegal path char
(caught and downgraded to 'the file path is invalid'). An absolute
include path must not depend on the base file name, so restore the
rooted short-circuit. The relative case keeps the compacted form; this
is now behaviorally identical to the original for all inputs while still
dropping the redundant current-directory fallback branches.
Include suite 60/60.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolve conflicts and adapt the <include> feature to upstream changes:
- ProjectAnalysisTests.fs: trivial comment conflict in the unrelated
ProjectActivePatternInSig module; take main's canonical wording.
- FSComp.txt: main claimed FS3887 for the new ilCustomAttrInvalidArrayElemType
diagnostic, colliding with the <include> feature's original 3887. Renumber
the two include messages (xmlDocIncludeError / xmlDocIncludeError2) to the
next free number FS3891, matching the shared-number convention of the
neighbouring xmlDoc diagnostics (3390). Propagate 3891 to the include test
assertions and the FS3887 references in XmlDoc.fs / .fsi comments.
Repair several silent auto-merge clobbers where my branch's stale base
overwrote content main added since the branch point:
- FSharp.Compiler.Service.fsproj: restored 6 dropped <Compile> entries
(EncMethodDebugInformation, CheckComputationExpressionsCustomOps,
CompilerGeneratedNameMapState, GeneratedNames, SynthesizedTypeMaps) by
taking main's project and re-applying only XmlDocIncludeExpander.fsi/.fs.
- FSharp.Compiler.ComponentTests.fsproj: restored ~30 dropped test <Compile>
entries; re-applied only Miscellaneous/XmlDocInclude.fs.
- FSharp.Test.Utilities.fsproj: restored ~92 unrelated changes incl. an
OpenTelemetry package downgrade (1.15.3 -> 1.11.2); re-applied only
XmlDocIncludeTestFramework.fs.
- release-notes/11.0.100.md: restored 105 dropped entries from other
contributors; re-applied only the single <include> "Added" entry.
xlf trans-units are keyed by identifier (not number) so they are unaffected.
Verified: ./build.sh -c Release 0W/0E; include suite 60/60; XmlCommentChecking
23/23; main's FS3887 attribute test 12/12. Total delta vs main is exactly the
26 <include> feature files.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
- FSComp.txt: relocate the two xmlDocIncludeError entries (3891) to after
3890 so error codes stay monotonically non-decreasing (FSCompCheck.fsx).
- XmlDocIncludeExpander.fs: use persistent Set for the in-progress cycle
set (drops the O(n) HashSet clone per recursion descent); use
XName.Get instead of !!(XName.op_Implicit ...).
- XmlDocIncludeTestFramework.fs: make tryMemberInner private.
- Compiler.fs: strip trailing whitespace on the added withXmlDoc helper lines.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
String.Contains(char) only exists on .NET Core 2.1+. FSharp.Test.Utilities
also targets net472 on Windows CI, where only Contains(string) is available,
so 't.Value.Contains '\n'' failed to compile (FS0001 expected string, got
char). The local macOS build only compiles the net10.0 leg, so it was missed.
Use string literals, which compile on every target framework.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…to 3892; repair fsproj + release-note clobbers
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@T-GroT-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is green and the earlier review comments are addressed. LGTM.

@T-Gro

T-Gro commented Aug 3, 2026

Copy link
Copy Markdown
Member

@abonie this is ready for your review when you have a moment — CI is green, and it's a compile-time-only, text-in/text-out <include> expansion with a full test battery.

T-Groand others added 3 commits August 4, 2026 15:39
RFC FS-1341 / C# XmlFileResolver parity: a relative include file is now
resolved next to the including source file first, then against the compiler
working directory. Also lock signature-file include resolution with a test.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
main added the record-spread messages (FS3892-FS3904) while this branch was in
flight, colliding with the <include> error code. Move both include messages to
the next free code (3905) so FSComp.txt stays sorted.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
T-Groand others added 3 commits August 5, 2026 09:23
Comment-only follow-up to the 3892->3905 renumber: two doc/inline comments
still named the old error code.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Production (src/Compiler/SyntaxTree):
- Drop restating/narration comments on the include expander; keep only the two
genuine why-comments (evaluateXPath lazy-materialize-inside-try, classifyInclude
foreign-namespace parity). Inline pathComparer -> StringComparer.Ordinal.
- Trim XmlDoc.Check include comment from 4 lines to 1.
Tests:
- Collapse repeated [<Fact>] cases into [<Theory>]/[<InlineData>]: DTD/XXE 4->1,
inline-selection 2->1, param-validation-warn 4->1 (62 discrete cases preserved).
- Move single-consumer verifyXmlDoc*/readEmittedXml out of the shared Compiler.fs
into the only test file that uses them; recompose withXmlDoc from existing
withOutputDirectory/withOptions; simplify countSubstring; reuse includeWarningCount.
Net -188 LOC, no behavior change; XmlDocInclude 62/62 green, build 0W/0E, fantomas clean.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Split(string) is .NET Core-only; the desktop (net472) test legs only have
Split(char[]) and Split(string[], StringSplitOptions). Use the latter so the
helper compiles on every target framework.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
T-Groand others added 2 commits August 6, 2026 13:32
The main merge brought the record-spread feature, which claimed 3905 (with
off-by-default handling in CompilerDiagnostics + its own tests). The XML
<include> diagnostics also sat at 3905, colliding. Move the two include
messages (xmlDocIncludeError/xmlDocIncludeError2) to 3908 so the record-spread
block stays contiguous (3902-3907) and the include feature owns its own number.
Updated the emitted-warning number in the two production comments and all
XmlDocInclude test assertions. .xlf files are keyed by identifier with unchanged
source text, so no localization regeneration is needed.
Verified: build 0 errors; XmlDocInclude 62/62 green; RecordSpreads 127/127 green.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants

@T-Gro@abonie