about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWalnut <39544927+Walnut356@users.noreply.github.com>2025-01-10 21:13:52 -0600
committerWalnut <39544927+Walnut356@users.noreply.github.com>2025-01-10 21:13:52 -0600
commitc425660858fb19e13dde3a28fef824b1c4dd5f48 (patch)
tree8498cd4ba8268ee68f2c709d5e91048833358f9a
parent71b6d49282ee68a980aada44a65d4f6e8f1c8991 (diff)
downloadrust-c425660858fb19e13dde3a28fef824b1c4dd5f48.tar.gz
rust-c425660858fb19e13dde3a28fef824b1c4dd5f48.zip
add msvc enum providers
-rw-r--r--src/etc/lldb_commands6
-rw-r--r--src/etc/lldb_providers.py175
2 files changed, 179 insertions, 2 deletions
diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands
index 23282d2d25b..24c485b3533 100644
--- a/src/etc/lldb_commands
+++ b/src/etc/lldb_commands
@@ -18,9 +18,10 @@ type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZ
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
+type synthetic add -l lldb_lookup.MSVCEnumSyntheticProvider -x "^enum2\$<.+>$" --category Rust
+type synthetic add -l lldb_lookup.synthetic_lookup -x "^enum2\$<.+>::.*$" --category Rust
 type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref\$<slice2\$<.+> >" --category Rust
 type synthetic add -l lldb_lookup.MSVCTupleSyntheticProvider -x "^tuple\$<.+>$" --category Rust
-type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust
 type synthetic add -l lldb_lookup.synthetic_lookup -x "^\(.*\)$" --category Rust
 type summary add -F _ -e -x -h "^.*$" --category Rust
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
@@ -45,5 +46,6 @@ type summary add -F lldb_lookup.summary_lookup  -e -x -h "^(std::([a-z_]+::)+)Pa
 type summary add -F lldb_lookup.summary_lookup  -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
 type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust
 type summary add -F lldb_lookup.StdSliceSummaryProvider -e -x -h "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
-type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
+type summary add -F lldb_lookup.MSVCEnumSummaryProvider -e -x -h "^enum2\$<.+>$" --category Rust
+type summary add -F lldb_lookup.summary_lookup  -e -x -h "^enum2\$<.+>::.*$" --category Rust
 type category enable Rust
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 7be40068b90..b05dd5162f0 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -3,6 +3,8 @@ import sys
 from lldb import (
     SBData,
     SBError,
+    SBType,
+    SBTypeStaticField,
     SBValue,
     eBasicTypeLong,
     eBasicTypeUnsignedLong,
@@ -325,6 +327,179 @@ class ClangEncodedEnumProvider:
                 default_index = i
         return default_index
 
+class MSVCEnumSyntheticProvider:
+    """
+    Synthetic provider for sum-type enums on MSVC. For a detailed explanation of the internals,
+    see:
+
+    https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+    """
+
+    __slots__ = ["valobj", "variant", "value"]
+    def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
+        self.valobj = valobj
+        self.variant: SBValue
+        self.value: SBValue
+        self.update()
+
+    def update(self):
+        tag: SBValue = self.valobj.GetChildMemberWithName("tag")
+
+        if tag.IsValid():
+            tag: int = tag.GetValueAsUnsigned()
+            for child in self.valobj.GetNonSyntheticValue().children:
+                if not child.name.startswith("variant"):
+                    continue
+
+                variant_type: SBType = child.GetType()
+                exact: SBTypeStaticField = variant_type.GetStaticFieldWithName(
+                    "DISCR_EXACT"
+                )
+
+                if exact.IsValid():
+                    discr: int = exact.GetConstantValue(
+                        self.valobj.target
+                    ).GetValueAsUnsigned()
+                    if tag == discr:
+                        self.variant = child
+                        self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                        return
+                else:  # if invalid, DISCR must be a range
+                    begin: int = variant_type.GetStaticFieldWithName(
+                        "DISCR_BEGIN"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+                    end: int = variant_type.GetStaticFieldWithName(
+                        "DISCR_END"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+
+                    # begin isn't necessarily smaller than end, so we must test for both cases
+                    if begin < end:
+                        if begin <= tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                            return
+                    else:
+                        if tag >= begin or tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                            return
+        else:  # if invalid, tag is a 128 bit value
+            tag_lo: int = self.valobj.GetChildMemberWithName("tag128_lo").GetValueAsUnsigned()
+            tag_hi: int = self.valobj.GetChildMemberWithName("tag128_hi").GetValueAsUnsigned()
+
+            tag: int = (tag_hi << 64) | tag_lo
+
+            for child in self.valobj.GetNonSyntheticValue().children:
+                if not child.name.startswith("variant"):
+                    continue
+
+                variant_type: SBType = child.GetType()
+                exact_lo: SBTypeStaticField = variant_type.GetStaticFieldWithName(
+                    "DISCR128_EXACT_LO"
+                )
+
+                if exact_lo.IsValid():
+                    exact_lo: int = exact_lo.GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+                    exact_hi: int = variant_type.GetStaticFieldWithName(
+                    "DISCR128_EXACT_HI"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+
+                    discr: int = (exact_hi << 64) | exact_lo
+                    if tag == discr:
+                        self.variant = child
+                        self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                        return
+                else:  # if invalid, DISCR must be a range
+                    begin_lo: int = variant_type.GetStaticFieldWithName(
+                    "DISCR128_BEGIN_LO"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+                    begin_hi: int = variant_type.GetStaticFieldWithName(
+                    "DISCR128_BEGIN_HI"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+
+                    end_lo: int = variant_type.GetStaticFieldWithName(
+                    "DISCR128_END_LO"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+                    end_hi: int = variant_type.GetStaticFieldWithName(
+                    "DISCR128_END_HI"
+                    ).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
+
+                    begin = (begin_hi << 64) | begin_lo
+                    end = (end_hi << 64) | end_lo
+
+                    # begin isn't necessarily smaller than end, so we must test for both cases
+                    if begin < end:
+                        if begin <= tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                            return
+                    else:
+                        if tag >= begin or tag <= end:
+                            self.variant = child
+                            self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
+                            return
+
+    def num_children(self) -> int:
+        return self.value.GetNumChildren()
+
+    def get_child_index(self, name: str) -> int:
+        return self.value.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index: int) -> SBValue:
+        return self.value.GetChildAtIndex(index)
+
+    def has_children(self) -> bool:
+        return self.value.MightHaveChildren()
+
+    def get_type_name(self) -> str:
+        name = self.valobj.GetTypeName()
+        # remove "enum2$<", str.removeprefix() is python 3.9+
+        name = name[7:]
+
+        # MSVC misinterprets ">>" as a shift operator, so spaces are inserted by rust to
+        # avoid that
+        if name.endswith(" >"):
+            name = name[:-2]
+        elif name.endswith(">"):
+            name = name[:-1]
+
+        return name
+
+
+def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
+    enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict)
+    variant_names: SBType = valobj.target.FindFirstType(
+        f"{enum_synth.valobj.GetTypeName()}::VariantNames"
+    )
+    name_idx = (
+        enum_synth.variant.GetType()
+        .GetStaticFieldWithName("NAME")
+        .GetConstantValue(valobj.target)
+        .GetValueAsUnsigned()
+    )
+
+    name: str = variant_names.enum_members[name_idx].name
+
+    if enum_synth.num_children() == 0:
+        return name
+
+    child_name: str = enum_synth.value.GetChildAtIndex(0).name
+    if child_name == "0" or child_name == "__0":
+        # enum variant is a tuple struct
+        return name + TupleSummaryProvider(enum_synth.value, _dict)
+    else:
+        # enum variant is a regular struct
+        var_list = (
+            str(enum_synth.value.GetNonSyntheticValue()).split("= ", 1)[1].splitlines()
+        )
+        vars = [x.strip() for x in var_list if x not in ("{", "}")]
+        if vars[0][0] == "(":
+            vars[0] = vars[0][1:]
+        if vars[-1][-1] == ")":
+            vars[-1] = vars[-1][:-1]
+
+        return f'{name}{{{", ".join(vars)}}}'
+
 
 class TupleSyntheticProvider:
     """Pretty-printer for tuples and tuple enum variants"""