https://github.com/Nerixyz created 
https://github.com/llvm/llvm-project/pull/175570

This PR adds a formatter for MSVC STL's `std::bitset`. Most of the code is 
already in `GenericBitset`. To add support, only `GetDataContainerMemberName` 
has to be modified. However, to get the size of the set, the template parameter 
is read. This doesn't work with PDB. Unfortunately, there's no static member 
that mirrors the template parameter value, so we have to parse the type name to 
get the size.

>From 9b5f77b5b05e1c57cc279e1704a131392a9f855d Mon Sep 17 00:00:00 2001
From: Nerixyz <[email protected]>
Date: Mon, 12 Jan 2026 16:36:21 +0100
Subject: [PATCH] [LLDB] Add MSVC STL bitset formatter

---
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  | 27 +++++++++++---
 .../Language/CPlusPlus/GenericBitset.cpp      | 30 ++++++++++++++++
 .../Plugins/Language/CPlusPlus/MsvcStl.h      |  6 ++++
 .../bitset/TestDataFormatterGenericBitset.py  | 36 ++++++++++++-------
 4 files changed, 81 insertions(+), 18 deletions(-)

diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index bdc67a004b06c..09c5e578b2150 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1419,10 +1419,10 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   stl_summary_flags.SetDontShowChildren(false);
   stl_summary_flags.SetSkipPointers(false);
 
-  AddCXXSummary(
-      cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
-      "libstdc++ std::bitset summary provider",
-      "^std::(__debug::)?bitset<.+>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::ContainerSizeSummaryProvider,
+                "libstdc++ std::bitset summary provider",
+                "^std::__debug::bitset<.+>(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(cpp_category_sp,
                 lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1499,7 +1499,7 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSynthetic(
       cpp_category_sp,
       lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator,
-      "std::bitset synthetic child", "^std::(__debug::)?bitset<.+>(( )?&)?$",
+      "std::bitset synthetic child", "^std::__debug::bitset<.+>(( )?&)?$",
       stl_deref_flags, true);
 
   AddCXXSummary(cpp_category_sp,
@@ -1690,6 +1690,17 @@ GenericSpanSyntheticFrontEndCreator(CXXSyntheticChildren 
*children,
   return LibStdcppSpanSyntheticFrontEndCreator(children, valobj_sp);
 }
 
+static SyntheticChildrenFrontEnd *
+GenericBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                      ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  if (IsMsvcStlBitset(*valobj_sp))
+    return MsvcStlBitsetSyntheticFrontEndCreator(children, valobj_sp);
+  return LibStdcppBitsetSyntheticFrontEndCreator(children, valobj_sp);
+}
+
 /// Load formatters that are formatting types from more than one STL
 static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   if (!cpp_category_sp)
@@ -1850,6 +1861,9 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
                   "std::(multi)?map/set synthetic children",
                   "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags,
                   true);
+  AddCXXSynthetic(cpp_category_sp, GenericBitsetSyntheticFrontEndCreator,
+                  "std::bitset synthetic children", "^std::bitset<.+>(( 
)?&)?$",
+                  stl_deref_flags, true);
 
   AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
                 "std::initializer_list summary provider",
@@ -1900,6 +1914,9 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
                 "MSVC STL/libstd++ std::span summary provider",
                 "^std::span<.+>$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "MSVC STL/libstdc++ std::bitset summary provider",
+                "^std::bitset<.+>(( )?&)?$", stl_summary_flags, true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
index f2521ec750875..5f905006dfbcb 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp
@@ -8,6 +8,7 @@
 
 #include "LibCxx.h"
 #include "LibStdcpp.h"
+#include "MsvcStl.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/Target/Target.h"
@@ -24,6 +25,7 @@ class GenericBitsetFrontEnd : public 
SyntheticChildrenFrontEnd {
   enum class StdLib {
     LibCxx,
     LibStdcpp,
+    MsvcStl,
   };
 
   GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib);
@@ -75,11 +77,14 @@ GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject 
&valobj, StdLib stdlib)
 llvm::StringRef GenericBitsetFrontEnd::GetDataContainerMemberName() {
   static constexpr llvm::StringLiteral s_libcxx_case("__first_");
   static constexpr llvm::StringLiteral s_libstdcpp_case("_M_w");
+  static constexpr llvm::StringLiteral s_msvcstl_case("_Array");
   switch (m_stdlib) {
   case StdLib::LibCxx:
     return s_libcxx_case;
   case StdLib::LibStdcpp:
     return s_libstdcpp_case;
+  case StdLib::MsvcStl:
+    return s_msvcstl_case;
   }
   llvm_unreachable("Unknown StdLib enum");
 }
@@ -96,6 +101,17 @@ lldb::ChildCacheState GenericBitsetFrontEnd::Update() {
 
   if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
     size = arg->value.GetAPSInt().getLimitedValue();
+  else {
+    // PDB doesn't create template types. Instead, the type is a (non-template)
+    // struct with the name "bitset<N>".
+    ConstString type_name =
+        m_backend.GetCompilerType().GetTypeName(/*BaseOnly=*/true);
+    llvm::StringRef size_str = type_name.GetStringRef();
+    size_str.consume_front("bitset<");
+    size_str.consume_back(">");
+    if (size_str.getAsInteger(10, size))
+      return lldb::ChildCacheState::eRefetch;
+  }
 
   m_elements.assign(size, ValueObjectSP());
   m_first =
@@ -156,3 +172,17 @@ SyntheticChildrenFrontEnd 
*formatters::LibcxxBitsetSyntheticFrontEndCreator(
                                      GenericBitsetFrontEnd::StdLib::LibCxx);
   return nullptr;
 }
+
+bool formatters::IsMsvcStlBitset(ValueObject &valobj) {
+  if (ValueObjectSP valobj_sp = valobj.GetNonSyntheticValue())
+    return valobj_sp->GetChildMemberWithName("_Array") != nullptr;
+  return false;
+}
+
+SyntheticChildrenFrontEnd *formatters::MsvcStlBitsetSyntheticFrontEndCreator(
+    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+  if (valobj_sp)
+    return new GenericBitsetFrontEnd(*valobj_sp,
+                                     GenericBitsetFrontEnd::StdLib::MsvcStl);
+  return nullptr;
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index b25005914e36e..f6c6a62929165 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -127,6 +127,12 @@ SyntheticChildrenFrontEnd *
 MsvcStlSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                     lldb::ValueObjectSP valobj_sp);
 
+// MSVC STL std::bitset<>
+bool IsMsvcStlBitset(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                      lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py
index 98000036eebe7..e94024c56eb80 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py
@@ -8,14 +8,14 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
 VALUE = "VALUE"
 REFERENCE = "REFERENCE"
 POINTER = "POINTER"
 
 
 class GenericBitsetDataFormatterTestCase(TestBase):
+    TEST_WITH_PDB_DEBUG_INFO = True
+
     def setUp(self):
         TestBase.setUp(self)
         primes = [1] * 1000
@@ -47,16 +47,14 @@ def check(self, name, size, variant):
             self.assertEqual(
                 child.GetValueAsUnsigned(),
                 self.primes[i],
-                "variable: %s, index: %d" % (name, size),
+                "variable: %s, index: %d" % (name, i),
             )
         self.expect_var_path(
             name, type=self.getBitsetVariant(size, variant), children=children
         )
 
-    def do_test_value(self, stdlib_type):
+    def do_test_value(self):
         """Test that std::bitset is displayed correctly"""
-        self.build(dictionary={stdlib_type: "1"})
-
         lldbutil.run_to_source_breakpoint(
             self, "// break here", lldb.SBFileSpec("main.cpp", False)
         )
@@ -68,16 +66,21 @@ def do_test_value(self, stdlib_type):
 
     @add_test_categories(["libstdcxx"])
     def test_value_libstdcpp(self):
-        self.do_test_value(USE_LIBSTDCPP)
+        self.build(dictionary={'USE_LIBSTDCPP': "1"})
+        self.do_test_value()
 
     @add_test_categories(["libc++"])
     def test_value_libcpp(self):
-        self.do_test_value(USE_LIBCPP)
+        self.build(dictionary={'USE_LIBCPP': "1"})
+        self.do_test_value()
 
-    def do_test_ptr_and_ref(self, stdlib_type):
-        """Test that ref and ptr to std::bitset is displayed correctly"""
-        self.build(dictionary={stdlib_type: "1"})
+    @add_test_categories(["msvcstl"])
+    def test_value_msvcstl(self):
+        self.build()
+        self.do_test_value()
 
+    def do_test_ptr_and_ref(self):
+        """Test that ref and ptr to std::bitset is displayed correctly"""
         (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Check ref and ptr", lldb.SBFileSpec("main.cpp", False)
         )
@@ -97,8 +100,15 @@ def do_test_ptr_and_ref(self, stdlib_type):
 
     @add_test_categories(["libstdcxx"])
     def test_ptr_and_ref_libstdcpp(self):
-        self.do_test_ptr_and_ref(USE_LIBSTDCPP)
+        self.build(dictionary={'USE_LIBSTDCPP': "1"})
+        self.do_test_ptr_and_ref()
 
     @add_test_categories(["libc++"])
     def test_ptr_and_ref_libcpp(self):
-        self.do_test_ptr_and_ref(USE_LIBCPP)
+        self.build(dictionary={'USE_LIBCPP': "1"})
+        self.do_test_ptr_and_ref()
+
+    @add_test_categories(["msvcstl"])
+    def test_ptr_and_ref_msvcstl(self):
+        self.build()
+        self.do_test_ptr_and_ref()

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to