diff --git a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs index 9458913f5..0e304b1ca 100644 --- a/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs +++ b/src/FsAutoComplete.Core/AbstractClassStubGenerator.fs @@ -54,7 +54,7 @@ let tryFindAbstractClassExprInBufferAtPos let allMembers = reprMembers @ members - let! inheritType, inheritMemberRange = // this must exist for abstract types + let! inheritType, inheritMemberRange = allMembers |> List.tryPick (function | SynMemberDefn.ImplicitInherit(inheritType, _, _, range) -> Some(inheritType, range) diff --git a/src/FsAutoComplete.Core/DocumentationFormatter.fs b/src/FsAutoComplete.Core/DocumentationFormatter.fs index c497c855d..bb22f75f4 100644 --- a/src/FsAutoComplete.Core/DocumentationFormatter.fs +++ b/src/FsAutoComplete.Core/DocumentationFormatter.fs @@ -309,7 +309,7 @@ module DocumentationFormatter = if String.IsNullOrWhiteSpace formattedParam then formattedParam else - "(requires " + formattedParam + " )" + "(requires " + formattedParam + ")" else "" @@ -379,7 +379,7 @@ module DocumentationFormatter = if String.IsNullOrWhiteSpace formattedParam then formattedParam else - "(requires " + formattedParam + " )" + "(requires " + formattedParam + ")" if paramConstraint = retTypeConstraint then paddedParam diff --git a/src/FsAutoComplete.Core/SignatureFormatter.fs b/src/FsAutoComplete.Core/SignatureFormatter.fs index c79e34433..ca7037a0c 100644 --- a/src/FsAutoComplete.Core/SignatureFormatter.fs +++ b/src/FsAutoComplete.Core/SignatureFormatter.fs @@ -43,6 +43,12 @@ module SignatureFormatter = let rec formatFSharpType (context: FSharpDisplayContext) (typ: FSharpType) : string = let context = context.WithPrefixGenericParameters() + let nullabilityClause = + if typ.HasNullAnnotation || typ.IsNullAmbivalent then + " | null" + else + "" + try if typ.IsTupleType || typ.IsStructTupleType then let refTupleStr = @@ -59,7 +65,7 @@ module SignatureFormatter = else refTupleStr elif typ.IsGenericParameter then // no longer need to differentiate between SRTP and normal generic parameter types - "'" + typ.GenericParameter.Name + "'" + typ.GenericParameter.Name + nullabilityClause elif typ.HasTypeDefinition && typ.GenericArguments.Count > 0 then let typeDef = typ.TypeDefinition @@ -68,19 +74,20 @@ module SignatureFormatter = if entityIsArray typeDef then if typ.GenericArguments.Count = 1 && typ.GenericArguments.[0].IsTupleType then - sprintf "(%s) array" genericArgs + $"(%s{genericArgs}) array%s{nullabilityClause}" else - sprintf "%s array" genericArgs + $"%s{genericArgs} array%s{nullabilityClause}" elif isMeasureType typeDef then - typ.Format context + typ.Format context + nullabilityClause else - sprintf "%s<%s>" (FSharpKeywords.NormalizeIdentifierBackticks typeDef.DisplayName) genericArgs + $"%s{FSharpKeywords.NormalizeIdentifierBackticks typeDef.DisplayName}<%s{genericArgs}>%s{nullabilityClause}" else if typ.HasTypeDefinition then FSharpKeywords.NormalizeIdentifierBackticks typ.TypeDefinition.DisplayName + + nullabilityClause else - typ.Format context + typ.Format context + nullabilityClause with _ -> - typ.Format context + typ.Format context + nullabilityClause let formatGenericParameter includeMemberConstraintTypes displayContext (param: FSharpGenericParameter) = @@ -357,7 +364,7 @@ module SignatureFormatter = if String.IsNullOrWhiteSpace formattedParam then formattedParam else - "(requires " + formattedParam + " )" + "(requires " + formattedParam + ")" if paramConstraint = retTypeConstraint then paramFormat diff --git a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs index 23ff1ae16..140708fac 100644 --- a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs @@ -523,8 +523,8 @@ let tooltipTests state = 13u (concatLines [ "val inline add:" - " x: 'a (requires static member ( + ) ) ->" - " y: 'b (requires static member ( + ) )" + " x: 'a (requires static member ( + )) ->" + " y: 'b (requires static member ( + ))" " -> 'c" ]) //verify rendering of solved generic constraints in tooltips for members where they are solved verifyDescription "solved generic parameters are called out in tooltip" @@ -548,15 +548,9 @@ let tooltipTests state = 60u 7u (concatLines -#if NET8_0 - [ "active pattern Value: " - " input: Expr" - " -> option" ]) -#else [ "active pattern Value: " " input: Expr" " -> option" ]) -#endif verifySignature "generic constraint rendering for IWSAM" 77u 5u @@ -570,7 +564,7 @@ let tooltipTests state = 25u (concatLines [ "static member GetAwaiter:" - " awaitable: 'Awaitable (requires member GetAwaiter )" + " awaitable: 'Awaitable (requires member GetAwaiter)" " -> Awaiter<^Awaiter,'TResult> (requires :> ICriticalNotifyCompletion and member IsCompleted and member GetResult)" ]) verifySignature "basic active pattern" @@ -584,15 +578,9 @@ let tooltipTests state = 70u 7u (concatLines -#if NET8_0 - [ "active pattern ValueWithName: " - " input: Expr" - " -> option" ]) -#else [ "active pattern ValueWithName: " " input: Expr" " -> option" ]) -#endif verifySignature "interface with members with and without parameter names" 96u 7u @@ -600,31 +588,31 @@ let tooltipTests state = [ "interface IWithAndWithoutParamNames" " abstract member WithParamNames: arg1: int * arg2: float -> string" " abstract member WithoutParamNames: int * string -> int" ]) - verifySignature "function with unsolved nullable parameter" 100u 7u (concatLines [ + verifySignature "function with unsolved nullable parameter" 102u 7u (concatLines [ "val usesNullable:" - " x: 't | null" - " -> 't (requires reference )" + " x: 't | null" + " -> 't (requires reference)" ]) - verifySignature "function with concrete nullable parameter" 101u 7u (concatLines [ + verifySignature "function with concrete nullable parameter" 103u 7u (concatLines [ "val usesConcreteNullable:" " x: string | null" - " -> String (requires reference )" + " -> String" ]) - verifySignature "function with generic nullable return" 102u 7u (concatLines [ + verifySignature "function with generic nullable return" 104u 7u (concatLines [ "val makesNullable:" - " x: 'x (requires reference)" - " -> 'x | null" + " x: 'x" + " -> 'x | null (requires reference)" ]) - verifySignature "function with concrete nullable return" 103u 7u (concatLines [ + verifySignature "function with concrete nullable return" 105u 7u (concatLines [ "val makesConcreteNullable:" " x: string" " -> string | null" ]) - verifySignature "function with nullable return from BCL call" 104u 7u (concatLines [ + verifySignature "function with nullable return from BCL call" 106u 7u (concatLines [ "val usesBCLNullable:" " key: string" - " -> string | null" + " -> string | null" ])] ] let closeTests state = diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/Tooltips/Script.fsx b/test/FsAutoComplete.Tests.Lsp/TestCases/Tooltips/Script.fsx index b9954da58..9db687fe7 100644 --- a/test/FsAutoComplete.Tests.Lsp/TestCases/Tooltips/Script.fsx +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/Tooltips/Script.fsx @@ -98,6 +98,8 @@ type IWithAndWithoutParamNames = abstract member WithParamNames : arg1: int * arg2: float -> string abstract member WithoutParamNames : int * string -> int +#nullable enable + let usesNullable (x: 't | null) = nonNull x let usesConcreteNullable (x: string | null) = nonNull x let makesNullable (x: 'x): 'x | null = null