[Repo Assist] Include type constraints in generated API documentation#1045
[Repo Assist] Include type constraints in generated API documentation#1045github-actions[bot] wants to merge 2 commits intomainfrom
Conversation
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>
| 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
| 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
| 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
| 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
| 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
|
|
||
| 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
|
|
||
| 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
|
|
||
| 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
|
|
||
| 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
|
|
||
| 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
🤖 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. TheFSharpGenericParameter.Constraintsproperty from FCS was available but unused — there was even a commented-out reference toindexedConstraintsatSymbolReader.fs:285.Fix
TypeFormatter.fs: AddedformatConstraintAsTextandformatConstraintsAsTextusingFSharpGenericParameter.Constraintsfrom FCS. Handles:equality,comparison,struct,not struct, default constructor,unmanaged, coercion (:>), enum, delegate, and member constraints.ApiDocTypes.fs: Addedconstraints: string listfield toApiDocMemberDetails; addedConstraintsmember andFormatTypeConstraintstoApiDocMember.SymbolReader.fs: Compute constraints from the sametpslist used for type parameters; pass empty list for union cases, fields, and static params.GenerateHtml.fs: Display aConstraints:line afterType 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:Trade-offs
ApiDocMemberDetailsdiscriminated union gained a newconstraintsfield — 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 Foo : 'T -> int)) are formatted usingFSharpDisplayContext.Emptywhich may include fully-qualified type names in some cases.Test Status