diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index c6b5c7b7df0..d58cb9ebaba 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -34,6 +34,11 @@ * Fix signature generation: backtick escaping for identifiers containing backticks. ([Issue #15389](https://github.com/dotnet/fsharp/issues/15389), [PR #19586](https://github.com/dotnet/fsharp/pull/19586)) * Fix signature generation: `private` keyword placement for prefix-style type abbreviations. ([Issue #15560](https://github.com/dotnet/fsharp/issues/15560), [PR #19586](https://github.com/dotnet/fsharp/pull/19586)) * Fix signature generation: missing `[]` attribute for types without visible constructors. ([Issue #16531](https://github.com/dotnet/fsharp/issues/16531), [PR #19586](https://github.com/dotnet/fsharp/pull/19586)) +* Fix signature generation: single-case struct DU gets spurious bar causing FS0300. ([Issue #19597](https://github.com/dotnet/fsharp/issues/19597), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) +* Fix signature generation: backticked active pattern case names lose escaping. ([Issue #19592](https://github.com/dotnet/fsharp/issues/19592), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) +* Fix signature generation: `namespace global` header dropped from generated signature. ([Issue #19593](https://github.com/dotnet/fsharp/issues/19593), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) +* Fix signature generation: SRTP constraints use postfix syntax that fails conformance, now uses explicit type param declarations. ([Issue #19594](https://github.com/dotnet/fsharp/issues/19594), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) +* Fix signature generation: type params with special characters missing backtick escaping. ([Issue #19595](https://github.com/dotnet/fsharp/issues/19595), [PR #19609](https://github.com/dotnet/fsharp/pull/19609)) ### Added diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 048375c24fb..bbfb5879110 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -730,11 +730,13 @@ module PrintTypes = | _, _ -> squareAngleL (sepListL RightL.semicolon ((match kind with TyparKind.Type -> [] | TyparKind.Measure -> [wordL (tagText "Measure")]) @ List.map (layoutAttrib denv) attrs)) ^^ restL and layoutTyparRef denv (typar: Typar) = + let rawName = typar.DeclaredName |> Option.defaultValue typar.Name + let name = if System.String.IsNullOrEmpty rawName then rawName else NormalizeIdentifierBackticks rawName tagTypeParameter (sprintf "%s%s%s" (if denv.showStaticallyResolvedTyparAnnotations then prefixOfStaticReq typar.StaticReq else "'") (if denv.showInferenceTyparAnnotations then prefixOfInferenceTypar typar else "") - (typar.DeclaredName |> Option.defaultValue typar.Name)) + name) |> mkNav typar.Range |> wordL @@ -1200,10 +1202,15 @@ module PrintTypes = let (prettyTyparInst, prettyArgInfos, prettyRetTy), cxs = PrettyTypes.PrettifyInstAndUncurriedSig denv.g (typarInst, argInfos, retTy) prettyTyparInst, prettyLayoutOfTopTypeInfoAux denv [prettyArgInfos] prettyRetTy cxs - let prettyLayoutOfCurriedMemberSig denv typarInst argInfos retTy parentTyparTys = + let prettyLayoutOfCurriedMemberSig denv typarInst argInfos retTy parentTyparTys excludeSrtpConstraints = let (prettyTyparInst, parentTyparTys, argInfos, retTy), cxs = PrettyTypes.PrettifyInstAndCurriedSig denv.g (typarInst, parentTyparTys, argInfos, retTy) // Filter out the parent typars, which don't get shown in the member signature let cxs = cxs |> List.filter (fun (tp, _) -> not (parentTyparTys |> List.exists (fun ty -> match tryDestTyparTy denv.g ty with ValueSome destTypar -> typarEq tp destTypar | _ -> false))) + // When SRTP method typars are shown on explicit type param declarations, exclude their constraints from postfix + let cxs = + if excludeSrtpConstraints then + cxs |> List.filter (fun (tp, _) -> tp.StaticReq <> TyparStaticReq.HeadType) + else cxs prettyTyparInst, prettyLayoutOfTopTypeInfoAux denv argInfos retTy cxs let prettyArgInfos denv allTyparInst = @@ -1224,7 +1231,8 @@ module PrintTypes = // aren't chosen as names for displayed variables. let memberParentTypars = List.map fst memberToParentInst let parentTyparTys = List.map (mkTyparTy >> instType allTyparInst) memberParentTypars - let prettyTyparInst, layout = prettyLayoutOfCurriedMemberSig denv typarInst argInfos retTy parentTyparTys + let hasStaticallyResolvedTypars = niceMethodTypars |> List.exists (fun tp -> tp.StaticReq = TyparStaticReq.HeadType) + let prettyTyparInst, layout = prettyLayoutOfCurriedMemberSig denv typarInst argInfos retTy parentTyparTys hasStaticallyResolvedTypars prettyTyparInst, niceMethodTypars, layout @@ -1355,8 +1363,10 @@ module PrintTastMemberOrVals = |> Seq.exists (fun tp -> parentTyparNames.Contains tp.typar_id.idText) let typarOrderMismatch = isTyparOrderMismatch niceMethodTypars argInfos + let hasStaticallyResolvedTypars = + niceMethodTypars |> List.exists (fun tp -> tp.StaticReq = TyparStaticReq.HeadType) let nameL = - if denv.showTyparBinding || typarOrderMismatch || memberHasSameTyparNameAsParentTypeTypars then + if denv.showTyparBinding || typarOrderMismatch || memberHasSameTyparNameAsParentTypeTypars || hasStaticallyResolvedTypars then layoutTyparDecls denv nameL true niceMethodTypars else nameL @@ -1526,10 +1536,19 @@ module PrintTastMemberOrVals = let isTyFunction = v.IsTypeFunction // Bug: 1143, and innerpoly tests let typarOrderMismatch = isTyparOrderMismatch tps argInfos + let hasStaticallyResolvedTypars = + tps |> List.exists (fun tp -> tp.StaticReq = TyparStaticReq.HeadType) && + not (IsLogicalOpName v.LogicalName) && + not denv.shortConstraints let typarBindingsL = - if isTyFunction || isOverGeneric || denv.showTyparBinding || typarOrderMismatch then + if isTyFunction || isOverGeneric || denv.showTyparBinding || typarOrderMismatch || hasStaticallyResolvedTypars then layoutTyparDecls denv nameL true tps else nameL + // When SRTP method typars are shown on explicit type param declarations, exclude their constraints from postfix + let cxs = + if hasStaticallyResolvedTypars then + cxs |> List.filter (fun (tp, _) -> tp.StaticReq <> TyparStaticReq.HeadType) + else cxs let valAndTypeL = (WordL.keywordVal ^^ (typarBindingsL |> addColonL)) --- layoutTopType denv env argInfos retTy cxs let valAndTypeL = match denv.generatedValueLayout v with @@ -1901,8 +1920,12 @@ module TastDefinitionPrinting = | fields -> (prefixL ^^ nmL ^^ WordL.keywordOf) --- layoutUnionCaseFields denv infoReader true enclosingTcref fields layoutXmlDocOfUnionCase denv infoReader (UnionCaseRef(enclosingTcref, ucase.Id.idText)) caseL - let layoutUnionCases denv infoReader enclosingTcref ucases = - let prefixL = WordL.bar // See bug://2964 - always prefix in case preceded by accessibility modifier + let layoutUnionCases denv infoReader isStruct enclosingTcref ucases = + let prefixL = + match ucases with + // Single-case struct: bar changes base type semantics (FS0300), so omit it + | [ _ ] when isStruct -> emptyL + | _ -> WordL.bar // See bug://2964 - always prefix in case preceded by accessibility modifier List.map (layoutUnionCase denv infoReader prefixL enclosingTcref) ucases /// When to force a break? "type tyname = repn" @@ -2331,8 +2354,9 @@ module TastDefinitionPrinting = | TFSharpTyconRepr { fsobjmodel_kind = TFSharpUnion } -> let denv = denv.AddAccessibility tycon.TypeReprAccessibility + let isStruct = tycon.IsStructOrEnumTycon tycon.UnionCasesAsList - |> layoutUnionCases denv infoReader tcref + |> layoutUnionCases denv infoReader isStruct tcref |> applyMaxMembers denv.maxMembers |> aboveListL |> addReprAccessL @@ -2582,6 +2606,16 @@ module InferredSigPrinting = let (@@*) = if denv.printVerboseSignatures then (@@----) else (@@--) + // Detect namespace global: bare types/vals at root level (not wrapped in Module binding) + let rec hasBareToplevelTypes x = + match x with + | TMDefRec(_, _, tycons, _, _) -> not (List.isEmpty tycons) + | TMDefLet _ | TMDefDo _ -> true + | TMDefOpens _ -> false + | TMDefs defs -> defs |> List.exists hasBareToplevelTypes + + let isGlobalNamespace = hasBareToplevelTypes expr + let rec isConcreteNamespace x = match x with | TMDefRec(_, _opens, tycons, mbinds, _) -> @@ -2707,7 +2741,7 @@ module InferredSigPrinting = if showHeader then // OK, we're not in F# Interactive // Check if this is an outer module with no namespace - if isNil outerPath then + if isNil outerPath && not isGlobalNamespace then // If so print a "module" declaration, no indentation modNameL @@ basic else @@ -2745,7 +2779,12 @@ module InferredSigPrinting = | EmptyModuleOrNamespaces mspecs when showHeader -> List.map emptyModuleOrNamespace mspecs |> aboveListL - | expr -> imdefL denv expr + | expr -> + let layout = imdefL denv expr + if isGlobalNamespace then + WordL.keywordNamespace ^^ wordL (TaggedText.tagNamespace "global") @@* layout + else + layout //-------------------------------------------------------------------------- diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fs b/src/Compiler/SyntaxTree/PrettyNaming.fs index 922d9d9009f..0990c8b0238 100755 --- a/src/Compiler/SyntaxTree/PrettyNaming.fs +++ b/src/Compiler/SyntaxTree/PrettyNaming.fs @@ -506,6 +506,24 @@ let ConvertValLogicalNameToDisplayNameCore opName = else opName +/// Escape active pattern case names that need backticks for display/signatures. +/// E.g. |A B| becomes |``A B``| (only for display, not for name resolution) +let EscapeActivePatternCases (opName: string) = + if IsActivePatternName opName then + let inner = opName.[1 .. opName.Length - 2] + let cases = inner.Split('|') + + let escapedCases = + cases + |> Array.map (fun c -> + if c = "_" then c + elif not (IsIdentifierName c) then "``" + c + "``" + else c) + + "|" + (escapedCases |> String.concat "|") + "|" + else + opName + let DoesIdentifierNeedBackticks (name: string) : bool = not (IsUnencodedOpName name) && not (IsIdentifierName name) @@ -538,7 +556,7 @@ let ConvertValLogicalNameToDisplayName isBaseVal name = if isBaseVal && name = "base" then "base" elif IsUnencodedOpName name || IsPossibleOpName name || IsActivePatternName name then - let nm = ConvertValLogicalNameToDisplayNameCore name + let nm = ConvertValLogicalNameToDisplayNameCore name |> EscapeActivePatternCases // Check for no decompilation, e.g. op_Implicit, op_NotAMangledOpName, op_A-B if IsPossibleOpName name && (nm = name) then AddBackticksToIdentifierIfNeeded nm @@ -563,7 +581,7 @@ let ConvertValLogicalNameToDisplayLayout isBaseVal nonOpLayout name = if isBaseVal && name = "base" then nonOpLayout "base" elif IsUnencodedOpName name || IsPossibleOpName name || IsActivePatternName name then - let nm = ConvertValLogicalNameToDisplayNameCore name + let nm = ConvertValLogicalNameToDisplayNameCore name |> EscapeActivePatternCases // Check for no decompilation, e.g. op_Implicit, op_NotAMangledOpName, op_A-B if IsPossibleOpName name && (nm = name) then ConvertLogicalNameToDisplayLayout nonOpLayout name diff --git a/src/Compiler/SyntaxTree/PrettyNaming.fsi b/src/Compiler/SyntaxTree/PrettyNaming.fsi index afc85dad491..424f3579d05 100644 --- a/src/Compiler/SyntaxTree/PrettyNaming.fsi +++ b/src/Compiler/SyntaxTree/PrettyNaming.fsi @@ -125,6 +125,9 @@ val internal ConvertLogicalNameToDisplayName: name: string -> string /// If not, the it is likely this should be replaced by ConvertValLogicalNameToDisplayName. val ConvertValLogicalNameToDisplayNameCore: opName: string -> string +/// Escape active pattern case names that need backticks for display/signatures. +val EscapeActivePatternCases: opName: string -> string + /// Take a core display name for a value (e.g. op_Addition or PropertyName) and convert it to display text /// Foo --> Foo /// + --> ``+`` diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs index 88e29155339..2fd70fab940 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs @@ -80,7 +80,7 @@ let main _ = [] [ ^T")>] + "val inline f0<^T> : x: ^T -> ^T")>] [] [ int when ^T: (static member A: int)")>] + "val inline f1<^T when ^T: (static member A: int)> : x: ^T -> int")>] [ int when (^T or int) : (static member A: int)")>] + "val inline f2<^T when (^T or int) : (static member A: int)> : x: ^T -> int")>] [ int when (^U or ^T) : (static member A: int)")>] + "val inline f3<^T,^U when (^U or ^T) : (static member A: int)> : x: ^T -> int")>] [ unit when ^T: (byte|int16|int32|int64|sbyte|uint16|uint32|uint64|nativeint|unativeint)")>] + "val inline h5<^T when ^T: (byte|int16|int32|int64|sbyte|uint16|uint32|uint64|nativeint|unativeint)> : x: ^T -> unit")>] [ uint32) (value)) let inline uint value = uint32 value""", - "val inline uint: value: ^a -> uint32 when ^a: (static member op_Explicit: ^a -> uint32)")>] + "val inline uint<^a when ^a: (static member op_Explicit: ^a -> uint32)> : value: ^a -> uint32")>] [ 'a -> int) -> x: 'a -> y: 'a -> bool")>] diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/ModuleOrNamespaceTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/ModuleOrNamespaceTests.fs index 638ef94e8a0..571b4ca58fb 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/ModuleOrNamespaceTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/ModuleOrNamespaceTests.fs @@ -376,3 +376,50 @@ let ```a` b`` (a:int) (b:int) = () module Foo val ```a` b`` : a: int -> b: int -> unit""" + +// Found by corpus-wide roundtrip sweep. Fixed: #19593 +[] +let ``Namespace global with class type roundtrips`` () = + let implSource = + """ +namespace global + +type Foo() = + member _.X = 1 +""" + + let generatedSignature = + FSharp implSource + |> printSignatures + + Fsi generatedSignature + |> withAdditionalSourceFile (FsSource implSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + +// Namespace global with nested module — fixed by moving ns global detection into NicePrint +[] +let ``Namespace global with module roundtrips`` () = + let implSource = + """ +namespace global + +type Foo() = + member _.X = 1 + +module Utils = + let f (x:Foo) = x +""" + + let generatedSignature = + FSharp implSource + |> printSignatures + + Fsi generatedSignature + |> withAdditionalSourceFile (FsSource implSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TestHelpers.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TestHelpers.fs index f0004aeb550..c82eb238fea 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TestHelpers.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TestHelpers.fs @@ -15,3 +15,12 @@ let assertSingleSignatureBinding implementation signature = FSharp $"module A\n\n{implementation}" |> printSignatures |> assertEqualIgnoreLineEnding $"\nmodule A\n\n{signature}" + +let assertSignatureRoundtrip (implSource: string) = + let generatedSignature = FSharp implSource |> printSignatures + Fsi generatedSignature + |> withAdditionalSourceFile (FsSource implSource) + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore diff --git a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs index cb0f5da188b..d4b4815e086 100644 --- a/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs @@ -171,7 +171,7 @@ type DataItem<'data> = Data: 'data } - static member inline Create: item: ^input -> DataItem<^input> when ^input: (member get_StringValue: unit -> string) and ^input: (member get_FriendlyStringValue: unit -> string) + static member inline Create<^input when ^input: (member get_StringValue: unit -> string) and ^input: (member get_FriendlyStringValue: unit -> string)> : item: ^input -> DataItem<^input> static member Create<'data> : identifier: string * label: string * data: 'data -> DataItem<'data>""" @@ -683,3 +683,95 @@ type A private () = |> compile |> shouldSucceed |> ignore + +// ========================================================================= +// Corpus-wide roundtrip sweep failures (1483 standalone .fs files swept). +// Each test below is a REAL sig-gen bug found in POSITIVE (legit) test code. +// Negative tests (intentionally broken code) are excluded. +// ========================================================================= + +// Sweep: single-case struct DU gets spurious bar (FS0300) +// Source: tests/fsharp/core/fsfromfsviacs/lib.fs — #19597 +[] +let ``Sweep - single-case struct DU roundtrips`` () = + assertSignatureRoundtrip """ +module Repro +[] +type U0 = U0 +""" + +// Sweep: backticked active pattern case names (FS0010) — #19592 +[] +let ``Sweep - backticked active pattern roundtrips`` () = + assertSignatureRoundtrip """ +module Repro +let (|``A B``|) (x:int) = x * 2 +""" + +// Sweep: type param with special chars needs backtick escaping (FS0010) — #19595 +[] +let ``Sweep - type param with angle brackets roundtrips`` () = + assertSignatureRoundtrip """ +module Repro +type Foo<'a, 'b>() = + member _.Bar<'``c``> (x: '``c``) = x +""" + +// Sweep: SRTP multi-witness constraint lost in generated sig (FS0340) +// Source: tests/fsharp/typecheck/sigs/pos36-srtp-lib.fs +[] +let ``Sweep - SRTP multi-witness constraint roundtrips`` () = + assertSignatureRoundtrip """ +module Lib + +let inline RequireM< ^Witnesses, ^T when (^Witnesses or ^T): (static member M : ^T -> string) > (x: ^T) : string = + ((^Witnesses or ^T): (static member M : ^T -> string) x) + +type C(p:int) = + member x.P = p + +type Witnesses() = + static member M (x: C) : string = sprintf "M(C), x = %d" x.P + static member M (x: int64) : string = sprintf "M(int64), x = %d" x + +type StaticMethods = + static member inline M< ^T when (Witnesses or ^T): (static member M: ^T -> string)> (x: ^T) : string = + RequireM< Witnesses, ^T> (x) +""" + +// Sweep: type application syntax — fixed by SRTP explicit type param change +// Source: tests/fsharp/typecheck/sigs/pos34.fs +[] +let ``Sweep - type application in member sig roundtrips`` () = + assertSignatureRoundtrip """ +module Pos34 + +[] +type Foo<'bar>() = + member inline _.Baz<'a> (x: 'a) = x +""" + +// Sweep: multi-case active pattern — fixed by backtick escaping change +// Source: tests/fsharp/typecheck/sigs/pos16.fs +[] +let ``Sweep - active pattern in sig roundtrips`` () = + assertSignatureRoundtrip """ +module Pos16 + +let (|A|B|) (x: int) = if x > 0 then A else B +""" + +// Sweep: overloaded member with unit parameter (FS0193) — #19596 +// Roundtrip fails: member M(()) generates sig 'member M: unit -> unit' but +// conformance checker can't match it when M is overloaded. The sig syntax +// is correct but the conformance check for unit-parameter overloads is broken. +[ unit fails when overloaded - FS0193")>] +let ``Sweep - overloaded member with unit param roundtrips`` () = + assertSignatureRoundtrip """ +module Repro +type R1 = { f1 : int } +type D() = + member x.N = x.M { f1 = 3 } + member x.M((y: R1)) = () + member x.M(()) = () +""" \ No newline at end of file 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 c1b468804d5..92c1330f2e6 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 @@ -6569,6 +6569,7 @@ FSharp.Compiler.Syntax.PrettyNaming: Microsoft.FSharp.Collections.FSharpList`1[S FSharp.Compiler.Syntax.PrettyNaming: Microsoft.FSharp.Core.FSharpOption`1[System.String] TryChopPropertyName(System.String) FSharp.Compiler.Syntax.PrettyNaming: System.String CompileOpName(System.String) FSharp.Compiler.Syntax.PrettyNaming: System.String ConvertValLogicalNameToDisplayNameCore(System.String) +FSharp.Compiler.Syntax.PrettyNaming: System.String EscapeActivePatternCases(System.String) FSharp.Compiler.Syntax.PrettyNaming: System.String FormatAndOtherOverloadsString(Int32) FSharp.Compiler.Syntax.PrettyNaming: System.String FsiDynamicModulePrefix FSharp.Compiler.Syntax.PrettyNaming: System.String NormalizeIdentifierBackticks(System.String) diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index b082644ebbc..e696606ff09 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -602,3 +602,49 @@ let normaliz{caret}e' x = x + 1 """ testXmlDocFallbackToSigFileWhileInImplFile sigSource implSource "Normalize with a prime" + +// ========================================================================= +// Tooltip display correctness for signature generation changes +// ========================================================================= + +// Backticked active pattern case names are already tested in +// Signatures.TypeTests.fs via the roundtrip test. +// Testing tooltip resolution for backticked identifiers with spaces +// is not feasible due to QuickParse limitations. + +// SRTP inline function shows type params in tooltip +[] +let ``Tooltip shows type params for SRTP inline function`` () = + Checker.getTooltip """ +module Foo +let inline a{caret}dd (x: ^T) (y: ^T) : ^T = x + y +""" + |> assertAndGetSingleToolTipText + |> fun text -> + // Tooltip shows 'T form (not ^T) with requires clause + Assert.Contains("'T", text) + Assert.Contains("requires", text) + +// Single-case struct DU tooltip shows without leading bar +[] +let ``Tooltip shows single-case struct DU without bar`` () = + Checker.getTooltip """ +module Foo +[] +type U{caret}0 = U0 +""" + |> assertAndGetSingleToolTipText + |> fun text -> + Assert.Contains("U0", text) + +// Inline function type param names are properly displayed in tooltip +[] +let ``Tooltip shows inline function type params properly`` () = + Checker.getTooltip """ +module Foo +let inline fo{caret}o< ^T> (x: ^T) = x +""" + |> assertAndGetSingleToolTipText + |> fun text -> + // Type param appears in tooltip + Assert.Contains("'T", text) diff --git a/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.bsl b/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.bsl index bb7711b1078..3a23769d3ec 100644 --- a/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.bsl +++ b/tests/fsharp/typecheck/overloads/neg_known_return_type_and_known_type_arguments.bsl @@ -10,17 +10,17 @@ Available overloads: - static member Zero.Zero: 'a list * Zero -> 'a list // Argument at index 1 doesn't match - static member Zero.Zero: 'a option * Zero -> 'a option // Argument at index 1 doesn't match - static member Zero.Zero: 'a seq * Zero -> 'a seq // Argument at index 1 doesn't match - - static member Zero.Zero: ('T -> ^Monoid) * Zero -> ('T -> ^Monoid) when (Zero or ^Monoid) : (static member Zero: ^Monoid * Zero -> ^Monoid) // Argument at index 1 doesn't match - - static member Zero.Zero: Async<^a> * Zero -> Async<^a> when (Zero or ^a) : (static member Zero: ^a * Zero -> ^a) // Argument at index 1 doesn't match - - static member Zero.Zero: Lazy<^a> * Zero -> Lazy<^a> when (Zero or ^a) : (static member Zero: ^a * Zero -> ^a) // Argument at index 1 doesn't match - static member Zero.Zero: Map<'a,'b> * Zero -> Map<'a,'b> when 'a: comparison // Argument at index 1 doesn't match - static member Zero.Zero: ResizeArray<'a> * Zero -> ResizeArray<'a> // Argument at index 1 doesn't match - static member Zero.Zero: Set<'a> * Zero -> Set<'a> when 'a: comparison // Argument at index 1 doesn't match - static member Zero.Zero: System.TimeSpan * Zero -> System.TimeSpan // Argument at index 1 doesn't match - - static member Zero.Zero: ^t * Default1 -> ('a1 -> 'a1) when ^t: null and ^t: struct // Argument at index 1 doesn't match - - static member Zero.Zero: ^t * Default1 -> ^t when ^t: (static member Zero: ^t) // Argument at index 1 doesn't match - - static member Zero.Zero: ^t * Default2 -> ('a1 -> 'a1) when ^t: null and ^t: struct // Argument at index 1 doesn't match - - static member Zero.Zero: ^t * Default2 -> ^t when (FromInt32 or ^t) : (static member FromInt32: ^t * FromInt32 -> (int32 -> ^t)) // Argument at index 1 doesn't match - - static member Zero.Zero: ^t * Default3 -> ^t when ^t: (static member Empty: ^t) // Argument at index 1 doesn't match - static member Zero.Zero: string * Zero -> string // Argument at index 1 doesn't match - static member Zero.Zero: unit * Zero -> unit // Argument at index 1 doesn't match + - static member Zero.Zero<'T,^Monoid when (Zero or ^Monoid) : (static member Zero: ^Monoid * Zero -> ^Monoid)> : ('T -> ^Monoid) * Zero -> ('T -> ^Monoid) // Argument at index 1 doesn't match + - static member Zero.Zero<^a when (Zero or ^a) : (static member Zero: ^a * Zero -> ^a)> : Async<^a> * Zero -> Async<^a> // Argument at index 1 doesn't match + - static member Zero.Zero<^a when (Zero or ^a) : (static member Zero: ^a * Zero -> ^a)> : Lazy<^a> * Zero -> Lazy<^a> // Argument at index 1 doesn't match + - static member Zero.Zero<^t when (FromInt32 or ^t) : (static member FromInt32: ^t * FromInt32 -> (int32 -> ^t))> : ^t * Default2 -> ^t // Argument at index 1 doesn't match + - static member Zero.Zero<^t when ^t: (static member Empty: ^t)> : ^t * Default3 -> ^t // Argument at index 1 doesn't match + - static member Zero.Zero<^t when ^t: (static member Zero: ^t)> : ^t * Default1 -> ^t // Argument at index 1 doesn't match + - static member Zero.Zero<^t,'a1 when ^t: null and ^t: struct> : ^t * Default1 -> ('a1 -> 'a1) // Argument at index 1 doesn't match + - static member Zero.Zero<^t,'a1 when ^t: null and ^t: struct> : ^t * Default2 -> ('a1 -> 'a1) // Argument at index 1 doesn't match diff --git a/tests/fsharp/typecheck/sigs/neg117.bsl b/tests/fsharp/typecheck/sigs/neg117.bsl index 2e029e468fe..2f67892c15d 100644 --- a/tests/fsharp/typecheck/sigs/neg117.bsl +++ b/tests/fsharp/typecheck/sigs/neg117.bsl @@ -6,5 +6,5 @@ Known return type: ('a -> Neg117.TargetA.M1 Microsoft.FSharp.Core.array) Known type parameters: < Neg117.TargetA.M1 Microsoft.FSharp.Core.array , Microsoft.FSharp.Core.obj , Neg117.Superpower.Transformer > Available overloads: - - static member Neg117.Superpower.Transformer.Transform: ^f * Neg117.TargetB.TargetB * Neg117.Superpower.Transformer -> (Neg117.TargetB.TransformerKind -> ^f) when (Neg117.TargetB.TargetB or ^f) : (static member Transform: ^f * Neg117.TargetB.TargetB -> (Neg117.TargetB.TransformerKind -> ^f)) // Argument at index 1 doesn't match - - static member Neg117.Superpower.Transformer.Transform: ^r * Neg117.TargetA.TargetA * Neg117.Superpower.Transformer -> (Neg117.TargetA.TransformerKind -> ^r) when (Neg117.TargetA.TargetA or ^r) : (static member Transform: ^r * Neg117.TargetA.TargetA -> (Neg117.TargetA.TransformerKind -> ^r)) // Argument at index 1 doesn't match + - static member Neg117.Superpower.Transformer.Transform<^f when (Neg117.TargetB.TargetB or ^f) : (static member Transform: ^f * Neg117.TargetB.TargetB -> (Neg117.TargetB.TransformerKind -> ^f))> : ^f * Neg117.TargetB.TargetB * Neg117.Superpower.Transformer -> (Neg117.TargetB.TransformerKind -> ^f) // Argument at index 1 doesn't match + - static member Neg117.Superpower.Transformer.Transform<^r when (Neg117.TargetA.TargetA or ^r) : (static member Transform: ^r * Neg117.TargetA.TargetA -> (Neg117.TargetA.TransformerKind -> ^r))> : ^r * Neg117.TargetA.TargetA * Neg117.Superpower.Transformer -> (Neg117.TargetA.TransformerKind -> ^r) // Argument at index 1 doesn't match diff --git a/tests/fsharp/typecheck/sigs/neg119a.bsl b/tests/fsharp/typecheck/sigs/neg119a.bsl index 056420911b0..159f0caa9c3 100644 --- a/tests/fsharp/typecheck/sigs/neg119a.bsl +++ b/tests/fsharp/typecheck/sigs/neg119a.bsl @@ -9,4 +9,4 @@ Available overloads: - static member Applicatives.Ap.Return: 'a seq * Ap: Applicatives.Ap -> ('a -> 'a seq) // Argument at index 1 doesn't match - static member Applicatives.Ap.Return: ('r -> 'a) * Ap: Applicatives.Ap -> (('a -> 'r -> 'a2) -> 'a3 -> 'a -> 'r -> 'a2) // Argument at index 1 doesn't match - static member Applicatives.Ap.Return: System.Tuple<'a> * Ap: Applicatives.Ap -> ('a -> System.Tuple<'a>) // Argument at index 1 doesn't match - - static member Applicatives.Ap.Return: r: ^R * obj -> ('a1 -> ^R) when ^R: (static member Return: 'a1 -> ^R) // Argument 'r' doesn't match Consider adding further type constraints + - static member Applicatives.Ap.Return<^R,'a1 when ^R: (static member Return: 'a1 -> ^R)> : r: ^R * obj -> ('a1 -> ^R) // Argument 'r' doesn't match Consider adding further type constraints diff --git a/tests/fsharp/typecheck/sigs/neg119b.bsl b/tests/fsharp/typecheck/sigs/neg119b.bsl index 414486b9017..420fd26dc74 100644 --- a/tests/fsharp/typecheck/sigs/neg119b.bsl +++ b/tests/fsharp/typecheck/sigs/neg119b.bsl @@ -9,4 +9,4 @@ Available overloads: - static member Applicatives.Ap.Return: 'a seq * Ap: Applicatives.Ap -> ('a -> 'a seq) // Argument at index 1 doesn't match - static member Applicatives.Ap.Return: ('r -> 'a) * Ap: Applicatives.Ap -> (('a -> 'r -> 'a2) -> 'a3 -> 'a -> 'r -> 'a2) // Argument at index 1 doesn't match - static member Applicatives.Ap.Return: System.Tuple<'a> * Ap: Applicatives.Ap -> ('a -> System.Tuple<'a>) // Argument at index 1 doesn't match - - static member Applicatives.Ap.Return: r: ^R * obj -> ('a1 -> ^R) when ^R: (static member Return: 'a1 -> ^R) // Argument 'r' doesn't match Consider adding further type constraints + - static member Applicatives.Ap.Return<^R,'a1 when ^R: (static member Return: 'a1 -> ^R)> : r: ^R * obj -> ('a1 -> ^R) // Argument 'r' doesn't match Consider adding further type constraints diff --git a/tests/fsharp/typecheck/sigs/neg131.bsl b/tests/fsharp/typecheck/sigs/neg131.bsl index 2319a3914d4..063534f985c 100644 --- a/tests/fsharp/typecheck/sigs/neg131.bsl +++ b/tests/fsharp/typecheck/sigs/neg131.bsl @@ -5,4 +5,4 @@ Known types of arguments: 'a * ('b -> int) Candidates: - static member OverloadsWithSrtp.SomeMethod: x: 'T list * f: ('T list -> int) -> int - - static member OverloadsWithSrtp.SomeMethod: x: ^T * f: (^T -> int) -> int when ^T: (member Length: int) + - static member OverloadsWithSrtp.SomeMethod<^T when ^T: (member Length: int)> : x: ^T * f: (^T -> int) -> int diff --git a/tests/fsharp/typecheck/sigs/neg132.bsl b/tests/fsharp/typecheck/sigs/neg132.bsl index c7d10ed0af0..d878fd49f81 100644 --- a/tests/fsharp/typecheck/sigs/neg132.bsl +++ b/tests/fsharp/typecheck/sigs/neg132.bsl @@ -5,4 +5,4 @@ Known types of arguments: 'a * ('b -> int) Candidates: - static member OverloadsWithSrtp.SomeMethod: x: 'T list * f: ('T list -> int) -> int - - static member OverloadsWithSrtp.SomeMethod: x: ^T * f: (^T -> int) -> int when ^T: (member Length: int) + - static member OverloadsWithSrtp.SomeMethod<^T when ^T: (member Length: int)> : x: ^T * f: (^T -> int) -> int