Author: nerix
Date: 2025-07-22T14:16:50+01:00
New Revision: 287b9447cc128d2218d148062d545a8633e37a4b

URL: 
https://github.com/llvm/llvm-project/commit/287b9447cc128d2218d148062d545a8633e37a4b
DIFF: 
https://github.com/llvm/llvm-project/commit/287b9447cc128d2218d148062d545a8633e37a4b.diff

LOG: [LLDB] Add formatters for MSVC STL unordered containers (#149519)

Adds formatters for MSVC STL's unordered containers. This one is
relatively simple, because it can reuse the `std::list` synthetic
children. The unordered containers (aka
[`_Hash`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/xhash#L327))
contain a
[`_List`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/xhash#L2012)
which contains all elements (and is used for iterating through the
container).

Towards https://github.com/llvm/llvm-project/issues/24834.

Added: 
    lldb/source/Plugins/Language/CPlusPlus/MsvcStlUnordered.cpp

Modified: 
    lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
    lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
    lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
    
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt 
b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index 1eaa6fe0e56dd..ab9d991fd48f7 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -37,6 +37,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   MsvcStlAtomic.cpp
   MsvcStlSmartPointer.cpp
   MsvcStlTuple.cpp
+  MsvcStlUnordered.cpp
   MsvcStlVariant.cpp
   MsvcStlVector.cpp
   MSVCUndecoratedNameParser.cpp

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 1a101cefe11e4..80dc4609f9b66 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1434,8 +1434,7 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$",
-      eFormatterMatchRegex,
+      "^std::__debug::unordered_(multi)?(map|set)<.+> >$", 
eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider")));
@@ -1490,8 +1489,8 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
 
   AddCXXSummary(cpp_category_sp,
                 lldb_private::formatters::ContainerSizeSummaryProvider,
-                "libstdc++ std unordered container summary provider",
-                "^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$",
+                "libstdc++ debug std unordered container summary provider",
+                "^std::__debug::unordered_(multi)?(map|set)<.+> >$",
                 stl_summary_flags, true);
 
   AddCXXSummary(
@@ -1660,6 +1659,19 @@ static bool GenericVariantSummaryProvider(ValueObject 
&valobj, Stream &stream,
   return LibStdcppVariantSummaryProvider(valobj, stream, options);
 }
 
+static SyntheticChildrenFrontEnd *
+GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                         ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  if (IsMsvcStlUnordered(*valobj_sp))
+    return MsvcStlUnorderedSyntheticFrontEndCreator(children, valobj_sp);
+  return new ScriptedSyntheticChildren::FrontEnd(
+      "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider",
+      *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)
@@ -1727,6 +1739,10 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSynthetic(cpp_category_sp, GenericVariantSyntheticFrontEndCreator,
                   "std::variant synthetic children", "^std::variant<.*>$",
                   stl_synth_flags, true);
+  AddCXXSynthetic(cpp_category_sp, GenericUnorderedSyntheticFrontEndCreator,
+                  "std::unordered container synthetic children",
+                  "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags,
+                  true);
 
   SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
   stl_deref_flags.SetFrontEndWantsDereference();
@@ -1766,6 +1782,10 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(cpp_category_sp, GenericVariantSummaryProvider,
                 "MSVC STL/libstdc++ std::variant summary provider",
                 "^std::variant<.*>$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "MSVC STL/libstdc++ std unordered container summary provider",
+                "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags,
+                true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index fd1b476d8a6b5..e2a015a537868 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -86,6 +86,12 @@ SyntheticChildrenFrontEnd *
 MsvcStlAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                       lldb::ValueObjectSP valobj_sp);
 
+// MSVC STL std::unordered_(multi){map|set}<>
+bool IsMsvcStlUnordered(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                         lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 

diff  --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlUnordered.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlUnordered.cpp
new file mode 100644
index 0000000000000..9540bff97d260
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlUnordered.cpp
@@ -0,0 +1,69 @@
+//===-- MsvcStlUnordered.cpp 
----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MsvcStl.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+class UnorderedFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  UnorderedFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
+    Update();
+  }
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
+    if (!m_list_sp)
+      return llvm::createStringError("Missing _List");
+    return m_list_sp->GetIndexOfChildWithName(name);
+  }
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override {
+    if (!m_list_sp)
+      return llvm::createStringError("Missing _List");
+    return m_list_sp->GetNumChildren();
+  }
+
+  ValueObjectSP GetChildAtIndex(uint32_t idx) override {
+    if (!m_list_sp)
+      return nullptr;
+    return m_list_sp->GetChildAtIndex(idx);
+  }
+
+private:
+  ValueObjectSP m_list_sp;
+};
+
+} // namespace
+
+lldb::ChildCacheState UnorderedFrontEnd::Update() {
+  m_list_sp = nullptr;
+  ValueObjectSP list_sp = m_backend.GetChildMemberWithName("_List");
+  if (!list_sp)
+    return lldb::ChildCacheState::eRefetch;
+  m_list_sp = list_sp->GetSyntheticValue();
+  return lldb::ChildCacheState::eRefetch;
+}
+
+bool formatters::IsMsvcStlUnordered(ValueObject &valobj) {
+  if (auto valobj_sp = valobj.GetNonSyntheticValue())
+    return valobj_sp->GetChildMemberWithName("_List") != nullptr;
+  return false;
+}
+
+SyntheticChildrenFrontEnd 
*formatters::MsvcStlUnorderedSyntheticFrontEndCreator(
+    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+  if (valobj_sp)
+    return new UnorderedFrontEnd(*valobj_sp);
+  return nullptr;
+}

diff  --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
index a4209ae069790..dd740bd43b063 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
@@ -2,17 +2,13 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
 
 class GenericUnorderedDataFormatterTestCase(TestBase):
     def setUp(self):
         TestBase.setUp(self)
         self.namespace = "std"
 
-    def do_test_with_run_command(self, stdlib_type):
-        self.build(dictionary={stdlib_type: "1"})
+    def do_test_with_run_command(self):
         self.runCmd("file " + self.getBuildArtifact("a.out"), 
CURRENT_EXECUTABLE_SET)
 
         lldbutil.run_break_set_by_source_regexp(self, "Set break point at this 
line.")
@@ -127,8 +123,23 @@ def look_for_content_and_continue(self, var_name, 
patterns):
 
     @add_test_categories(["libstdcxx"])
     def test_with_run_command_libstdcpp(self):
-        self.do_test_with_run_command(USE_LIBSTDCPP)
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test_with_run_command()
+
+    @add_test_categories(["libstdcxx"])
+    def test_with_run_command_libstdcxx_debug(self):
+        self.build(
+            dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": 
"-D_GLIBCXX_DEBUG"}
+        )
+        self.do_test_with_run_command()
 
     @add_test_categories(["libc++"])
     def test_with_run_command_libcpp(self):
-        self.do_test_with_run_command(USE_LIBCPP)
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test_with_run_command()
+
+    @add_test_categories(["msvcstl"])
+    def test_with_run_command_msvcstl(self):
+        # No flags, because the "msvcstl" category checks that the MSVC STL is 
used by default.
+        self.build()
+        self.do_test_with_run_command()


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to