Skip to content

[Repo Assist] Include type constraints in generated API documentation#1045

Open
github-actions[bot] wants to merge 2 commits intomainfrom
repo-assist/fix-issue-591-type-constraints-9dce2011594bb32d
Open

[Repo Assist] Include type constraints in generated API documentation#1045
github-actions[bot] wants to merge 2 commits intomainfrom
repo-assist/fix-issue-591-type-constraints-9dce2011594bb32d

Conversation

@github-actions
Copy link
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Closes #591

Root Cause

Type constraints on generic parameters (e.g. 'T : equality, 'T : comparison, 'T :> IComparable) were not extracted or displayed in generated API docs. The FSharpGenericParameter.Constraints property from FCS was available but unused — there was even a commented-out reference to indexedConstraints at SymbolReader.fs:285.

Fix

  • TypeFormatter.fs: Added formatConstraintAsText and formatConstraintsAsText using FSharpGenericParameter.Constraints from FCS. Handles: equality, comparison, struct, not struct, default constructor, unmanaged, coercion (:>), enum, delegate, and member constraints.
  • ApiDocTypes.fs: Added constraints: string list field to ApiDocMemberDetails; added Constraints member and FormatTypeConstraints to ApiDocMember.
  • SymbolReader.fs: Compute constraints from the same tps list used for type parameters; pass empty list for union cases, fields, and static params.
  • GenerateHtml.fs: Display a Constraints: line after Type parameters: in the member tooltip. Also fixed a pre-existing issue where type parameters were only shown when modifiers were non-empty.

Example

For val hash : 'T -> int when 'T : equality, the generated docs will now show:

Type parameters: 'T
Constraints: 'T : equality

Trade-offs

  • The ApiDocMemberDetails discriminated union gained a new constraints field — this is a minor breaking change for any external code that pattern-matches on it (same category of change as any field addition to this DU).
  • Member constraints (SRTP, e.g. (member Foo : 'T -> int)) are formatted using FSharpDisplayContext.Empty which may include fully-qualified type names in some cases.

Test Status

  • Build: ✅ succeeded
  • Tests: ✅ 72/72 passed, 4 skipped (pre-existing)
  • Formatting: ✅ Fantomas applied

Generated by Repo Assist for issue #591

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@afb00b92a9514fee9a14c583f059a03d05738f70

Fixes #591. Type constraints on generic parameters (e.g. 'T : equality,
'T : comparison, 'T :> IComparable) are now extracted from FCS symbols
and displayed in the member tooltip in the 'Constraints:' line.

Changes:
- TypeFormatter.fs: add formatConstraintAsText and formatConstraintsAsText
  using FSharpGenericParameter.Constraints from FCS; supports equality,
  comparison, struct, reference-type, default-ctor, unmanaged, coercesTo,
  enum, delegate, and member constraints
- ApiDocTypes.fs: add 'constraints: string list' field to ApiDocMemberDetails;
  add Constraints member and FormatTypeConstraints to ApiDocMember
- SymbolReader.fs: compute constraints from generic type parameters and
  pass to ApiDocMemberDetails; empty list for union cases, fields, static params
- GenerateHtml.fs: display 'Constraints:' line after 'Type parameters:';
  also fix: type parameters now show independently of modifiers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme dsyme marked this pull request as ready for review February 26, 2026 19:03
let typarName = formatTypeArgumentAsText typar

if cx.IsEqualityConstraint then
Some $"{typarName} : equality"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.
if cx.IsEqualityConstraint then
Some $"{typarName} : equality"
elif cx.IsComparisonConstraint then
Some $"{typarName} : comparison"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.
elif cx.IsComparisonConstraint then
Some $"{typarName} : comparison"
elif cx.IsNonNullableValueTypeConstraint then
Some $"{typarName} : struct"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.
elif cx.IsNonNullableValueTypeConstraint then
Some $"{typarName} : struct"
elif cx.IsReferenceTypeConstraint then
Some $"{typarName} : not struct"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.
elif cx.IsReferenceTypeConstraint then
Some $"{typarName} : not struct"
elif cx.IsRequiresDefaultConstructorConstraint then
Some $"{typarName} : (new : unit -> {typarName})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.

let retType = d.MemberReturnType.Format(FSharpDisplayContext.Empty)
let staticKw = if d.MemberIsStatic then "static member" else "member"
Some $"{typarName} : ({staticKw} {d.MemberName} : {argTypes} -> {retType})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.

let retType = d.MemberReturnType.Format(FSharpDisplayContext.Empty)
let staticKw = if d.MemberIsStatic then "static member" else "member"
Some $"{typarName} : ({staticKw} {d.MemberName} : {argTypes} -> {retType})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.

let retType = d.MemberReturnType.Format(FSharpDisplayContext.Empty)
let staticKw = if d.MemberIsStatic then "static member" else "member"
Some $"{typarName} : ({staticKw} {d.MemberName} : {argTypes} -> {retType})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.

let retType = d.MemberReturnType.Format(FSharpDisplayContext.Empty)
let staticKw = if d.MemberIsStatic then "static member" else "member"
Some $"{typarName} : ({staticKw} {d.MemberName} : {argTypes} -> {retType})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.

let retType = d.MemberReturnType.Format(FSharpDisplayContext.Empty)
let staticKw = if d.MemberIsStatic then "static member" else "member"
Some $"{typarName} : ({staticKw} {d.MemberName} : {argTypes} -> {retType})"

Check warning

Code scanning / Ionide.Analyzers.Cli

Warns about missing type specifiers in interpolated strings Warning

Interpolated hole expression without format detected. Use prefix with the correct % to enforce type safety.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Include type constraints in documentation

0 participants