diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 3e76ba30ada59..21108b27896a1 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -12,6 +12,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibCxxList.cpp LibCxxMap.cpp LibCxxQueue.cpp + LibCxxRangesRefView.cpp LibCxxSpan.cpp LibCxxTuple.cpp LibCxxUnorderedMap.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 752e68c73f875..97b62b4601669 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -835,6 +835,12 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "libc++ std::span synthetic children", ConstString("^std::__[[:alnum:]]+::span<.+>(( )?&)?$"), stl_deref_flags, true); + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEndCreator, + "libc++ std::ranges::ref_view synthetic children", + ConstString("^std::__[[:alnum:]]+::ranges::ref_view<.+>(( )?&)?$"), + stl_deref_flags, true); cpp_category_sp->AddTypeSynthetic( "^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$", eFormatterMatchRegex, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index 96b4c10f32cea..9ab22153a92a8 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -256,6 +256,10 @@ SyntheticChildrenFrontEnd * LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp new file mode 100644 index 0000000000000..6aeb557a95ff3 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxRangesRefView.cpp @@ -0,0 +1,87 @@ +//===-- LibCxxRangesRefView.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 "LibCxx.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/APSInt.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { + +class LibcxxStdRangesRefViewSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibcxxStdRangesRefViewSyntheticFrontEnd() override = default; + + size_t CalculateNumChildren() override { + // __range_ will be the sole child of this type + return 1; + } + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { + // Since we only have a single child, return it + assert(idx == 0); + return m_range_sp; + } + + bool Update() override; + + bool MightHaveChildren() override { return true; } + + size_t GetIndexOfChildWithName(ConstString name) override { + // We only have a single child + return 0; + } + +private: + /// Pointer to the dereferenced __range_ member + lldb::ValueObjectSP m_range_sp = nullptr; +}; + +lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd:: + LibcxxStdRangesRefViewSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +bool lldb_private::formatters::LibcxxStdRangesRefViewSyntheticFrontEnd:: + Update() { + ValueObjectSP range_ptr = + GetChildMemberWithName(m_backend, {ConstString("__range_")}); + if (!range_ptr) + return false; + + lldb_private::Status error; + m_range_sp = range_ptr->Dereference(error); + + return error.Success(); +} + +lldb_private::SyntheticChildrenFrontEnd * +LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + CompilerType type = valobj_sp->GetCompilerType(); + if (!type.IsValid()) + return nullptr; + return new LibcxxStdRangesRefViewSyntheticFrontEnd(valobj_sp); +} + +} // namespace formatters +} // namespace lldb_private diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/Makefile new file mode 100644 index 0000000000000..3c4f9b45f41de --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/Makefile @@ -0,0 +1,6 @@ +USE_LIBCPP := 1 + +CXX_SOURCES := main.cpp +CXXFLAGS_EXTRAS := -std=c++20 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/TestDataFormatterLibcxxRangesRefView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/TestDataFormatterLibcxxRangesRefView.py new file mode 100644 index 0000000000000..38b97604a6067 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/TestDataFormatterLibcxxRangesRefView.py @@ -0,0 +1,65 @@ +""" +Test LLDB's std::ranges::ref_view formatter +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class LibcxxRangesRefViewDataFormatterTestCase(TestBase): + + def check_string_vec_children(self): + return [ValueCheck(name='[0]', summary='"First"'), + ValueCheck(name='[1]', summary='"Second"'), + ValueCheck(name='[2]', summary='"Third"'), + ValueCheck(name='[3]', summary='"Fourth"')] + + def check_string_vec_ref_view(self): + return ValueCheck( + name='*__range_', + summary='size=4', + children=self.check_string_vec_children()) + + def check_foo(self): + return ValueCheck( + name='vec', + children=self.check_string_vec_children()) + + @add_test_categories(["libc++"]) + def test_with_run_command(self): + """Test that std::ranges::ref_view is formatted correctly when printed. + """ + self.build() + (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, 'Break here', lldb.SBFileSpec('main.cpp', False)) + + # Check ref_view over a std::string + self.expect_var_path('single', + children=[ValueCheck( + name='*__range_', + summary='"First"')]) + + # Check all_view, which is a ref_view in this case + self.expect_var_path('all', + children=[self.check_string_vec_ref_view()]) + + # Check take_view format. Embeds a ref_view + self.expect_var_path('subset', + children=[ + ValueCheck(children=[self.check_string_vec_ref_view()]), + ValueCheck(name='__count_', value='2') + ]) + + lldbutil.continue_to_breakpoint(self.process(), bkpt) + + # Check ref_view over custom type 'struct Foo' + self.expect_var_path('view', + children=[ValueCheck( + name='*__range_', + children=[ + ValueCheck(name='[0]', type='Foo', children=[self.check_foo()]), + ValueCheck(name='[1]', type='Foo', children=[self.check_foo()]) + ]) + ]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/main.cpp new file mode 100644 index 0000000000000..a26fb9e9321c1 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/ranges/ref_view/main.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +using string_vec = std::vector; + +string_vec svec{"First", "Second", "Third", "Fourth"}; + +struct Foo { + string_vec vec = svec; +}; + +int main() { + { + auto single = std::ranges::ref_view(svec[0]); + auto all = std::views::all(svec); + auto subset = all | std::views::take(2); + std::puts("Break here"); + } + + { + Foo f[2]; + auto view = std::ranges::ref_view(f); + std::puts("Break here"); + } +}