Kiva bdae26f3b4
[LLDB][DWARF] Use the same qualified name computation for Rust (#165840)
Currently LLDB's `ParseRustVariantPart` generates the following
`CXXRecordDecl` for a Rust enum
```rust
enum AA {
  A(u8)
}
```

```
CXXRecordDecl 0x5555568d5970 <<invalid sloc>> <invalid sloc> struct AA
|-CXXRecordDecl 0x5555568d5ab0 <<invalid sloc>> <invalid sloc> union test_issue::AA$Inner definition
| |-CXXRecordDecl 0x5555568d5d18 <<invalid sloc>> <invalid sloc> struct A$Variant definition
| | |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable trivial
| | | `-Destructor simple irrelevant trivial needs_implicit
| | `-FieldDecl 0x555555a77880 <<invalid sloc>> <invalid sloc> value 'test_issue::AA::A'
| `-FieldDecl 0x555555a778f0 <<invalid sloc>> <invalid sloc> $variant$ 'test_issue::AA::test_issue::AA$Inner::A$Variant'
|-CXXRecordDecl 0x5555568d5c48 <<invalid sloc>> <invalid sloc> struct A definition
| `-FieldDecl 0x555555a777e0 <<invalid sloc>> <invalid sloc> __0 'unsigned char'
`-FieldDecl 0x555555a77960 <<invalid sloc>> <invalid sloc> $variants$ 'test_issue::AA::test_issue::AA$Inner'
```

While when the Rust enum type name is the same as its variant name, the
generated `CXXRecordDecl` becomes the following – there's a circular
reference between `struct A$Variant` and `struct A`, causing #163048.

```rust
enum A {
  A(u8)
}
```

```
CXXRecordDecl 0x5555568d5760 <<invalid sloc>> <invalid sloc> struct A
|-CXXRecordDecl 0x5555568d58a0 <<invalid sloc>> <invalid sloc> union test_issue::A$Inner definition
| |-CXXRecordDecl 0x5555568d5a38 <<invalid sloc>> <invalid sloc> struct A$Variant definition
| | `-FieldDecl 0x5555568d5b70 <<invalid sloc>> <invalid sloc> value 'test_issue::A'    <---- bug here
| `-FieldDecl 0x5555568d5be0 <<invalid sloc>> <invalid sloc> $variant$ 'test_issue::A::test_issue::A$Inner::A$Variant'
`-FieldDecl 0x5555568d5c50 <<invalid sloc>> <invalid sloc> $variants$ 'test_issue::A::test_issue::A$Inner'
```

The problem was caused by `GetUniqueTypeNameAndDeclaration` not
returning the correct qualified name for DWARF DIE `test_issue::A::A`,
instead, it returned `A`. This caused `ParseStructureLikeDIE` to find
the wrong type `test_issue::A` and returned early.

The failure in `GetUniqueTypeNameAndDeclaration` appears to stem from a
language check that returns early unless the language is C++. I changed
it so Rust follows the C++ path rather than returning. I’m not entirely
sure this is the right approach — Rust’s qualified name rules look
similar, but not identical? Alternatively, we could add a Rust-specific
implementation that forms qualified names according to Rust's rules.
2025-11-17 10:38:40 +00:00

70 lines
2.5 KiB
Python

"""Helper library to traverse data emitted for Rust enums """
from lldbsuite.test.lldbtest import *
DISCRIMINANT_MEMBER_NAME = "$discr$"
VALUE_MEMBER_NAME = "value"
class RustEnumValue:
def __init__(self, value: lldb.SBValue):
self.value = value
def getAllVariantTypes(self):
result = []
for i in range(self._inner().GetNumChildren()):
result.append(self.getVariantByIndex(i).GetDisplayTypeName())
return result
def _inner(self) -> lldb.SBValue:
return self.value.GetChildAtIndex(0)
def getVariantByIndex(self, index):
return (
self._inner()
.GetChildAtIndex(index)
.GetChildMemberWithName(VALUE_MEMBER_NAME)
)
@staticmethod
def _getDiscriminantValueAsUnsigned(discr_sbvalue: lldb.SBValue):
byte_size = discr_sbvalue.GetType().GetByteSize()
error = lldb.SBError()
# when discriminant is u16 Clang emits 'unsigned char'
# and LLDB seems to treat it as character type disalowing to call GetValueAsUnsigned
if byte_size == 1:
return discr_sbvalue.GetData().GetUnsignedInt8(error, 0)
elif byte_size == 2:
return discr_sbvalue.GetData().GetUnsignedInt16(error, 0)
elif byte_size == 4:
return discr_sbvalue.GetData().GetUnsignedInt32(error, 0)
elif byte_size == 8:
return discr_sbvalue.GetData().GetUnsignedInt64(error, 0)
else:
return discr_sbvalue.GetValueAsUnsigned()
def getCurrentVariantIndex(self):
default_index = 0
for i in range(self._inner().GetNumChildren()):
variant: lldb.SBValue = self._inner().GetChildAtIndex(i)
discr = variant.GetChildMemberWithName(DISCRIMINANT_MEMBER_NAME)
if discr.IsValid():
discr_unsigned_value = RustEnumValue._getDiscriminantValueAsUnsigned(
discr
)
if variant.GetName() == f"$variant${discr_unsigned_value}":
return discr_unsigned_value
else:
default_index = i
return default_index
def getFields(self):
result = []
for i in range(self._inner().GetNumChildren()):
type: lldb.SBType = self._inner().GetType()
result.append(type.GetFieldAtIndex(i).GetName())
return result
def getCurrentValue(self) -> lldb.SBValue:
return self.getVariantByIndex(self.getCurrentVariantIndex())