[Lldb-commits] [lldb] [LLDB] Add formatters for MSVC STL map-like types (PR #148385)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Jul 12 10:39:57 PDT 2025
https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/148385
>From 31e031a33bb80d8e752bcbdf235db6ac35a382bf Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at 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 | 1 +
10 files changed, 539 insertions(+), 44 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..be0125cba84bf 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,6 +27,7 @@ int main() {
ii.insert(3);
ii.insert(4);
ii.insert(5);
+
thefoo_rw(1); // Set break point at this line.
ii.insert(6);
More information about the lldb-commits
mailing list