[Lldb-commits] [lldb] [lldb] Print empty enums as if they were unrecognised normal enums (PR #97553)

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 3 03:46:07 PDT 2024


https://github.com/DavidSpickett updated https://github.com/llvm/llvm-project/pull/97553

>From 08565607da9f35649c8ef037cdd1f9ac2f5c7e25 Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Wed, 3 Jul 2024 10:28:49 +0000
Subject: [PATCH 1/3] [lldb] Print empty enums as if they were unrecognised
 normal enums

Fixes #97514

Given this example:
```
enum E {};

int main()
{
    E x = E(0);
    E y = E(1);
    E z = E(2);
    return 0;
}
```
lldb used to print nothing for `x`, but `0x1` for `y` and `0x2` for
`z`.

At first this seemed like the 0 case needed fixing but the real issue
here is that en enum with no enumerators was being detected as a
"bitfield like enum".

Which is an enum where all enumerators are a single bit value,
or the sum of previous single bit values.

For these we do not print anything for a value of 0, as we assume
it must be the remainder after we've printed the other bits that
were set (I think this is also unfortunate, but I'm not addressing that
here).

Clearly an enum with no enumerators cannot be being used as a bitfield,
so check that up front and print it as if it's a normal enum where we didn't
match any of the enumerators. This means you now get:
```
(lldb) p x
(E) 0
(lldb) p y
(E) 1
(lldb) p z
(E) 2
```

Which is a change to decimal from hex, but I think it's overall more consistent.
Printing hex here was never a concious decision.
---
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 31 +++++++++++--------
 .../DumpValueObjectOptionsTests.cpp           | 28 +++++++++++++----
 2 files changed, 40 insertions(+), 19 deletions(-)

diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 093d27a92d718..960c9f6acde57 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -8649,19 +8649,24 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
   // At the same time, we're applying a heuristic to determine whether we want
   // to print this enum as a bitfield. We're likely dealing with a bitfield if
   // every enumerator is either a one bit value or a superset of the previous
-  // enumerators. Also 0 doesn't make sense when the enumerators are used as
-  // flags.
-  for (auto *enumerator : enum_decl->enumerators()) {
-    uint64_t val = enumerator->getInitVal().getSExtValue();
-    val = llvm::SignExtend64(val, 8*byte_size);
-    if (llvm::popcount(val) != 1 && (val & ~covered_bits) != 0)
-      can_be_bitfield = false;
-    covered_bits |= val;
-    ++num_enumerators;
-    if (val == enum_svalue) {
-      // Found an exact match, that's all we need to do.
-      s.PutCString(enumerator->getNameAsString());
-      return true;
+  // enumerators. Also an enumerator of 0 doesn't make sense when the
+  // enumerators are used as flags.
+  clang::EnumDecl::enumerator_range enumerators = enum_decl->enumerators();
+  if (enumerators.empty())
+    can_be_bitfield = false;
+  else {
+    for (auto *enumerator : enum_decl->enumerators()) {
+      uint64_t val = enumerator->getInitVal().getSExtValue();
+      val = llvm::SignExtend64(val, 8 * byte_size);
+      if (llvm::popcount(val) != 1 && (val & ~covered_bits) != 0)
+        can_be_bitfield = false;
+      covered_bits |= val;
+      ++num_enumerators;
+      if (val == enum_svalue) {
+        // Found an exact match, that's all we need to do.
+        s.PutCString(enumerator->getNameAsString());
+        return true;
+      }
     }
   }
 
diff --git a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
index 6cb982d7f5980..767f19872f858 100644
--- a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
+++ b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
@@ -71,12 +71,13 @@ class ValueObjectMockProcessTest : public ::testing::Test {
   }
 
   CompilerType
-  MakeEnumType(const std::vector<std::pair<const char *, int>> enumerators) {
-    CompilerType uint_type = m_type_system->GetBuiltinTypeForEncodingAndBitSize(
-        lldb::eEncodingUint, 32);
+  MakeEnumType(const std::vector<std::pair<const char *, int>> enumerators,
+               bool is_signed) {
+    CompilerType int_type = m_type_system->GetBuiltinTypeForEncodingAndBitSize(
+        is_signed ? lldb::eEncodingSint : lldb::eEncodingUint, 32);
     CompilerType enum_type = m_type_system->CreateEnumerationType(
         "TestEnum", m_type_system->GetTranslationUnitDecl(),
-        OptionalClangModuleID(), Declaration(), uint_type, false);
+        OptionalClangModuleID(), Declaration(), int_type, false);
 
     m_type_system->StartTagDeclarationDefinition(enum_type);
     Declaration decl;
@@ -123,12 +124,27 @@ class ValueObjectMockProcessTest : public ::testing::Test {
   lldb::ProcessSP m_process_sp;
 };
 
+TEST_F(ValueObjectMockProcessTest, EmptyEnum) {
+  // All values of an empty enum should be shown as plain numbers.
+  TestDumpValueObject(MakeEnumType({}, false),
+                      {{0, {}, "(TestEnum) test_var = 0\n"},
+                       {1, {}, "(TestEnum) test_var = 1\n"},
+                       {2, {}, "(TestEnum) test_var = 2\n"}});
+
+  TestDumpValueObject(MakeEnumType({}, true),
+                      {{-2, {}, "(TestEnum) test_var = -2\n"},
+                       {-1, {}, "(TestEnum) test_var = -1\n"},
+                       {0, {}, "(TestEnum) test_var = 0\n"},
+                       {1, {}, "(TestEnum) test_var = 1\n"},
+                       {2, {}, "(TestEnum) test_var = 2\n"}});
+}
+
 TEST_F(ValueObjectMockProcessTest, Enum) {
   // This is not a bitfield-like enum, so values are printed as decimal by
   // default. Also we only show the enumerator name if the value is an
   // exact match.
   TestDumpValueObject(
-      MakeEnumType({{"test_2", 2}, {"test_3", 3}}),
+      MakeEnumType({{"test_2", 2}, {"test_3", 3}}, false),
       {{0, {}, "(TestEnum) test_var = 0\n"},
        {1, {}, "(TestEnum) test_var = 1\n"},
        {2, {}, "(TestEnum) test_var = test_2\n"},
@@ -152,7 +168,7 @@ TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) {
   // as hex, a value of 0 shows nothing, and values with no exact enumerator are
   // shown as combinations of the other values.
   TestDumpValueObject(
-      MakeEnumType({{"test_2", 2}, {"test_4", 4}}),
+      MakeEnumType({{"test_2", 2}, {"test_4", 4}}, false),
       {
           {0, {}, "(TestEnum) test_var =\n"},
           {1, {}, "(TestEnum) test_var = 0x1\n"},

>From dd646e5f899cd378ff45ecebfcffd465412ec272 Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Wed, 3 Jul 2024 10:38:09 +0000
Subject: [PATCH 2/3] Reuse enumerator var

---
 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 960c9f6acde57..fefe4ef146947 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -8655,7 +8655,7 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
   if (enumerators.empty())
     can_be_bitfield = false;
   else {
-    for (auto *enumerator : enum_decl->enumerators()) {
+    for (auto *enumerator : enumerators) {
       uint64_t val = enumerator->getInitVal().getSExtValue();
       val = llvm::SignExtend64(val, 8 * byte_size);
       if (llvm::popcount(val) != 1 && (val & ~covered_bits) != 0)

>From 110562ab488aa57360c8aebc9d63333c23bc9ec1 Mon Sep 17 00:00:00 2001
From: David Spickett <david.spickett at linaro.org>
Date: Wed, 3 Jul 2024 10:42:57 +0000
Subject: [PATCH 3/3] Update test

---
 .../SymbolFile/DWARF/x86/debug-types-missing-signature.test   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-missing-signature.test b/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-missing-signature.test
index 548dd6cdbc275..b2c792ed6003e 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-missing-signature.test
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/debug-types-missing-signature.test
@@ -22,5 +22,5 @@ PRINTEC: use of undeclared identifier 'EC'
 
 RUN: %lldb %t -b -o "target variable a e ec" | FileCheck --check-prefix=VARS %s
 VARS: (const (unnamed struct)) a = <incomplete type "const (unnamed struct)">
-VARS: (const (unnamed enum)) e = 0x1
-VARS: (const (unnamed enum)) ec = 0x1
+VARS: (const (unnamed enum)) e = 1
+VARS: (const (unnamed enum)) ec = 1



More information about the lldb-commits mailing list