Skip to content

Commit 041a528

Browse files
authored
Nice print: format top-level generic types using a prefix style (#18897)
1 parent 9fadb2e commit 041a528

File tree

11 files changed

+102
-18
lines changed

11 files changed

+102
-18
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.100.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Add support for tail calls in computation expressions ([PR #18804](https://github.com/dotnet/fsharp/pull/18804))
55
* Add `--typecheck-only` flag support for F# Interactive (FSI) scripts to type-check without execution. ([Issue #18686](https://github.com/dotnet/fsharp/issues/18686))
66
* Diagnostics: add extended data for 'No constructors' error ([PR #18863](https://github.com/dotnet/fsharp/pull/18863))
7+
* FSharpType.Format: support top-level prefix generic types style. ([PR #18897](https://github.com/dotnet/fsharp/pull/18897))
78
* FCS: allow getting captured types ([PR $18878](https://github.com/dotnet/fsharp/pull/18878))
89

910
### Fixed
@@ -25,6 +26,7 @@
2526
* Fix Show XML doc for enum fields in external metadata ([Issue #17939](https://github.com/dotnet/fsharp/issues/17939#issuecomment-3137410105), [PR #18800](https://github.com/dotnet/fsharp/pull/18800))
2627
* Fix nullable types formatting in `FSharpType.Format` and tooltips to include parentheses. ([PR #18842](https://github.com/dotnet/fsharp/pull/18842))
2728
* TypeMismatchDiagnosticExtendedData: fix expected and actual types calculation. ([Issue ](https://github.com/dotnet/fsharp/pull/18851))
29+
* Format top-level generic types using a prefix style in inherit/interface declarations and flexible type annotations. ([PR #18897](https://github.com/dotnet/fsharp/pull/18897))
2830
* Parser: fix range for computed binding expressions ([PR #18903](https://github.com/dotnet/fsharp/pull/18903))
2931

3032
### Changed

src/Compiler/Checking/NicePrint.fs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,10 @@ module internal PrintUtilities =
169169

170170
let usePrefix (denv: DisplayEnv) (tcref: TyconRef) =
171171
match denv.genericParameterStyle with
172-
| GenericParameterStyle.Implicit -> tcref.IsPrefixDisplay
173-
| GenericParameterStyle.Prefix -> true
174-
| GenericParameterStyle.Suffix -> false
172+
| GenericParameterStyle.Implicit -> tcref.IsPrefixDisplay, denv
173+
| GenericParameterStyle.Prefix -> true, denv
174+
| GenericParameterStyle.Suffix -> false, denv
175+
| GenericParameterStyle.TopLevelPrefix nested -> true, denv.UseGenericParameterStyle(nested)
175176

176177
/// <summary>
177178
/// Creates a layout for TyconRef.
@@ -187,7 +188,7 @@ module internal PrintUtilities =
187188
/// </param>
188189
let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) (demangledPath: string list option) =
189190

190-
let prefix = usePrefix denv tcref
191+
let prefix, denv = usePrefix denv tcref
191192
let isArray = not prefix && isArrayTyconRef denv.g tcref
192193
let demangled =
193194
if isArray then
@@ -744,12 +745,8 @@ module PrintTypes =
744745
| Some typarConstraintTy ->
745746
if Zset.contains typar env.singletons then
746747
let tyLayout =
747-
match typarConstraintTy with
748-
| TType_app (tyconRef = tc; typeInstantiation = ti)
749-
when ti.Length > 0 && not (usePrefix denv tc) ->
750-
layoutTypeWithInfo denv env typarConstraintTy
751-
|> bracketL
752-
| _ -> layoutTypeWithInfo denv env typarConstraintTy
748+
let denv = denv.UseTopLevelPrefixGenericParameterStyle()
749+
layoutTypeWithInfo denv env typarConstraintTy
753750

754751
leftL (tagPunctuation "#") ^^ tyLayout
755752
else
@@ -975,10 +972,10 @@ module PrintTypes =
975972

976973
// Layout a type application
977974
| TType_ucase (UnionCaseRef(tc, _), args) ->
978-
let prefix = usePrefix denv tc
975+
let prefix, denv = usePrefix denv tc
979976
layoutTypeAppWithInfoAndPrec denv env (layoutTyconRefImpl false denv tc None) prec prefix args
980977
| TType_app (tc, args, nullness) ->
981-
let prefix = usePrefix denv tc
978+
let prefix, denv = usePrefix denv tc
982979
let demangledCompilationPathOpt, args =
983980
if not denv.includeStaticParametersInTypeNames then
984981
None, args
@@ -2047,6 +2044,7 @@ module TastDefinitionPrinting =
20472044
GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m ty
20482045

20492046
let iimplsLs =
2047+
let denv = denv.UseTopLevelPrefixGenericParameterStyle()
20502048
iimpls
20512049
|> List.map (fun intfTy -> (if isInterfaceTy g ty then WordL.keywordInherit else WordL.keywordInterface) -* layoutType denv intfTy)
20522050

@@ -2181,7 +2179,8 @@ module TastDefinitionPrinting =
21812179
| _ -> ()
21822180
]
21832181

2184-
let inheritsL =
2182+
let inheritsL =
2183+
let denv = denv.UseTopLevelPrefixGenericParameterStyle()
21852184
inherits
21862185
|> List.map (fun super -> WordL.keywordInherit ^^ (layoutType denv super))
21872186

src/Compiler/Symbols/Symbols.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ type FSharpDisplayContext(denv: TcGlobals -> DisplayEnv) =
216216
member _.WithSuffixGenericParameters () =
217217
FSharpDisplayContext(fun g -> { denv g with genericParameterStyle = GenericParameterStyle.Suffix } )
218218

219+
member x.WithTopLevelPrefixGenericParameters () =
220+
FSharpDisplayContext(fun g -> (denv g).UseTopLevelPrefixGenericParameterStyle())
221+
219222
// delay the realization of 'item' in case it is unresolved
220223
type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> CcuThunk -> AccessorDomain -> bool) =
221224

src/Compiler/Symbols/Symbols.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ type FSharpDisplayContext =
7474
/// for example `int list`
7575
member WithSuffixGenericParameters: unit -> FSharpDisplayContext
7676

77+
/// Causes type signatures to be formatted with prefix-style generic parameters for a top level type
78+
/// while nested types inherit generic parameters style from the current `FSharpDisplayContext` instance,
79+
/// for example, `int list seq` becomes `seq<int list>`
80+
member WithTopLevelPrefixGenericParameters: unit -> FSharpDisplayContext
81+
7782
/// Represents a symbol in checked F# source code or a compiled .NET component.
7883
///
7984
/// The subtype of the symbol may reveal further information and can be one of FSharpEntity, FSharpUnionCase

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,6 +3171,7 @@ type GenericParameterStyle =
31713171
| Implicit
31723172
| Prefix
31733173
| Suffix
3174+
| TopLevelPrefix of nested: GenericParameterStyle
31743175

31753176
[<NoEquality; NoComparison>]
31763177
type DisplayEnv =
@@ -3255,6 +3256,14 @@ type DisplayEnv =
32553256

32563257
member denv.UseGenericParameterStyle style =
32573258
{ denv with genericParameterStyle = style }
3259+
3260+
member denv.UseTopLevelPrefixGenericParameterStyle() =
3261+
let nestedStyle =
3262+
match denv.genericParameterStyle with
3263+
| TopLevelPrefix(nested) -> nested
3264+
| style -> style
3265+
3266+
{ denv with genericParameterStyle = TopLevelPrefix(nestedStyle) }
32583267

32593268
static member InitialForSigFileGeneration g =
32603269
let denv =

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,9 @@ type GenericParameterStyle =
10611061
| Prefix
10621062
/// Force the suffix style: int List
10631063
| Suffix
1064+
/// Force the prefix style for a top-level type,
1065+
/// for example, `seq<int list>` instead of `int list seq`
1066+
| TopLevelPrefix of nested: GenericParameterStyle
10641067

10651068
[<NoEquality; NoComparison>]
10661069
type DisplayEnv =
@@ -1111,6 +1114,8 @@ type DisplayEnv =
11111114

11121115
member UseGenericParameterStyle: GenericParameterStyle -> DisplayEnv
11131116

1117+
member UseTopLevelPrefixGenericParameterStyle: unit -> DisplayEnv
1118+
11141119
static member InitialForSigFileGeneration: TcGlobals -> DisplayEnv
11151120

11161121
val tagEntityRefName: xref: EntityRef -> name: string -> TaggedText

tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5166,6 +5166,7 @@ FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisp
51665166
FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithPrefixGenericParameters()
51675167
FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithShortTypeNames(Boolean)
51685168
FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithSuffixGenericParameters()
5169+
FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithTopLevelPrefixGenericParameters()
51695170
FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext get_Empty()
51705171
FSharp.Compiler.Symbols.FSharpEntity: Boolean Equals(System.Object)
51715172
FSharp.Compiler.Symbols.FSharpEntity: Boolean HasAssemblyCodeRepresentation

tests/FSharp.Compiler.Service.Tests/Symbols.fs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,34 @@ let tester2: int Group = []
449449

450450
| other -> failwithf "myArr was supposed to be a value, but is %A" other
451451

452+
[<Fact>]
453+
let ``FSharpType.Format with top-level prefix generic parameters style`` () =
454+
let _, checkResults = getParseAndCheckResults """
455+
let f (x: int list seq) = ()
456+
"""
457+
let symbolUse = findSymbolUseByName "x" checkResults
458+
let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue
459+
let typeArg = symbol.FullType
460+
let displayContext = symbolUse.DisplayContext
461+
462+
let topLevelPrefixStyle =
463+
displayContext.WithTopLevelPrefixGenericParameters()
464+
465+
let topLevelPrefixWithNestedPrefixStyle1 =
466+
displayContext.WithPrefixGenericParameters().WithTopLevelPrefixGenericParameters()
467+
468+
// Should be idempotent
469+
let topLevelPrefixWithNestedPrefixStyle2 =
470+
topLevelPrefixWithNestedPrefixStyle1.WithTopLevelPrefixGenericParameters()
471+
472+
[ typeArg.Format(topLevelPrefixStyle)
473+
typeArg.Format(topLevelPrefixWithNestedPrefixStyle1)
474+
typeArg.Format(topLevelPrefixWithNestedPrefixStyle2) ]
475+
|> shouldBe [
476+
"seq<int list>"
477+
"seq<list<int>>"
478+
"seq<list<int>>" ]
479+
452480
[<Fact>]
453481
let ``Unfinished long ident type `` () =
454482
let _, checkResults = getParseAndCheckResults """

tests/FSharp.Compiler.Service.Tests/TooltipTests.fs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,4 +535,36 @@ open System
535535
let doIt(myAction : Action<int>) = myAc{caret}tion.Invoke(42)
536536
"""
537537
|> assertAndGetSingleToolTipText
538-
|> Assert.shouldBeEquivalentTo ("""val myAction: Action<int>""" |> normalize)
538+
|> Assert.shouldBeEquivalentTo ("""val myAction: Action<int>""" |> normalize)
539+
540+
[<Fact>]
541+
let ``Super type should be formatted in the prefix style`` () =
542+
Checker.getTooltip """
543+
namespace Foo
544+
545+
type A{caret} =
546+
inherit seq<int list>
547+
"""
548+
|> assertAndGetSingleToolTipText
549+
|> Assert.shouldBeEquivalentTo "type A =\n inherit seq<int list>"
550+
551+
[<Fact>]
552+
let ``Interface impl should be formatted in the prefix style`` () =
553+
Checker.getTooltip """
554+
namespace Foo
555+
556+
type A{caret} =
557+
interface seq<int list> with
558+
"""
559+
|> assertAndGetSingleToolTipText
560+
|> Assert.shouldBeEquivalentTo "type A =\n interface seq<int list>"
561+
562+
[<Fact>]
563+
let ``Flexible generic type should be formatted in the prefix style`` () =
564+
Checker.getTooltip """
565+
module Foo
566+
567+
let f (x{caret}: #seq<int list>) = ()
568+
"""
569+
|> assertAndGetSingleToolTipText
570+
|> Assert.shouldBeEquivalentTo "val x: #seq<int list>"

tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack.
4141
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack.
4242
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<System.Tuple`3<FSharp.Compiler.Parser+token,int32,int32>,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack.
43-
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$Symbols+fullName@2498-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack.
43+
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$Symbols+fullName@2501-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack.
4444
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000011][found Char] Unexpected type on the stack.
4545
[IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack.
4646
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<int32>)][offset 0x000000E6][found Char] Unexpected type on the stack.
@@ -60,7 +60,7 @@
6060
[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerConfig+TcConfig::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, bool)][offset 0x00000634][found Char] Unexpected type on the stack.
6161
[IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000065][found Byte] Unexpected type on the stack.
6262
[IL]: Error [StackUnexpected]: : <StartupCode$FSharp-Compiler-Service>.$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x00000015][found Boolean] Unexpected type on the stack.
63-
[IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2078-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000BE][found Char] Unexpected type on the stack.
63+
[IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2076-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000BE][found Char] Unexpected type on the stack.
6464
[IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+PrintUtilities::layoutXmlDoc([FSharp.Compiler.Service]FSharp.Compiler.TypedTreeOps+DisplayEnv, bool, [FSharp.Compiler.Service]FSharp.Compiler.Xml.XmlDoc, [FSharp.Compiler.Service]FSharp.Compiler.Text.Layout)][offset 0x00000033][found Char] Unexpected type on the stack.
6565
[IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateNamespaceName(string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1<Microsoft.FSharp.Core.CompilerServices.ITypeProvider>, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string)][offset 0x00000063][found Char] Unexpected type on the stack.
6666
[IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1<FSharp.Compiler.TypeProviders+ProvidedType>)][offset 0x000000AD][found Char] Unexpected type on the stack.

0 commit comments

Comments
 (0)