diff --git a/ILSpy.Tests/Metadata/CustomDebugInformationRowDetailsTests.cs b/ILSpy.Tests/Metadata/CustomDebugInformationRowDetailsTests.cs
index 6a2db951b..f45859430 100644
--- a/ILSpy.Tests/Metadata/CustomDebugInformationRowDetailsTests.cs
+++ b/ILSpy.Tests/Metadata/CustomDebugInformationRowDetailsTests.cs
@@ -138,6 +138,26 @@ public class CustomDebugInformationRowDetailsTests
.Select(h => new CustomDebugInformationEntry(metadataFile, h))
.ToList();
+ [Test]
+ public void Offset_And_Friendly_Kind_Columns_Match_The_Tables_Conventions()
+ {
+ // The Kind column carries the GUID heap offset, the decoded friendly name, and the
+ // raw GUID in one cell; Offset positions the row within the metadata stream like
+ // every other table's Offset column.
+ var metadataFile = BuildPdbFixture();
+ var entries = LoadEntries(metadataFile);
+
+ entries[0].Kind.Should().MatchRegex("^[0-9A-F]{8} - Source Link \\(C# / VB\\) \\[cc110556-a091-4d38-9fec-25ab9a351a6a\\]$");
+ entries[1].Kind.Should().EndWith($"- Unknown [{UnknownKindGuid}]");
+ entries[3].Kind.Should().Contain("State Machine Hoisted Local Scopes (C# / VB)");
+
+ int rowSize = metadataFile.Metadata.GetTableRowSize(TableIndex.CustomDebugInformation);
+ entries[0].Offset.Should().BePositive("the table lives at a real offset inside the PDB metadata");
+ entries.Select(e => e.Offset).Should().BeInAscendingOrder()
+ .And.HaveCount(7);
+ (entries[1].Offset - entries[0].Offset).Should().Be(rowSize);
+ }
+
[Test]
public void RowDetails_Parses_The_Structured_Kinds_Into_Typed_Rows()
{
diff --git a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs
index 70bed1137..ea41f93e7 100644
--- a/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs
+++ b/ILSpy/Metadata/DebugTables/CustomDebugInformationTableTreeNode.cs
@@ -45,9 +45,9 @@ namespace ILSpy.Metadata.DebugTables
///
/// View of the CustomDebugInformation table — extensible per-entity payloads used for
/// async/iterator state-machine info, embedded source, source-link JSON, and similar
- /// debug-time data. Each row carries a Parent token, a Kind GUID, and an opaque Value
- /// blob. The friendly-name decoding of well-known Kind GUIDs (StateMachineHoistedLocalScopes,
- /// SourceLink, etc.) is deferred until the Phase 4 cell-tooltip work.
+ /// debug-time data. Each row carries a Parent token, a Kind GUID (decoded to a friendly
+ /// name in the Kind column), and an opaque Value blob whose parsed contents show as the
+ /// row's details.
///
public sealed class CustomDebugInformationTableTreeNode : MetadataTableTreeNode
{
@@ -106,14 +106,58 @@ namespace ILSpy.Metadata.DebugTables
[ColumnInfo("X8")]
public int Token => MetadataTokens.GetToken(handle);
+ [ColumnInfo("X8")]
+ public int Offset => GetRowOffset(metadataFile, TableIndex.CustomDebugInformation, RID);
+
[ColumnInfo("X8", Kind = ColumnKind.Token)]
public int Parent => MetadataTokens.GetToken(debugInfo.Parent);
string? parentTooltip;
public string? ParentTooltip => GenerateTooltip(ref parentTooltip, metadataFile, debugInfo.Parent);
- [ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
- public int Kind => MetadataTokens.GetHeapOffset(debugInfo.Kind);
+ static readonly (Guid Guid, string Name)[] knownKindNames = {
+ (KnownGuids.StateMachineHoistedLocalScopes, "State Machine Hoisted Local Scopes (C# / VB)"),
+ (KnownGuids.DynamicLocalVariables, "Dynamic Local Variables (C#)"),
+ (KnownGuids.DefaultNamespaces, "Default Namespaces (VB)"),
+ (KnownGuids.EditAndContinueLocalSlotMap, "Edit And Continue Local Slot Map (C# / VB)"),
+ (KnownGuids.EditAndContinueLambdaAndClosureMap, "Edit And Continue Lambda And Closure Map (C# / VB)"),
+ (KnownGuids.EncStateMachineStateMap, "Edit And Continue State Machine State Map (C# / VB)"),
+ (KnownGuids.EmbeddedSource, "Embedded Source (C# / VB)"),
+ (KnownGuids.SourceLink, "Source Link (C# / VB)"),
+ (KnownGuids.MethodSteppingInformation, "Method Stepping Information (C# / VB)"),
+ (KnownGuids.CompilationOptions, "Compilation Options (C# / VB)"),
+ (KnownGuids.CompilationMetadataReferences, "Compilation Metadata References (C# / VB)"),
+ (KnownGuids.TupleElementNames, "Tuple Element Names (C#)"),
+ (KnownGuids.TypeDefinitionDocuments, "Type Definition Documents (C# / VB)"),
+ };
+
+ string? kindString;
+
+ ///
+ /// One cell carrying the GUID heap offset, the decoded friendly name of well-known
+ /// kind GUIDs, and the raw GUID, e.g.
+ /// 0000000B - Source Link (C# / VB) [cc110556-...]. Plain text (instead of a
+ /// bare heap offset plus tooltip) so the column filter matches the friendly names.
+ ///
+ public string Kind {
+ get {
+ if (kindString != null)
+ return kindString;
+ if (debugInfo.Kind.IsNil)
+ return kindString = "";
+ var guid = metadataFile.Metadata.GetGuid(debugInfo.Kind);
+ string name = "Unknown";
+ foreach (var (knownGuid, knownName) in knownKindNames)
+ {
+ if (guid == knownGuid)
+ {
+ name = knownName;
+ break;
+ }
+ }
+ return kindString = $"{MetadataTokens.GetHeapOffset(debugInfo.Kind):X8} - {name} [{guid}]";
+ }
+ }
[ColumnInfo("X8", Kind = ColumnKind.HeapOffset)]
public int Value => MetadataTokens.GetHeapOffset(debugInfo.Value);