From ac1b89108eca721697f9a282db46eb5e7b2f32bf Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 14 Apr 2026 10:35:40 +0200 Subject: [PATCH 1/3] Fix methods tagged as Member instead of Method in tooltips (#10540) Rebase onto main after TypedTreeOps split. Apply the change to TypedTreeOps.FreeVars.fs (previously TypedTreeOps.fs). - NicePrint.fs: check isNil argInfos to use tagMethod for methods vs tagMember - TypedTreeOps.FreeVars.fs: check ValReprInfo.ArgInfos to distinguish methods - 8 regression tests: methods, properties, indexers, explicit getters - Release notes added Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + src/Compiler/Checking/NicePrint.fs | 3 +- .../TypedTree/TypedTreeOps.FreeVars.fs | 9 +- .../TooltipTests.fs | 97 +++++++++++++++++++ 4 files changed, 108 insertions(+), 2 deletions(-) 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 7751906ad1..b68913218a 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -26,6 +26,7 @@ * Fix box instruction for literal upcasts. (Issue [#18319](https://github.com/dotnet/fsharp/issues/18319), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) * Fix Decimal Literal causes InvalidProgramException in Debug builds. (Issue [#18956](https://github.com/dotnet/fsharp/issues/18956), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) * Fix `AttributeUsage.AllowMultiple` not being inherited for attributes subclassed in C#. ([Issue #17107](https://github.com/dotnet/fsharp/issues/17107), [PR #19315](https://github.com/dotnet/fsharp/pull/19315)) +* Fix methods being tagged as `Member` instead of `Method` in tooltips. ([Issue #10540](https://github.com/dotnet/fsharp/issues/10540), [PR #19507](https://github.com/dotnet/fsharp/pull/19507)) ### Added diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 62c95f6bbf..844dd4446b 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -1383,7 +1383,8 @@ module PrintTastMemberOrVals = let resL = if short then tauL else - let nameL = layoutMemberName denv vref niceMethodTypars argInfos tagMember vref.DisplayNameCoreMangled true + let tag = if isNil argInfos then tagMember else tagMethod + let nameL = layoutMemberName denv vref niceMethodTypars argInfos tag vref.DisplayNameCoreMangled true let nameL = if short then nameL else mkInlineL denv vref.Deref nameL stat --- ((nameL |> addColonL) ^^ tauL) prettyTyparInst, resL diff --git a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs index 92649150c9..16bd0944ce 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs @@ -1449,7 +1449,14 @@ module internal MemberRepresentation = | SynMemberKind.PropertyGetSet -> tagProperty vref.DisplayName | SynMemberKind.ClassConstructor | SynMemberKind.Constructor -> tagMethod vref.DisplayName - | SynMemberKind.Member -> tagMember vref.DisplayName + | SynMemberKind.Member -> + match vref.ValReprInfo with + | Some valReprInfo -> + let numArgGroups = valReprInfo.ArgInfos.Length + let isMethod = if memberInfo.MemberFlags.IsInstance then numArgGroups > 1 else numArgGroups > 0 + if isMethod then tagMethod vref.DisplayName + else tagMember vref.DisplayName + | None -> tagMember vref.DisplayName match fullNameOfParentOfValRefAsLayout vref with | ValueNone -> wordL n diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index b082644ebb..11f132ad60 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -388,6 +388,17 @@ let assertAndGetSingleToolTipText items = let text,_xml,_remarks = assertAndExtractTooltip items text +let getMainDescriptionTags (ToolTipText(items)) = + match items with + | ToolTipElement.Group [ singleElement ] :: _ -> singleElement.MainDescription + | _ -> failwith $"Expected single group in tooltip, got {items}" + +let assertNameTagInTooltip expectedTag expectedName (tooltip: ToolTipText) = + let tags = getMainDescriptionTags tooltip + let found = tags |> Array.exists (fun t -> t.Tag = expectedTag && t.Text = expectedName) + let desc = tags |> Array.map (fun t -> sprintf "(%A, %s)" t.Tag t.Text) |> String.concat ", " + Assert.True(found, sprintf "Expected tag %A with text '%s' in tooltip, but found: %s" expectedTag expectedName desc) + let normalize (s: string) = s.Replace("\r\n", "\n").Replace("\n\n", "\n") [] @@ -602,3 +613,89 @@ let normaliz{caret}e' x = x + 1 """ testXmlDocFallbackToSigFileWhileInImplFile sigSource implSource "Normalize with a prime" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Instance method should be tagged as Method in tooltip`` () = + Checker.getTooltip """ +type T() = + member x.Metho{caret}d() = () +""" + |> assertNameTagInTooltip TextTag.Method "Method" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Instance method with parameters should be tagged as Method in tooltip`` () = + Checker.getTooltip """ +type T() = + member x.Ad{caret}d(a: int, b: int) = a + b +""" + |> assertNameTagInTooltip TextTag.Method "Add" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Static method should be tagged as Method in tooltip`` () = + Checker.getTooltip """ +type T() = + static member Creat{caret}e() = T() +""" + |> assertNameTagInTooltip TextTag.Method "Create" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Property-like member should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + member x.Valu{caret}e = 42 +""" + |> assertNameTagInTooltip TextTag.Property "Value" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Auto property should be tagged as Property`` () = + Checker.getTooltip """ +namespace Foo + +type Bar() = + member val Fo{caret}o = "bla" with get, set +""" + |> assertNameTagInTooltip TextTag.Property "Foo" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Indexer should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + member x.Ite{caret}m with get(i: int) = i +""" + |> assertNameTagInTooltip TextTag.Property "Item" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Indexer with getter and setter should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + let mutable data = [| 0; 1; 2 |] + member x.Ite{caret}m + with get(i: int) = data.[i] + and set (i: int) (v: int) = data.[i] <- v +""" + |> assertNameTagInTooltip TextTag.Property "Item" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Property with explicit getter should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + member x.Valu{caret}e with get() = 42 +""" + |> assertNameTagInTooltip TextTag.Property "Value" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Static property should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + static member Defaul{caret}t = T() +""" + |> assertNameTagInTooltip TextTag.Property "Default" From f2f7cc031c08379c852eacde0df50286bc58d582 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 14 Apr 2026 11:25:44 +0200 Subject: [PATCH 2/3] Apply fantomas formatting to TypedTreeOps.FreeVars.fs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs index 16bd0944ce..054df3371f 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs @@ -1453,9 +1453,17 @@ module internal MemberRepresentation = match vref.ValReprInfo with | Some valReprInfo -> let numArgGroups = valReprInfo.ArgInfos.Length - let isMethod = if memberInfo.MemberFlags.IsInstance then numArgGroups > 1 else numArgGroups > 0 - if isMethod then tagMethod vref.DisplayName - else tagMember vref.DisplayName + + let isMethod = + if memberInfo.MemberFlags.IsInstance then + numArgGroups > 1 + else + numArgGroups > 0 + + if isMethod then + tagMethod vref.DisplayName + else + tagMember vref.DisplayName | None -> tagMember vref.DisplayName match fullNameOfParentOfValRefAsLayout vref with From d6672d9f29295197dd5d444291cd46915824c208 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Wed, 15 Apr 2026 09:39:41 +0200 Subject: [PATCH 3/3] Add tests for named indexed properties with parameters Address review feedback: add explicit tests for named indexed properties (properties with parameters) to verify they are correctly tagged as Property in tooltips. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TooltipTests.fs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 11f132ad60..bb96c163f1 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -699,3 +699,24 @@ type T() = static member Defaul{caret}t = T() """ |> assertNameTagInTooltip TextTag.Property "Default" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Named indexed property with getter should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + member x.Valu{caret}e with get(key: string) = key +""" + |> assertNameTagInTooltip TextTag.Property "Value" + +// https://github.com/dotnet/fsharp/issues/10540 +[] +let ``Named indexed property with getter and setter should be tagged as Property`` () = + Checker.getTooltip """ +type T() = + let mutable store = Map.empty + member x.Valu{caret}e + with get(key: string) = store.[key] + and set (key: string) (v: int) = store <- store.Add(key, v) +""" + |> assertNameTagInTooltip TextTag.Property "Value"