diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index c87819d01ba..a066dd424ef 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -4,6 +4,7 @@ * Add support for tail calls in computation expressions ([PR #18804](https://github.com/dotnet/fsharp/pull/18804)) * Add `--typecheck-only` flag support for F# Interactive (FSI) scripts to type-check without execution. ([Issue #18686](https://github.com/dotnet/fsharp/issues/18686)) * Diagnostics: add extended data for 'No constructors' error ([PR #18863](https://github.com/dotnet/fsharp/pull/18863)) +* FSharpType.Format: support top-level prefix generic types style. ([PR #18897](https://github.com/dotnet/fsharp/pull/18897)) * FCS: allow getting captured types ([PR $18878](https://github.com/dotnet/fsharp/pull/18878)) ### Fixed @@ -25,6 +26,7 @@ * 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)) * Fix nullable types formatting in `FSharpType.Format` and tooltips to include parentheses. ([PR #18842](https://github.com/dotnet/fsharp/pull/18842)) * TypeMismatchDiagnosticExtendedData: fix expected and actual types calculation. ([Issue ](https://github.com/dotnet/fsharp/pull/18851)) +* 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)) * Parser: fix range for computed binding expressions ([PR #18903](https://github.com/dotnet/fsharp/pull/18903)) ### Changed diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 00b6f4c0b55..94a239da048 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -169,9 +169,10 @@ module internal PrintUtilities = let usePrefix (denv: DisplayEnv) (tcref: TyconRef) = match denv.genericParameterStyle with - | GenericParameterStyle.Implicit -> tcref.IsPrefixDisplay - | GenericParameterStyle.Prefix -> true - | GenericParameterStyle.Suffix -> false + | GenericParameterStyle.Implicit -> tcref.IsPrefixDisplay, denv + | GenericParameterStyle.Prefix -> true, denv + | GenericParameterStyle.Suffix -> false, denv + | GenericParameterStyle.TopLevelPrefix nested -> true, denv.UseGenericParameterStyle(nested) /// /// Creates a layout for TyconRef. @@ -187,7 +188,7 @@ module internal PrintUtilities = /// let layoutTyconRefImpl isAttribute (denv: DisplayEnv) (tcref: TyconRef) (demangledPath: string list option) = - let prefix = usePrefix denv tcref + let prefix, denv = usePrefix denv tcref let isArray = not prefix && isArrayTyconRef denv.g tcref let demangled = if isArray then @@ -744,12 +745,8 @@ module PrintTypes = | Some typarConstraintTy -> if Zset.contains typar env.singletons then let tyLayout = - match typarConstraintTy with - | TType_app (tyconRef = tc; typeInstantiation = ti) - when ti.Length > 0 && not (usePrefix denv tc) -> - layoutTypeWithInfo denv env typarConstraintTy - |> bracketL - | _ -> layoutTypeWithInfo denv env typarConstraintTy + let denv = denv.UseTopLevelPrefixGenericParameterStyle() + layoutTypeWithInfo denv env typarConstraintTy leftL (tagPunctuation "#") ^^ tyLayout else @@ -975,10 +972,10 @@ module PrintTypes = // Layout a type application | TType_ucase (UnionCaseRef(tc, _), args) -> - let prefix = usePrefix denv tc + let prefix, denv = usePrefix denv tc layoutTypeAppWithInfoAndPrec denv env (layoutTyconRefImpl false denv tc None) prec prefix args | TType_app (tc, args, nullness) -> - let prefix = usePrefix denv tc + let prefix, denv = usePrefix denv tc let demangledCompilationPathOpt, args = if not denv.includeStaticParametersInTypeNames then None, args @@ -2047,6 +2044,7 @@ module TastDefinitionPrinting = GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g amap m ty let iimplsLs = + let denv = denv.UseTopLevelPrefixGenericParameterStyle() iimpls |> List.map (fun intfTy -> (if isInterfaceTy g ty then WordL.keywordInherit else WordL.keywordInterface) -* layoutType denv intfTy) @@ -2181,7 +2179,8 @@ module TastDefinitionPrinting = | _ -> () ] - let inheritsL = + let inheritsL = + let denv = denv.UseTopLevelPrefixGenericParameterStyle() inherits |> List.map (fun super -> WordL.keywordInherit ^^ (layoutType denv super)) diff --git a/src/Compiler/Symbols/Symbols.fs b/src/Compiler/Symbols/Symbols.fs index fb413eccea7..28045551725 100644 --- a/src/Compiler/Symbols/Symbols.fs +++ b/src/Compiler/Symbols/Symbols.fs @@ -216,6 +216,9 @@ type FSharpDisplayContext(denv: TcGlobals -> DisplayEnv) = member _.WithSuffixGenericParameters () = FSharpDisplayContext(fun g -> { denv g with genericParameterStyle = GenericParameterStyle.Suffix } ) + member x.WithTopLevelPrefixGenericParameters () = + FSharpDisplayContext(fun g -> (denv g).UseTopLevelPrefixGenericParameterStyle()) + // delay the realization of 'item' in case it is unresolved type FSharpSymbol(cenv: SymbolEnv, item: unit -> Item, access: FSharpSymbol -> CcuThunk -> AccessorDomain -> bool) = diff --git a/src/Compiler/Symbols/Symbols.fsi b/src/Compiler/Symbols/Symbols.fsi index 701e9a8fe3c..0fec9922171 100644 --- a/src/Compiler/Symbols/Symbols.fsi +++ b/src/Compiler/Symbols/Symbols.fsi @@ -74,6 +74,11 @@ type FSharpDisplayContext = /// for example `int list` member WithSuffixGenericParameters: unit -> FSharpDisplayContext + /// Causes type signatures to be formatted with prefix-style generic parameters for a top level type + /// while nested types inherit generic parameters style from the current `FSharpDisplayContext` instance, + /// for example, `int list seq` becomes `seq` + member WithTopLevelPrefixGenericParameters: unit -> FSharpDisplayContext + /// Represents a symbol in checked F# source code or a compiled .NET component. /// /// The subtype of the symbol may reveal further information and can be one of FSharpEntity, FSharpUnionCase diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 368dd9a99ce..3c5d2dcd20b 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3171,6 +3171,7 @@ type GenericParameterStyle = | Implicit | Prefix | Suffix + | TopLevelPrefix of nested: GenericParameterStyle [] type DisplayEnv = @@ -3255,6 +3256,14 @@ type DisplayEnv = member denv.UseGenericParameterStyle style = { denv with genericParameterStyle = style } + + member denv.UseTopLevelPrefixGenericParameterStyle() = + let nestedStyle = + match denv.genericParameterStyle with + | TopLevelPrefix(nested) -> nested + | style -> style + + { denv with genericParameterStyle = TopLevelPrefix(nestedStyle) } static member InitialForSigFileGeneration g = let denv = diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 06200be47f7..ed847cc5839 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -1061,6 +1061,9 @@ type GenericParameterStyle = | Prefix /// Force the suffix style: int List | Suffix + /// Force the prefix style for a top-level type, + /// for example, `seq` instead of `int list seq` + | TopLevelPrefix of nested: GenericParameterStyle [] type DisplayEnv = @@ -1111,6 +1114,8 @@ type DisplayEnv = member UseGenericParameterStyle: GenericParameterStyle -> DisplayEnv + member UseTopLevelPrefixGenericParameterStyle: unit -> DisplayEnv + static member InitialForSigFileGeneration: TcGlobals -> DisplayEnv val tagEntityRefName: xref: EntityRef -> name: string -> TaggedText diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index dd72f416ac0..161933e9c16 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -5166,6 +5166,7 @@ FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisp FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithPrefixGenericParameters() FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithShortTypeNames(Boolean) FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithSuffixGenericParameters() +FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext WithTopLevelPrefixGenericParameters() FSharp.Compiler.Symbols.FSharpDisplayContext: FSharp.Compiler.Symbols.FSharpDisplayContext get_Empty() FSharp.Compiler.Symbols.FSharpEntity: Boolean Equals(System.Object) FSharp.Compiler.Symbols.FSharpEntity: Boolean HasAssemblyCodeRepresentation diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index 06a84651fb6..220bfc62fde 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -449,6 +449,34 @@ let tester2: int Group = [] | other -> failwithf "myArr was supposed to be a value, but is %A" other + [] + let ``FSharpType.Format with top-level prefix generic parameters style`` () = + let _, checkResults = getParseAndCheckResults """ +let f (x: int list seq) = () +""" + let symbolUse = findSymbolUseByName "x" checkResults + let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue + let typeArg = symbol.FullType + let displayContext = symbolUse.DisplayContext + + let topLevelPrefixStyle = + displayContext.WithTopLevelPrefixGenericParameters() + + let topLevelPrefixWithNestedPrefixStyle1 = + displayContext.WithPrefixGenericParameters().WithTopLevelPrefixGenericParameters() + + // Should be idempotent + let topLevelPrefixWithNestedPrefixStyle2 = + topLevelPrefixWithNestedPrefixStyle1.WithTopLevelPrefixGenericParameters() + + [ typeArg.Format(topLevelPrefixStyle) + typeArg.Format(topLevelPrefixWithNestedPrefixStyle1) + typeArg.Format(topLevelPrefixWithNestedPrefixStyle2) ] + |> shouldBe [ + "seq" + "seq>" + "seq>" ] + [] let ``Unfinished long ident type `` () = let _, checkResults = getParseAndCheckResults """ diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 92d30a3e4cd..517e4a8b59d 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -535,4 +535,36 @@ open System let doIt(myAction : Action) = myAc{caret}tion.Invoke(42) """ |> assertAndGetSingleToolTipText - |> Assert.shouldBeEquivalentTo ("""val myAction: Action""" |> normalize) \ No newline at end of file + |> Assert.shouldBeEquivalentTo ("""val myAction: Action""" |> normalize) + +[] +let ``Super type should be formatted in the prefix style`` () = + Checker.getTooltip """ +namespace Foo + +type A{caret} = + inherit seq +""" + |> assertAndGetSingleToolTipText + |> Assert.shouldBeEquivalentTo "type A =\n inherit seq" + +[] +let ``Interface impl should be formatted in the prefix style`` () = + Checker.getTooltip """ +namespace Foo + +type A{caret} = + interface seq with +""" + |> assertAndGetSingleToolTipText + |> Assert.shouldBeEquivalentTo "type A =\n interface seq" + +[] +let ``Flexible generic type should be formatted in the prefix style`` () = + Checker.getTooltip """ +module Foo + +let f (x{caret}: #seq) = () +""" + |> assertAndGetSingleToolTipText + |> Assert.shouldBeEquivalentTo "val x: #seq" diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 3cf4d4e819b..1e08e69a953 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -40,7 +40,7 @@ [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-516::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$Symbols+fullName@2498-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Symbols+fullName@2501-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000011][found Char] Unexpected type on the stack. [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. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000E6][found Char] Unexpected type on the stack. @@ -60,7 +60,7 @@ [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. [IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000065][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x00000015][found Boolean] Unexpected type on the stack. -[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. +[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. [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. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateNamespaceName(string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string)][offset 0x00000063][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1)][offset 0x000000AD][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 8be51b83a5e..5830b7dd0a1 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -39,7 +39,7 @@ [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-531::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-531::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-531::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$Symbols+fullName@2498-3::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000030][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Symbols+fullName@2501-3::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000030][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@286-1::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@286-1::Invoke(string)][offset 0x00000014][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000010][found Char] Unexpected type on the stack. @@ -65,7 +65,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1875'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000040][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x0000000B][found Boolean] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2078-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000B3][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2076-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000B3][found Char] Unexpected type on the stack. [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 0x00000034][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateNamespaceName(string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string)][offset 0x00000074][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1)][offset 0x000000A8][found Char] Unexpected type on the stack.