https://github.com/Nerixyz updated 
https://github.com/llvm/llvm-project/pull/148385

>From 64982c79003c95c41d5e7c2b40c7ac67c29b1c78 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerix...@outlook.de>
Date: Sat, 12 Jul 2025 18:44:51 +0200
Subject: [PATCH] [LLDB] Add formatters for MSVC STL map-like types

---
 .../Plugins/Language/CPlusPlus/CMakeLists.txt |   1 +
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  |  66 ++-
 .../Plugins/Language/CPlusPlus/MsvcStl.h      |  12 +
 .../Language/CPlusPlus/MsvcStlTree.cpp        | 407 ++++++++++++++++++
 .../generic/map/TestDataFormatterStdMap.py    |  11 +-
 .../data-formatter-stl/generic/map/main.cpp   |   2 +
 .../TestDataFormatterGenericMultiMap.py       |  18 +-
 .../TestDataFormatterGenericMultiSet.py       |  32 +-
 .../set/TestDataFormatterGenericSet.py        |  33 +-
 .../data-formatter-stl/generic/set/main.cpp   |   2 +-
 10 files changed, 539 insertions(+), 45 deletions(-)
 create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp

diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt 
b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index 296159ea28407..96773b43e3685 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -34,6 +34,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   LibStdcppTuple.cpp
   LibStdcppUniquePointer.cpp
   MsvcStl.cpp
+  MsvcStlTree.cpp
   MsvcStlSmartPointer.cpp
   MSVCUndecoratedNameParser.cpp
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 2db3e6f0ca315..d7bf6783b8d3e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1409,7 +1409,7 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           stl_synth_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?map<.+> >(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::map<.+> >(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_synth_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
@@ -1419,17 +1419,17 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?set<.+> >(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::set<.+> >(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?multimap<.+> >(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::multimap<.+> >(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug::)?multiset<.+> >(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::multiset<.+> >(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
@@ -1470,15 +1470,15 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       "libstdc++ std::vector summary provider",
       "^std::(__debug::)?vector<.+>(( )?&)?$", stl_summary_flags, true);
 
-  AddCXXSummary(
-      cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
-      "libstdc++ std::map summary provider",
-      "^std::(__debug::)?map<.+> >(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::ContainerSizeSummaryProvider,
+                "libstdc++ std::map summary provider",
+                "^std::__debug::map<.+> >(( )?&)?$", stl_summary_flags, true);
 
-  AddCXXSummary(
-      cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
-      "libstdc++ std::set summary provider",
-      "^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::ContainerSizeSummaryProvider,
+                "libstdc++ std::set summary provider",
+                "^std::__debug::set<.+> >(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(
       cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1488,12 +1488,12 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(
       cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
       "libstdc++ std::multimap summary provider",
-      "^std::(__debug::)?multimap<.+> >(( )?&)?$", stl_summary_flags, true);
+      "^std::__debug::multimap<.+> >(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(
       cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
       "libstdc++ std::multiset summary provider",
-      "^std::(__debug::)?multiset<.+> >(( )?&)?$", stl_summary_flags, true);
+      "^std::__debug::multiset<.+> >(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(cpp_category_sp,
                 lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1599,6 +1599,18 @@ GenericSmartPointerSummaryProvider(ValueObject &valobj, 
Stream &stream,
   return LibStdcppSmartPointerSummaryProvider(valobj, stream, options);
 }
 
+static lldb_private::SyntheticChildrenFrontEnd *
+GenericMapLikeSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                       lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  if (IsMsvcStlMapLike(*valobj_sp))
+    return MsvcStlMapLikeSyntheticFrontEndCreator(valobj_sp);
+  return new ScriptedSyntheticChildren::FrontEnd(
+      "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider", *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)
@@ -1642,12 +1654,19 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           },
           "MSVC STL/libstdc++ std::wstring summary provider"));
 
+  stl_summary_flags.SetDontShowChildren(false);
+  stl_summary_flags.SetSkipPointers(false);
+
   AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
                   "std::shared_ptr synthetic children",
                   "^std::shared_ptr<.+>(( )?&)?$", stl_synth_flags, true);
   AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
                   "std::weak_ptr synthetic children",
                   "^std::weak_ptr<.+>(( )?&)?$", stl_synth_flags, true);
+  AddCXXSynthetic(cpp_category_sp, GenericMapLikeSyntheticFrontEndCreator,
+                  "std::(multi)?map/set synthetic children",
+                  "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags,
+                  true);
 
   AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
                 "MSVC STL/libstdc++ std::shared_ptr summary provider",
@@ -1655,6 +1674,10 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
                 "MSVC STL/libstdc++ std::weak_ptr summary provider",
                 "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "MSVC STL/libstdc++ std::(multi)?map/set summary provider",
+                "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_summary_flags,
+                true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
@@ -1669,6 +1692,9 @@ static void 
LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       .SetDontShowValue(false)
       .SetShowMembersOneLiner(false)
       .SetHideItemNames(false);
+  SyntheticChildren::Flags stl_synth_flags;
+  stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+      false);
 
   using StringElementType = StringPrinter::StringElementType;
 
@@ -1690,6 +1716,18 @@ static void 
LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           stl_summary_flags,
           MsvcStlStringSummaryProvider<StringElementType::UTF32>,
           "MSVC STL std::u32string summary provider"));
+
+  stl_summary_flags.SetDontShowChildren(false);
+  stl_summary_flags.SetSkipPointers(false);
+
+  AddCXXSynthetic(cpp_category_sp, MsvcStlTreeIterSyntheticFrontEndCreator,
+                  "MSVC STL tree iterator synthetic children",
+                  "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", 
stl_synth_flags,
+                  true);
+  AddCXXSummary(cpp_category_sp, MsvcStlTreeIterSummaryProvider,
+                "MSVC STL tree iterator summary",
+                "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", 
stl_summary_flags,
+                true);
 }
 
 static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index edf3f4e8a5387..5506df35832ad 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -37,6 +37,18 @@ bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, 
Stream &stream,
 lldb_private::SyntheticChildrenFrontEnd *
 MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
 
+bool IsMsvcStlTreeIter(ValueObject &valobj);
+bool MsvcStlTreeIterSummaryProvider(ValueObject &valobj, Stream &stream,
+                                    const TypeSummaryOptions &options);
+lldb_private::SyntheticChildrenFrontEnd *
+MsvcStlTreeIterSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                        lldb::ValueObjectSP valobj_sp);
+
+// std::map,set,multimap,multiset
+bool IsMsvcStlMapLike(ValueObject &valobj);
+lldb_private::SyntheticChildrenFrontEnd *
+MsvcStlMapLikeSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
new file mode 100644
index 0000000000000..ddf6c27a3e003
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
@@ -0,0 +1,407 @@
+//===-- MsvcStlTree.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/FormattersHelpers.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include <cstdint>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+// A Node looks as follows:
+// struct _Tree_node {
+//   _Tree_node *_Left;
+//   _Tree_node *_Parent;
+//   _Tree_node *_Right;
+//   char _Color;
+//   char _Isnil;         // true (!= 0) if head or nil node
+//   value_type _Myval;
+// };
+
+namespace {
+
+class MapEntry {
+public:
+  MapEntry() = default;
+  explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+  explicit MapEntry(ValueObject *entry)
+      : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+  ValueObjectSP left() const {
+    if (!m_entry_sp)
+      return m_entry_sp;
+    return m_entry_sp->GetSyntheticChildAtOffset(
+        0, m_entry_sp->GetCompilerType(), true);
+  }
+
+  ValueObjectSP right() const {
+    if (!m_entry_sp)
+      return m_entry_sp;
+    return m_entry_sp->GetSyntheticChildAtOffset(
+        2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+        m_entry_sp->GetCompilerType(), true);
+  }
+
+  ValueObjectSP parent() const {
+    if (!m_entry_sp)
+      return m_entry_sp;
+    return m_entry_sp->GetSyntheticChildAtOffset(
+        m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+        m_entry_sp->GetCompilerType(), true);
+  }
+
+  uint64_t value() const {
+    if (!m_entry_sp)
+      return 0;
+    return m_entry_sp->GetValueAsUnsigned(0);
+  }
+
+  bool is_nil() const {
+    if (!m_entry_sp)
+      return true;
+    auto isnil_sp = m_entry_sp->GetChildMemberWithName("_Isnil");
+    if (!isnil_sp)
+      return true;
+    return isnil_sp->GetValueAsUnsigned(1) != 0;
+  }
+
+  bool error() const {
+    if (!m_entry_sp)
+      return true;
+    return m_entry_sp->GetError().Fail();
+  }
+
+  bool is_nullptr() const { return (value() == 0); }
+
+  ValueObjectSP GetEntry() const { return m_entry_sp; }
+
+  void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+  bool operator==(const MapEntry &rhs) const {
+    return (rhs.m_entry_sp.get() == m_entry_sp.get());
+  }
+
+private:
+  ValueObjectSP m_entry_sp;
+};
+
+class MapIterator {
+public:
+  MapIterator(ValueObject *entry, size_t depth = 0)
+      : m_entry(entry), m_max_depth(depth) {}
+
+  MapIterator() = default;
+
+  ValueObjectSP value() { return m_entry.GetEntry(); }
+
+  ValueObjectSP advance(size_t count) {
+    ValueObjectSP fail;
+    if (m_error)
+      return fail;
+    size_t steps = 0;
+    while (count > 0) {
+      next();
+      count--, steps++;
+      if (m_error || m_entry.is_nullptr() || (steps > m_max_depth))
+        return fail;
+    }
+    return m_entry.GetEntry();
+  }
+
+private:
+  /// Mimicks _Tree_unchecked_const_iterator::operator++()
+  void next() {
+    if (m_entry.is_nullptr())
+      return;
+    MapEntry right(m_entry.right());
+    if (!right.is_nil()) {
+      m_entry = tree_min(std::move(right));
+      return;
+    }
+    size_t steps = 0;
+    MapEntry pnode(m_entry.parent());
+    while (!pnode.is_nil() &&
+           m_entry.value() == MapEntry(pnode.right()).value()) {
+      m_entry = pnode;
+      steps++;
+      if (steps > m_max_depth) {
+        m_entry = MapEntry();
+        return;
+      }
+      pnode.SetEntry(m_entry.parent());
+    }
+    m_entry = std::move(pnode);
+  }
+
+  /// Mimicks MSVC STL's _Min() algorithm (finding the leftmost node in the
+  /// subtree).
+  MapEntry tree_min(MapEntry pnode) {
+    if (pnode.is_nullptr())
+      return MapEntry();
+    MapEntry left(pnode.left());
+    size_t steps = 0;
+    while (!left.is_nil()) {
+      if (left.error()) {
+        m_error = true;
+        return MapEntry();
+      }
+      pnode = left;
+      left.SetEntry(pnode.left());
+      steps++;
+      if (steps > m_max_depth)
+        return MapEntry();
+    }
+    return pnode;
+  }
+
+  MapEntry m_entry;
+  size_t m_max_depth = 0;
+  bool m_error = false;
+};
+
+} // namespace
+
+namespace lldb_private {
+namespace formatters {
+class MsvcStlTreeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+  ~MsvcStlTreeSyntheticFrontEnd() override = default;
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+private:
+  /// Returns the ValueObject for the _Tree_node at index \ref idx.
+  ///
+  /// \param[in] idx The child index that we're looking to get the value for.
+  ///
+  /// \param[in] max_depth The maximum search depth after which we stop trying
+  ///                      to find the node for.
+  ///
+  /// \returns On success, returns the ValueObjectSP corresponding to the
+  ///          _Tree_node's _Myval member.
+  ///          On failure, nullptr is returned.
+  ValueObjectSP GetValueAt(size_t idx, size_t max_depth);
+
+  ValueObject *m_tree = nullptr;
+  ValueObject *m_begin_node = nullptr;
+  size_t m_count = UINT32_MAX;
+  std::map<size_t, MapIterator> m_iterators;
+};
+
+class MsvcStlTreeIterSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlTreeIterSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+      : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override {
+    if (!m_inner_sp)
+      return 0;
+    return m_inner_sp->GetNumChildren();
+  }
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
+    if (!m_inner_sp)
+      return nullptr;
+    return m_inner_sp->GetChildAtIndex(idx);
+  }
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
+    if (!m_inner_sp)
+      return llvm::createStringError("There are no children.");
+    return m_inner_sp->GetIndexOfChildWithName(name);
+  }
+
+  lldb::ValueObjectSP GetSyntheticValue() override { return m_inner_sp; }
+
+private:
+  ValueObjectSP m_inner_sp;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::
+    MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+    : SyntheticChildrenFrontEnd(*valobj_sp) {
+  if (valobj_sp)
+    Update();
+}
+
+llvm::Expected<uint32_t>
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::CalculateNumChildren() 
{
+  if (m_count != UINT32_MAX)
+    return m_count;
+
+  if (m_tree == nullptr)
+    return 0;
+
+  if (auto node_sp = m_tree->GetChildMemberWithName("_Mysize")) {
+    m_count = node_sp->GetValueAsUnsigned(0);
+    return m_count;
+  }
+
+  return llvm::createStringError("Failed to read size.");
+}
+
+ValueObjectSP
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetValueAt(
+    size_t idx, size_t max_depth) {
+  MapIterator iterator(m_begin_node, max_depth);
+
+  size_t advance_by = idx;
+  if (idx > 0) {
+    // If we have already created the iterator for the previous
+    // index, we can start from there and advance by 1.
+    auto cached_iterator = m_iterators.find(idx - 1);
+    if (cached_iterator != m_iterators.end()) {
+      iterator = cached_iterator->second;
+      advance_by = 1;
+    }
+  }
+
+  ValueObjectSP iterated_sp(iterator.advance(advance_by));
+  if (!iterated_sp)
+    // this tree is garbage - stop
+    return nullptr;
+
+  ValueObjectSP value_sp = iterated_sp->GetChildMemberWithName("_Myval");
+  if (!value_sp)
+    return nullptr;
+
+  m_iterators[idx] = iterator;
+
+  return value_sp;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetChildAtIndex(
+    uint32_t idx) {
+  uint32_t num_children = CalculateNumChildrenIgnoringErrors();
+  if (idx >= num_children)
+    return nullptr;
+
+  if (m_tree == nullptr || m_begin_node == nullptr)
+    return nullptr;
+
+  ValueObjectSP val_sp = GetValueAt(idx, /*max_depth=*/num_children);
+  if (!val_sp) {
+    // this will stop all future searches until an Update() happens
+    m_tree = nullptr;
+    return nullptr;
+  }
+
+  // at this point we have a valid pair
+  // we need to copy current_sp into a new object otherwise we will end up with
+  // all items named _Myval
+  StreamString name;
+  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+  return val_sp->Clone(ConstString(name.GetString()));
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::Update() {
+  m_count = UINT32_MAX;
+  m_tree = m_begin_node = nullptr;
+  m_iterators.clear();
+  m_tree =
+      m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myval2"}).get();
+  if (!m_tree)
+    return lldb::ChildCacheState::eRefetch;
+
+  m_begin_node = m_tree->GetChildAtNamePath({"_Myhead", "_Left"}).get();
+
+  return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<size_t>
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetIndexOfChildWithName(
+    ConstString name) {
+  auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
+  if (!optional_idx) {
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  }
+  return *optional_idx;
+}
+
+lldb::ChildCacheState MsvcStlTreeIterSyntheticFrontEnd::Update() {
+  m_inner_sp = nullptr;
+  auto node_sp = m_backend.GetChildMemberWithName("_Ptr");
+  if (!node_sp)
+    return lldb::eRefetch;
+
+  MapEntry entry(node_sp.get());
+  if (entry.is_nil())
+    return lldb::eRefetch; // end
+
+  m_inner_sp = node_sp->GetChildMemberWithName("_Myval");
+  return lldb::eRefetch;
+}
+
+bool formatters::IsMsvcStlTreeIter(ValueObject &valobj) {
+  return valobj.GetChildMemberWithName("_Ptr") != nullptr;
+}
+
+bool formatters::MsvcStlTreeIterSummaryProvider(
+    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+  auto valobj_sp = valobj.GetNonSyntheticValue();
+  if (!valobj_sp)
+    return false;
+  auto node_sp = valobj_sp->GetChildMemberWithName("_Ptr");
+  if (!node_sp)
+    return false;
+
+  MapEntry entry(node_sp.get());
+  if (entry.is_nil()) {
+    stream.Printf("end");
+    return true;
+  }
+
+  auto value_sp = node_sp->GetChildMemberWithName("_Myval");
+  if (!value_sp)
+    return false;
+
+  auto *summary = value_sp->GetSummaryAsCString();
+  if (summary)
+    stream << summary;
+  return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlTreeIterSyntheticFrontEndCreator(
+    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+  return (valobj_sp ? new MsvcStlTreeIterSyntheticFrontEnd(valobj_sp)
+                    : nullptr);
+}
+
+bool formatters::IsMsvcStlMapLike(ValueObject &valobj) {
+  return valobj.GetChildMemberWithName("_Mypair") != nullptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlMapLikeSyntheticFrontEndCreator(
+    lldb::ValueObjectSP valobj_sp) {
+  return (valobj_sp ? new MsvcStlTreeSyntheticFrontEnd(valobj_sp) : nullptr);
+}
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py
index 5851588b59b5f..07d6c963eb05d 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/TestDataFormatterStdMap.py
@@ -21,7 +21,7 @@ def check_pair(self, first_value, second_value):
         ]
         return ValueCheck(children=pair_children)
 
-    def do_test(self):
+    def do_test(self, *, supports_end_iter=False):
         """Test that that file and class static variables display correctly."""
         self.runCmd("file " + self.getBuildArtifact("a.out"), 
CURRENT_EXECUTABLE_SET)
 
@@ -143,6 +143,9 @@ def cleanup():
                 ValueCheck(name="second", value="0"),
             ],
         )
+        if supports_end_iter:
+            self.expect("frame variable it_end", substrs=["= end"])
+            self.expect("frame variable const_it_end", substrs=["= end"])
 
         # check that MightHaveChildren() gets it right
         self.assertTrue(
@@ -343,3 +346,9 @@ def test_libstdcxx_debug(self):
             dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": 
"-D_GLIBCXX_DEBUG"}
         )
         self.do_test()
+
+    @add_test_categories(["msvcstl"])
+    def test_msvcstl(self):
+        # No flags, because the "msvcstl" category checks that the MSVC STL is 
used by default.
+        self.build()
+        self.do_test(supports_end_iter=True)
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/main.cpp
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/main.cpp
index 91bdf0b58d9e4..a626f175c11c4 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/main.cpp
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/map/main.cpp
@@ -26,6 +26,8 @@ int main() {
   intint_map::iterator it = ii.begin();
   intint_map::const_iterator const_it = ii.cbegin();
   std::printf("%d %d\n", it->second, const_it->second);
+  intint_map::iterator it_end = ii.end();
+  intint_map::const_iterator const_it_end = ii.cend();
 
   thefoo_rw(1); // Set break point at this line.
   ii[2] = 0;
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py
index 5c0e1597c2a76..7ac79714db88d 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py
@@ -9,9 +9,6 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
 
 class GenericMultiMapDataFormatterTestCase(TestBase):
     def setUp(self):
@@ -38,9 +35,8 @@ def check(self, var_name, size):
             var_name, type=self.getVariableType(var_name), children=children
         )
 
-    def do_test_with_run_command(self, stdlib_type):
+    def do_test_with_run_command(self):
         """Test that that file and class static variables display correctly."""
-        self.build(dictionary={stdlib_type: "1"})
         self.runCmd("file " + self.getBuildArtifact("a.out"), 
CURRENT_EXECUTABLE_SET)
 
         bkpt = self.target().FindBreakpointByID(
@@ -331,9 +327,17 @@ def cleanup():
     @add_test_categories(["libstdcxx"])
     @skipIf(compiler="clang", compiler_version=["<", "9.0"])
     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()
 
     @skipIf(compiler="clang", compiler_version=["<", "9.0"])
     @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()
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py
index 24ab4def6c59e..7e922fccdf7d7 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multiset/TestDataFormatterGenericMultiSet.py
@@ -8,9 +8,6 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
 
 class GenericMultiSetDataFormatterTestCase(TestBase):
     def setUp(self):
@@ -37,9 +34,8 @@ def check(self, var_name, size):
             var_name, type=self.getVariableType(var_name), children=children
         )
 
-    def do_test_with_run_command(self, stdlib_type):
+    def do_test_with_run_command(self):
         """Test that that file and class static variables display correctly."""
-        self.build(dictionary={stdlib_type: "1"})
         (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set break point at this line.", lldb.SBFileSpec("main.cpp", 
False)
         )
@@ -129,9 +125,8 @@ def cleanup():
             ],
         )
 
-    def do_test_ref_and_ptr(self, stdlib_type):
+    def do_test_ref_and_ptr(self):
         """Test that the data formatters work on ref and ptr."""
-        self.build(dictionary={stdlib_type: "1"})
         (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self,
             "Stop here to check by ref and ptr.",
@@ -145,16 +140,31 @@ def do_test_ref_and_ptr(self, stdlib_type):
 
     @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(["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()
 
     @add_test_categories(["libstdcxx"])
     def test_ref_and_ptr_libstdcpp(self):
-        self.do_test_ref_and_ptr(USE_LIBSTDCPP)
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test_ref_and_ptr()
 
     @add_test_categories(["libc++"])
     def test_ref_and_ptr_libcpp(self):
-        self.do_test_ref_and_ptr(USE_LIBCPP)
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test_ref_and_ptr()
+
+    @add_test_categories(["msvcstl"])
+    def test_ref_and_ptr_msvcstl(self):
+        self.build()
+        self.do_test_ref_and_ptr()
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py
index d3d6ef55c6ff0..1ac5e323e23e3 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/TestDataFormatterGenericSet.py
@@ -8,9 +8,6 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
 
 class GenericSetDataFormatterTestCase(TestBase):
     def setUp(self):
@@ -37,9 +34,8 @@ def check(self, var_name, size):
             var_name, type=self.getVariableType(var_name), children=children
         )
 
-    def do_test_with_run_command(self, stdlib_type):
+    def do_test_with_run_command(self):
         """Test that that file and class static variables display correctly."""
-        self.build(dictionary={stdlib_type: "1"})
         (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "Set break point at this line.", lldb.SBFileSpec("main.cpp", 
False)
         )
@@ -74,6 +70,7 @@ def cleanup():
                 "[5] = 5",
             ],
         )
+
         lldbutil.continue_to_breakpoint(process, bkpt)
         self.check("ii", 7)
 
@@ -129,15 +126,22 @@ def cleanup():
 
     @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(["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()
 
-    def do_test_ref_and_ptr(self, stdlib_type):
+    @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()
+
+    def do_test_ref_and_ptr(self):
         """Test that the data formatters work on ref and ptr."""
-        self.build(dictionary={stdlib_type: "1"})
         (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self,
             "Stop here to check by ref and ptr.",
@@ -152,8 +156,15 @@ def do_test_ref_and_ptr(self, stdlib_type):
 
     @add_test_categories(["libstdcxx"])
     def test_ref_and_ptr_libstdcpp(self):
-        self.do_test_ref_and_ptr(USE_LIBSTDCPP)
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test_ref_and_ptr()
 
     @add_test_categories(["libc++"])
     def test_ref_and_ptr_libcpp(self):
-        self.do_test_ref_and_ptr(USE_LIBCPP)
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test_ref_and_ptr()
+
+    @add_test_categories(["msvcstl"])
+    def test_ref_and_ptr_msvcstl(self):
+        self.build()
+        self.do_test_ref_and_ptr()
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/main.cpp
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/main.cpp
index 314d1e79367d3..b0294214f7287 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/main.cpp
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/set/main.cpp
@@ -27,8 +27,8 @@ int main() {
   ii.insert(3);
   ii.insert(4);
   ii.insert(5);
-  thefoo_rw(1); // Set break point at this line.
 
+  thefoo_rw(1); // Set break point at this line.
   ii.insert(6);
   thefoo_rw(1); // Set break point at this line.
 

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

Reply via email to