https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/175570
>From 9b5f77b5b05e1c57cc279e1704a131392a9f855d Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Mon, 12 Jan 2026 16:36:21 +0100 Subject: [PATCH 1/4] [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() >From fc3e5c9f7493ee13dfe29f595b4e2ff554feaa72 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Mon, 12 Jan 2026 16:55:06 +0100 Subject: [PATCH 2/4] fix: formatting --- .../generic/bitset/TestDataFormatterGenericBitset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e94024c56eb80..80431ce958b9d 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 @@ -66,12 +66,12 @@ def do_test_value(self): @add_test_categories(["libstdcxx"]) def test_value_libstdcpp(self): - self.build(dictionary={'USE_LIBSTDCPP': "1"}) + self.build(dictionary={"USE_LIBSTDCPP": "1"}) self.do_test_value() @add_test_categories(["libc++"]) def test_value_libcpp(self): - self.build(dictionary={'USE_LIBCPP': "1"}) + self.build(dictionary={"USE_LIBCPP": "1"}) self.do_test_value() @add_test_categories(["msvcstl"]) @@ -100,12 +100,12 @@ def do_test_ptr_and_ref(self): @add_test_categories(["libstdcxx"]) def test_ptr_and_ref_libstdcpp(self): - self.build(dictionary={'USE_LIBSTDCPP': "1"}) + self.build(dictionary={"USE_LIBSTDCPP": "1"}) self.do_test_ptr_and_ref() @add_test_categories(["libc++"]) def test_ptr_and_ref_libcpp(self): - self.build(dictionary={'USE_LIBCPP': "1"}) + self.build(dictionary={"USE_LIBCPP": "1"}) self.do_test_ptr_and_ref() @add_test_categories(["msvcstl"]) >From 56bd68ea904360dbf165335a8c56f322220c5d80 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Mon, 12 Jan 2026 22:48:30 +0100 Subject: [PATCH 3/4] fix: assert and trim --- lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index 5f905006dfbcb..d61167a891b49 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -107,8 +107,11 @@ lldb::ChildCacheState GenericBitsetFrontEnd::Update() { ConstString type_name = m_backend.GetCompilerType().GetTypeName(/*BaseOnly=*/true); llvm::StringRef size_str = type_name.GetStringRef(); + assert(size_str.starts_with("bitset<") && size_str.ends_with(">")); + size_str.consume_front("bitset<"); size_str.consume_back(">"); + size_str = size_str.trim(); if (size_str.getAsInteger(10, size)) return lldb::ChildCacheState::eRefetch; } >From ecf882d5bcd9e72b5c4aa28c26635f22a4fd2f2d Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Tue, 13 Jan 2026 19:17:20 +0100 Subject: [PATCH 4/4] fix: remove trim --- lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp index d61167a891b49..1ca0280fc4421 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp @@ -111,7 +111,6 @@ lldb::ChildCacheState GenericBitsetFrontEnd::Update() { size_str.consume_front("bitset<"); size_str.consume_back(">"); - size_str = size_str.trim(); if (size_str.getAsInteger(10, size)) return lldb::ChildCacheState::eRefetch; } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
