This patch adds a formatter for `std::ranges::ref_view<T>`.
It simply holds a `T*`, so all this formatter does is dereference
this pointer and format it as `T` would be.
**Testing**
* Added API tests
Differential Revision: https://reviews.llvm.org/D138558
LibCxxList.cpp
LibCxxMap.cpp
LibCxxQueue.cpp
+ LibCxxRangesRefView.cpp
LibCxxSpan.cpp
LibCxxTuple.cpp
LibCxxUnorderedMap.cpp
"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,
LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);
+SyntheticChildrenFrontEnd *
+LibcxxStdRangesRefViewSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
} // namespace formatters
} // namespace lldb_private
--- /dev/null
+//===-- 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
--- /dev/null
+USE_LIBCPP := 1
+
+CXX_SOURCES := main.cpp
+CXXFLAGS_EXTRAS := -std=c++20
+
+include Makefile.rules
--- /dev/null
+"""
+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()])
+ ])
+ ])
--- /dev/null
+#include <cstdio>
+#include <ranges>
+#include <string>
+#include <vector>
+
+using string_vec = std::vector<std::string>;
+
+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");
+ }
+}