From 93feba079a39981f27a81c2434ea03226cee73a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 22:02:33 +0000 Subject: [PATCH 1/4] Initial plan From a302a1af27bb3477f368117d723605c79a04e56a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:44:02 +0000 Subject: [PATCH 2/4] Changes before error encountered Agent-Logs-Url: https://github.com/dotnet/fsharp/sessions/b27c5791-48eb-4cc0-98ea-a88977b5f4c5 Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com> --- src/Compiler/Checking/NicePrint.fs | 3 +- src/Compiler/TypedTree/TypedTreeOps.fs | 9 ++- .../TooltipTests.fs | 58 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) 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.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 7da5a63eef..83b9c7007f 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3433,7 +3433,14 @@ let fullDisplayTextOfValRefAsLayout (vref: ValRef) = | 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 | ValueSome pathText -> diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index b082644ebb..9e1828335f 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -383,6 +383,17 @@ let assertAndExtractTooltip (ToolTipText(items)) = |> taggedTextsToString toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextsToString | _ -> failwith $"Expected group, got {items[0]}" + +let getMainDescriptionTags (ToolTipText(items)) = + match items with + | ToolTipElement.Group [ singleElement ] :: _ -> singleElement.MainDescription + | _ -> failwith $"Expected group, 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 description = tags |> Array.map (fun t -> sprintf "(%A, %s)" t.Tag t.Text) |> String.concat ", " + Assert.True(found, sprintf "Expected to find tag %A with text '%s' in tooltip, but found: %s" expectedTag expectedName description) let assertAndGetSingleToolTipText items = let text,_xml,_remarks = assertAndExtractTooltip items @@ -602,3 +613,50 @@ 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 ``Instance property should be tagged as Property in tooltip`` () = + 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 ``Instance member without parameters should be tagged as Member in tooltip`` () = + Checker.getTooltip """ +type T() = + member x.Valu{caret}e = 42 +""" + |> assertNameTagInTooltip TextTag.Member "Value" From c62ee79d3220371693acc735eaf5b7c4e7ec51f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:41:57 +0000 Subject: [PATCH 3/4] Fix methods tagged as Member instead of Method in tooltips (#10540) In NicePrint.fs, use tagMethod for SynMemberKind.Member when argInfos is non-empty (indicating a method), tagMember otherwise. In TypedTreeOps.fs, check ValReprInfo arg groups to distinguish methods from property-like members in fullDisplayTextOfValRefAsLayout. Add regression tests and release notes. Agent-Logs-Url: https://github.com/dotnet/fsharp/sessions/bc1d07d8-2d87-4e39-9610-ca7f79ae7d79 Co-authored-by: abonie <20281641+abonie@users.noreply.github.com> --- .../.FSharp.Compiler.Service/11.0.100.md | 1 + .../TooltipTests.fs | 35 ++++++++++--------- 2 files changed, 19 insertions(+), 17 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 d5c2087765..a8354f05aa 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -17,6 +17,7 @@ * Fix `YieldFromFinal`/`ReturnFromFinal` being incorrectly called in non-tail positions (`for`, `use`, `use!`, `try/with` handler). ([Issue #19402](https://github.com/dotnet/fsharp/issues/19402), [PR #19403](https://github.com/dotnet/fsharp/pull/19403)) * Fixed how the source ranges of warn directives are reported (as trivia) in the parser output (by not reporting leading spaces). ([Issue #19405](https://github.com/dotnet/fsharp/issues/19405), [PR #19408]((https://github.com/dotnet/fsharp/pull/19408))) * Fix UoM value type `ToString()` returning garbage values when `--checknulls+` is enabled, caused by double address-taking in codegen. ([Issue #19435](https://github.com/dotnet/fsharp/issues/19435), [PR #19440](https://github.com/dotnet/fsharp/pull/19440)) +* Fix methods being tagged as `Member` instead of `Method` in tooltips. ([Issue #10540](https://github.com/dotnet/fsharp/issues/10540), [PR #19501](https://github.com/dotnet/fsharp/pull/19501)) ### Added diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 9e1828335f..385ba3539c 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -383,21 +383,22 @@ let assertAndExtractTooltip (ToolTipText(items)) = |> taggedTextsToString toolTipText, singleElement.XmlDoc, singleElement.Remarks |> Option.map taggedTextsToString | _ -> failwith $"Expected group, got {items[0]}" + +let assertAndGetSingleToolTipText items = + let text,_xml,_remarks = assertAndExtractTooltip items + text let getMainDescriptionTags (ToolTipText(items)) = match items with | ToolTipElement.Group [ singleElement ] :: _ -> singleElement.MainDescription - | _ -> failwith $"Expected group, got {items}" + | _ -> 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 description = tags |> Array.map (fun t -> sprintf "(%A, %s)" t.Tag t.Text) |> String.concat ", " - Assert.True(found, sprintf "Expected to find tag %A with text '%s' in tooltip, but found: %s" expectedTag expectedName description) - -let assertAndGetSingleToolTipText items = - let text,_xml,_remarks = assertAndExtractTooltip items - text + if not found then + let desc = tags |> Array.map (fun t -> sprintf "(%A, %s)" t.Tag t.Text) |> String.concat ", " + failwith (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") @@ -643,20 +644,20 @@ type T() = // https://github.com/dotnet/fsharp/issues/10540 [] -let ``Instance property should be tagged as Property in tooltip`` () = +let ``Property-like member should be tagged as Property`` () = Checker.getTooltip """ -namespace Foo - -type Bar() = - member val Fo{caret}o = "bla" with get, set +type T() = + member x.Valu{caret}e = 42 """ - |> assertNameTagInTooltip TextTag.Property "Foo" + |> assertNameTagInTooltip TextTag.Property "Value" // https://github.com/dotnet/fsharp/issues/10540 [] -let ``Instance member without parameters should be tagged as Member in tooltip`` () = +let ``Auto property should be tagged as Property`` () = Checker.getTooltip """ -type T() = - member x.Valu{caret}e = 42 +namespace Foo + +type Bar() = + member val Fo{caret}o = "bla" with get, set """ - |> assertNameTagInTooltip TextTag.Member "Value" + |> assertNameTagInTooltip TextTag.Property "Foo" From 7bd4f64620823c802b3f7c2400bffc7e3fe9e42e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:46:22 +0000 Subject: [PATCH 4/4] Address review: use Assert.True instead of failwith in test helper Agent-Logs-Url: https://github.com/dotnet/fsharp/sessions/bc1d07d8-2d87-4e39-9610-ca7f79ae7d79 Co-authored-by: abonie <20281641+abonie@users.noreply.github.com> --- tests/FSharp.Compiler.Service.Tests/TooltipTests.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs index 385ba3539c..f510684e1f 100644 --- a/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TooltipTests.fs @@ -396,9 +396,8 @@ let getMainDescriptionTags (ToolTipText(items)) = let assertNameTagInTooltip expectedTag expectedName (tooltip: ToolTipText) = let tags = getMainDescriptionTags tooltip let found = tags |> Array.exists (fun t -> t.Tag = expectedTag && t.Text = expectedName) - if not found then - let desc = tags |> Array.map (fun t -> sprintf "(%A, %s)" t.Tag t.Text) |> String.concat ", " - failwith (sprintf "Expected tag %A with text '%s' in tooltip, but found: %s" expectedTag expectedName desc) + 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")