Author: Daedie-git Date: 2026-08-22T11:59:18+02:00 New Revision: b8193ac24fe88f5e41fed451fbfcc7f92589fb47
URL: https://github.com/llvm/llvm-project/commit/b8193ac24fe88f5e41fed451fbfcc7f92589fb47 DIFF: https://github.com/llvm/llvm-project/commit/b8193ac24fe88f5e41fed451fbfcc7f92589fb47.diff LOG: [lldb] Add MSVC STL formatter for std::valarray (#217242) Pretty-print MSVC STL `std::valarray` from `_Myptr` / `_Mysize`. The size summary is emitted only when that layout is present so libstdc++ valarrays (same `std::` name, no inline namespace) are not stolen. References are dereferenced and the element type is taken from `_Myptr`. Tests: generic valarray suite's MSVC STL category (Windows), covering empty arrays, references, exact synthetic children, and the libstdc++ dispatch guard. Part of #24834 Assisted-by: Grok 4.6 Assisted-by: codex-5.6-high --------- Co-authored-by: Bjorn Schobben <[email protected]> Added: lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp Modified: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 1a4717fe5fe9c..2118b614dd228 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -49,6 +49,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN MsvcStlTree.cpp MsvcStlTuple.cpp MsvcStlUnordered.cpp + MsvcStlValarray.cpp MsvcStlVariant.cpp MsvcStlVector.cpp MSVCUndecoratedNameParser.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index ca5aeed182dda..c5b277311eadc 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1753,6 +1753,23 @@ static bool GenericExpectedSummaryProvider(ValueObject &valobj, Stream &stream, return false; } +static SyntheticChildrenFrontEnd * +GenericValarraySyntheticFrontEndCreator(CXXSyntheticChildren *children, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + if (IsMsvcStlValarray(*valobj_sp)) + return MsvcStlValarraySyntheticFrontEndCreator(children, valobj_sp); + return nullptr; +} + +static bool GenericValarraySummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (!IsMsvcStlValarray(valobj)) + return false; + return ContainerSizeSummaryProvider(valobj, stream, options); +} + /// Load formatters that are formatting types from more than one STL static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -2024,6 +2041,13 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, GenericExpectedSummaryProvider, "MSVC STL std::expected summary provider", "^std::expected<.+>(( )?&)?$", stl_summary_flags, true); + + AddCXXSynthetic(cpp_category_sp, GenericValarraySyntheticFrontEndCreator, + "MSVC STL std::valarray synthetic children", + "^std::valarray<.+>(( )?&)?$", stl_deref_flags, true); + AddCXXSummary(cpp_category_sp, GenericValarraySummaryProvider, + "MSVC STL std::valarray summary provider", + "^std::valarray<.+>(( )?&)?$", stl_summary_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index b1f6fce2b1da4..d488a6e7c1d32 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -159,6 +159,12 @@ SyntheticChildrenFrontEnd * MsvcStlExpectedSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp); +// MSVC STL std::valarray<> +bool IsMsvcStlValarray(ValueObject &valobj); +SyntheticChildrenFrontEnd * +MsvcStlValarraySyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp new file mode 100644 index 0000000000000..89c37b803e0f5 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlValarray.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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/StreamString.h" +#include "lldb/ValueObject/ValueObject.h" +#include "llvm/Support/ErrorExtras.h" +#include <cinttypes> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace { +class MsvcStlValarraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlValarraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); + } + + llvm::Expected<uint32_t> CalculateNumChildren() override { + if (!m_start || m_element_size == 0) + return 0; + return m_count; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_start || idx >= m_count) + return {}; + + uint64_t offset = m_start->GetValueAsUnsigned(0) + + static_cast<uint64_t>(idx) * m_element_size; + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateChildValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); + } + + lldb::ChildCacheState Update() override { + m_start = nullptr; + m_count = 0; + m_element_size = 0; + + ValueObjectSP start = m_backend.GetChildMemberWithName("_Myptr"); + ValueObjectSP size_sp = m_backend.GetChildMemberWithName("_Mysize"); + if (!start || !size_sp) + return ChildCacheState::eRefetch; + + // Prefer the pointer's pointee type: template arguments are missing on + // references unless the frontend asked for a dereference, and PDB may + // omit them entirely. + m_element_type = start->GetCompilerType().GetPointeeType(); + if (!m_element_type.IsValid()) { + CompilerType type = m_backend.GetCompilerType().GetNonReferenceType(); + m_element_type = type.GetTypeTemplateArgument(0); + } + if (!m_element_type.IsValid()) + return ChildCacheState::eRefetch; + + if (std::optional<uint64_t> size = + llvm::expectedToOptional(m_element_type.GetByteSize(nullptr))) + m_element_size = *size; + + if (m_element_size == 0) + return ChildCacheState::eRefetch; + + m_start = start.get(); + m_count = size_sp->GetValueAsUnsigned(0); + return ChildCacheState::eRefetch; + } + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { + if (!m_start) + return llvm::createStringErrorV("type has no child named '{0}'", name); + auto optional_idx = ExtractIndexFromString(name.GetCString()); + if (!optional_idx) + return llvm::createStringErrorV("type has no child named '{0}'", name); + return *optional_idx; + } + +private: + /// A non-owning pointer to valarray's _Myptr member. + ValueObject *m_start = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; + uint64_t m_count = 0; +}; +} // namespace + +bool formatters::IsMsvcStlValarray(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Myptr") != nullptr; + return false; +} + +SyntheticChildrenFrontEnd *formatters::MsvcStlValarraySyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + return new MsvcStlValarraySyntheticFrontEnd(valobj_sp); +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py index 6386e431f80c6..fd12d8abed2fd 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/TestDataFormatterStdValarray.py @@ -10,12 +10,9 @@ class StdValarrayDataFormatterTestCase(TestBase): SHARED_BUILD_TESTCASE = False + TEST_WITH_PDB_DEBUG_INFO = True - def do_test(self): - (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( - self, "break here", lldb.SBFileSpec("main.cpp", False) - ) - + def check_valarray(self, process, bkpt): # # std::valarray # @@ -31,6 +28,18 @@ def do_test(self): "}", ], ) + self.expect("frame variable va_empty", substrs=["va_empty = size=0"]) + self.expect( + "frame variable va_ref", + patterns=[r"va_ref = (0x[0-9a-fA-F]+ )?size=4"], + substrs=[ + "[0] = 0", + "[1] = 0", + "[2] = 0", + "[3] = 0", + "}", + ], + ) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( @@ -44,6 +53,17 @@ def do_test(self): "}", ], ) + self.expect( + "frame variable va_ref", + patterns=[r"va_ref = (0x[0-9a-fA-F]+ )?size=4"], + substrs=[ + "[0] = 1", + "[1] = 12", + "[2] = 123", + "[3] = 1234", + "}", + ], + ) # check access-by-index self.expect("frame variable va_int[0]", substrs=["1"]) @@ -79,6 +99,12 @@ def do_test(self): substrs=['array index 4 is not valid for "(valarray'], ) + def do_test(self): + (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp", False) + ) + self.check_valarray(process, bkpt) + # # std::slice_array # @@ -182,3 +208,21 @@ def do_test(self): def test_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test() + + @add_test_categories(["libstdcxx"]) + def test_libstdcxx_not_stolen_by_msvc(self): + """libstdc++ valarray must not get the MSVC size=raw-field-count summary.""" + self.build(dictionary={"USE_LIBSTDCPP": 1}) + lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp", False) + ) + va = self.expect_var_path("va_int") + self.assertNotEqual(va.summary, "size=2") + + @add_test_categories(["msvcstl"]) + def test_msvcstl(self): + self.build() + (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp", False) + ) + self.check_valarray(process, bkpt) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp index d31951c755eac..a6e5e18eae853 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/valarray/main.cpp @@ -4,6 +4,8 @@ int main() { std::valarray<int> va_int(4); + std::valarray<int> va_empty; + std::valarray<int> &va_ref = va_int; std::cout << "break here"; va_int[0] = 1; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
