Author: Nerixyz Date: 2026-08-21T10:59:44Z New Revision: 497027272dc689303f0ef15736368ed609762308
URL: https://github.com/llvm/llvm-project/commit/497027272dc689303f0ef15736368ed609762308 DIFF: https://github.com/llvm/llvm-project/commit/497027272dc689303f0ef15736368ed609762308.diff LOG: [LLDB] Add MSVC STL bitset formatter (#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. Added: Modified: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/GenericBitset.cpp lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/bitset/TestDataFormatterGenericBitset.py Removed: ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index cb6f9a04a0c55..1ee1de2bef2ff 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1441,10 +1441,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, @@ -1521,7 +1521,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, @@ -1725,6 +1725,17 @@ GenericStrongOrderingSummaryProvider(ValueObject &valobj, Stream &stream, return LibStdcppStrongOrderingSummaryProvider(valobj, stream, options); } +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) @@ -1889,6 +1900,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", @@ -1983,6 +1997,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "MSVC STL/libstdc++ std::error_code/error_condition " "synthetic children", "^std::error_(code|condition)$", stl_synth_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 d3282d0562366..034959d50537d 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 "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Target/Target.h" #include <optional> @@ -23,6 +24,7 @@ class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { enum class StdLib { LibCxx, LibStdcpp, + MsvcStl, }; GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); @@ -65,11 +67,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"); } @@ -86,6 +91,19 @@ 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(); + assert(size_str.starts_with("bitset<") && size_str.ends_with(">")); + + 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 = @@ -146,3 +164,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 7858a7d45b51e..7e2657a1a37c1 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -145,6 +145,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 d84c2949c8a75..e71dfe589a026 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 @@ -13,6 +13,7 @@ class GenericBitsetDataFormatterTestCase(TestBase): + TEST_WITH_PDB_DEBUG_INFO = True SHARED_BUILD_TESTCASE = False def setUp(self): @@ -46,7 +47,7 @@ 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 @@ -73,6 +74,11 @@ def test_value_libcpp(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test_value() + @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( @@ -101,3 +107,8 @@ def test_ptr_and_ref_libstdcpp(self): def test_ptr_and_ref_libcpp(self): 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
