https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/148385
>From 87e2769ae03af331b5affadf238032bcaad361e6 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 | 38 +- .../data-formatter-stl/generic/set/main.cpp | 6 + 10 files changed, 549 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..63d00fb29cd1f 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, *, supports_end_iter=False): """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,12 @@ def cleanup(): "[5] = 5", ], ) + self.expect("frame variable it", endstr="= 1\n") + self.expect("frame variable const_it", endstr="= 1\n") + if supports_end_iter: + self.expect("frame variable it_end", substrs=["= end"]) + self.expect("frame variable const_it_end", substrs=["= end"]) + lldbutil.continue_to_breakpoint(process, bkpt) self.check("ii", 7) @@ -129,15 +131,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(supports_end_iter=True) + + 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 +161,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..c6a5d06d157e2 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,12 @@ int main() { ii.insert(3); ii.insert(4); ii.insert(5); + + auto it = ++ii.begin(); + auto const_it = ++ii.cbegin(); + auto it_end = ii.end(); + auto const_it_end = ii.cend(); + thefoo_rw(1); // Set break point at this line. ii.insert(6); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits