From 13ee5fe2aa9e7f8b0125f8b1fae527de4417577b Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Wed, 30 Jul 2025 20:35:36 +0200 Subject: [PATCH 1/7] Add test for text as external metadata for enum --- tests/FSharp.Compiler.Service.Tests/Symbols.fs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index 72df8fa58cc..47b2fb23651 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -231,7 +231,7 @@ module Mod2 = mod2.XmlDocSig |> shouldEqual "T:Mod1.Mod2" mod1val1.XmlDocSig |> shouldEqual "P:Mod1.val1" mod2func2.XmlDocSig |> shouldEqual "M:Mod1.Mod2.func2" - + module Attributes = [] let ``Emit conditional attributes`` () = @@ -1249,3 +1249,19 @@ module Delegates = symbols["EventHandler"].IsDelegate |> shouldEqual true symbols["Action"].IsDelegate |> shouldEqual true + +module MetadataAsText = + [] + let ``TryGetMetadataAsText returns metadata for external enum field`` () = + let _, checkResults = getParseAndCheckResults """ +let test = System.DateTimeKind.Utc +""" + let symbolUse = checkResults |> findSymbolUseByName "DateTimeKind" + match symbolUse.Symbol with + | :? FSharpEntity as symbol -> + match symbol.TryGetMetadataText() with + | Some metadataText -> + metadataText.ToString() |> shouldContain "The time represented is UTC" + | None -> + failwith "Expected metadata text, got None" + | _ -> failwith "Expected FSharpEntity symbol" \ No newline at end of file From 0dd810e592ef6123803364b9d7ebaf4afee1712a Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Wed, 30 Jul 2025 20:35:59 +0200 Subject: [PATCH 2/7] Show external metadata for enum in decompiled view --- src/Compiler/Checking/NicePrint.fs | 33 ++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 7dca2705946..58e08caee46 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -305,7 +305,7 @@ module internal PrintUtilities = let layoutXmlDocOfILFieldInfo (denv: DisplayEnv) (infoReader: InfoReader) (finfo: ILFieldInfo) restL = if denv.showDocumentation then GetXmlDocSigOfILFieldInfo infoReader Range.range0 finfo - |> layoutXmlDocFromSig denv infoReader true XmlDoc.Empty restL + |> layoutXmlDocFromSig denv infoReader true XmlDoc.Empty restL else restL @@ -465,9 +465,11 @@ module PrintIL = | None -> WordL.equals ^^ (comment "value unavailable") | Some s -> WordL.equals ^^ wordL s - let layoutILEnumCase nm litVal = + let layoutILEnumCase nm litVal xmlDocOpt = let nameL = ConvertLogicalNameToDisplayLayout (tagEnum >> wordL) nm - WordL.bar ^^ nameL ^^ layoutILFieldInit litVal + match xmlDocOpt with + | Some layout -> layout @@ (WordL.bar ^^ nameL ^^ layoutILFieldInit litVal) + | None -> WordL.bar ^^ nameL ^^ layoutILFieldInit litVal module PrintTypes = // Note: We need nice printing of constants in order to print literals and attributes @@ -2084,6 +2086,9 @@ module TastDefinitionPrinting = IsILFieldInfoAccessible g amap m ad fld && not (isDiscard fld.FieldName) && typeEquiv g ty fld.ApparentEnclosingType) + |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), x) + |> List.sortBy fst + |> List.map snd let ctorLs = if denv.shrinkOverloads then @@ -2108,9 +2113,7 @@ module TastDefinitionPrinting = let ilFieldsL = ilFields - |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), layoutILFieldInfo denv infoReader m x) - |> List.sortBy fst - |> List.map snd + |> List.map (fun x -> layoutILFieldInfo denv infoReader m x) let staticVals = if isRecdTy g ty then @@ -2225,6 +2228,18 @@ module TastDefinitionPrinting = else (lhsL ^^ WordL.equals) -* rhsL + let tryGetFieldXml (layoutList: Layout list) idxOpt = + match idxOpt with + | Some i -> + let t = layoutList[i] + match t with + | Node (left, _, _) -> + match left with + | Node (_, right, _) -> Some right + | _ -> None + | _ -> None + | None -> None + let typeDeclL = match repr with @@ -2328,8 +2343,10 @@ module TastDefinitionPrinting = | TILObjectRepr _ when tycon.ILTyconRawMetadata.IsEnum -> infoReader.GetILFieldInfosOfType (None, ad, m, ty) - |> List.filter (fun x -> x.FieldName <> "value__") - |> List.map (fun x -> PrintIL.layoutILEnumCase x.FieldName x.LiteralValue) + |> List.filter (fun (x: ILFieldInfo) -> x.FieldName <> "value__") + |> List.map (fun (x: ILFieldInfo) -> + let xmlDocOpt = List.tryFindIndex (fun (e: ILFieldInfo) -> e.FieldName = x.FieldName) ilFields |> tryGetFieldXml ilFieldsL + PrintIL.layoutILEnumCase x.FieldName x.LiteralValue xmlDocOpt) |> applyMaxMembers denv.maxMembers |> aboveListL |> addLhs From 5bc0e84051cc28c792854aee76ec39a86cffc88e Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Wed, 30 Jul 2025 20:58:35 +0200 Subject: [PATCH 3/7] Remove whitespace --- tests/FSharp.Compiler.Service.Tests/Symbols.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index 47b2fb23651..44dc501458d 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -231,7 +231,7 @@ module Mod2 = mod2.XmlDocSig |> shouldEqual "T:Mod1.Mod2" mod1val1.XmlDocSig |> shouldEqual "P:Mod1.val1" mod2func2.XmlDocSig |> shouldEqual "M:Mod1.Mod2.func2" - + module Attributes = [] let ``Emit conditional attributes`` () = From d5a7d8ded578922633d1c27df12d6c51914af1c1 Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Sat, 2 Aug 2025 09:46:54 +0200 Subject: [PATCH 4/7] Preserve ilFields variable in function scope, but sort locally for enum nice printing case --- src/Compiler/Checking/NicePrint.fs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 58e08caee46..9adf661c28f 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -2086,9 +2086,6 @@ module TastDefinitionPrinting = IsILFieldInfoAccessible g amap m ad fld && not (isDiscard fld.FieldName) && typeEquiv g ty fld.ApparentEnclosingType) - |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), x) - |> List.sortBy fst - |> List.map snd let ctorLs = if denv.shrinkOverloads then @@ -2113,7 +2110,9 @@ module TastDefinitionPrinting = let ilFieldsL = ilFields - |> List.map (fun x -> layoutILFieldInfo denv infoReader m x) + |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), layoutILFieldInfo denv infoReader m x) + |> List.sortBy fst + |> List.map snd let staticVals = if isRecdTy g ty then @@ -2342,10 +2341,15 @@ module TastDefinitionPrinting = |> addLhs | TILObjectRepr _ when tycon.ILTyconRawMetadata.IsEnum -> - infoReader.GetILFieldInfosOfType (None, ad, m, ty) + let ilFieldsSorted = + ilFields + |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), x) + |> List.sortBy fst + |> List.map snd + infoReader.GetILFieldInfosOfType (None, ad, m, ty) |> List.filter (fun (x: ILFieldInfo) -> x.FieldName <> "value__") |> List.map (fun (x: ILFieldInfo) -> - let xmlDocOpt = List.tryFindIndex (fun (e: ILFieldInfo) -> e.FieldName = x.FieldName) ilFields |> tryGetFieldXml ilFieldsL + let xmlDocOpt = List.tryFindIndex (fun (e: ILFieldInfo) -> e.FieldName = x.FieldName) ilFieldsSorted |> tryGetFieldXml ilFieldsL PrintIL.layoutILEnumCase x.FieldName x.LiteralValue xmlDocOpt) |> applyMaxMembers denv.maxMembers |> aboveListL From c44fa716b050b242458676ed55d4dfe44bde799b Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Thu, 7 Aug 2025 19:54:55 +0200 Subject: [PATCH 5/7] Updated baseline files --- .../ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 756a6fa55d7..ce3418ffedc 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -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@2070-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@2072-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 b2afcae84a3..d41b70e500f 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -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@1873'][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@2070-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@2072-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. From 7fb76c00ebf209523f800bedfd8c0dd04b15275b Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Mon, 11 Aug 2025 18:40:10 +0200 Subject: [PATCH 6/7] Address comments from PR with general code quality improvements --- src/Compiler/Checking/NicePrint.fs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Compiler/Checking/NicePrint.fs b/src/Compiler/Checking/NicePrint.fs index 9adf661c28f..66b26e43a91 100644 --- a/src/Compiler/Checking/NicePrint.fs +++ b/src/Compiler/Checking/NicePrint.fs @@ -2110,9 +2110,8 @@ module TastDefinitionPrinting = let ilFieldsL = ilFields - |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), layoutILFieldInfo denv infoReader m x) - |> List.sortBy fst - |> List.map snd + |> List.sortBy (fun x -> x.IsStatic, x.FieldName) + |> List.map (fun x -> layoutILFieldInfo denv infoReader m x) let staticVals = if isRecdTy g ty then @@ -2229,15 +2228,11 @@ module TastDefinitionPrinting = let tryGetFieldXml (layoutList: Layout list) idxOpt = match idxOpt with - | Some i -> - let t = layoutList[i] - match t with - | Node (left, _, _) -> - match left with - | Node (_, right, _) -> Some right - | _ -> None + | Some i when i >= 0 && i < layoutList.Length -> + match layoutList[i] with + | Node (Node (_, right, _), _, _) -> Some right | _ -> None - | None -> None + | _ -> None let typeDeclL = @@ -2343,9 +2338,7 @@ module TastDefinitionPrinting = | TILObjectRepr _ when tycon.ILTyconRawMetadata.IsEnum -> let ilFieldsSorted = ilFields - |> List.map (fun x -> (true, x.IsStatic, x.FieldName, 0, 0), x) - |> List.sortBy fst - |> List.map snd + |> List.sortBy (fun x -> x.IsStatic, x.FieldName) infoReader.GetILFieldInfosOfType (None, ad, m, ty) |> List.filter (fun (x: ILFieldInfo) -> x.FieldName <> "value__") |> List.map (fun (x: ILFieldInfo) -> From 5f8d8ffa8776de84f6b38696cde39f35d8abd4e6 Mon Sep 17 00:00:00 2001 From: Thobias Larsen Date: Tue, 12 Aug 2025 11:58:06 +0200 Subject: [PATCH 7/7] Added release note for fix --- docs/release-notes/.FSharp.Compiler.Service/10.0.100.md | 1 + 1 file changed, 1 insertion(+) 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 7256a47504c..6d84886e0ed 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -18,6 +18,7 @@ * Fix active pattern typechecking regression. ([Issue #18638](https://github.com/dotnet/fsharp/issues/18638), [PR #18642](https://github.com/dotnet/fsharp/pull/18642)) * Fix nullness warnings when casting non-nullable values to `IEquatable` to match C# behavior. ([Issue #18759](https://github.com/dotnet/fsharp/issues/18759)) * Fix IsByRefLikeAttribute types being incorrectly suppressed in completion lists. Types like `Span` and `ReadOnlySpan` now appear correctly in IntelliSense. +* 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)) ### Changed * Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645))