Michael137 updated this revision to Diff 436077.
Michael137 added a comment.

- Updated tests to avoid using `self.expect` and instead the more specific 
assertions from `lldbtest` and `SB API`


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D127481/new/

https://reviews.llvm.org/D127481

Files:
  lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
  lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
  lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
  lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
  
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile
  
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py
  
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp

Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/main.cpp
@@ -0,0 +1,56 @@
+#include <array>
+#include <span>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+template <class T, size_t N>
+void by_ref_and_ptr(std::span<T, N> &ref, std::span<T, N> *ptr) {
+  printf("Stop here to check by ref");
+  return;
+}
+
+int main() {
+  std::array numbers = {1, 12, 123, 1234, 12345};
+
+  using dynamic_string_span = std::span<std::string>;
+
+  // Test span of ints
+
+  //   Full view of numbers with static extent
+  std::span numbers_span = numbers;
+
+  printf("break here");
+
+  by_ref_and_ptr(numbers_span, &numbers_span);
+
+  // Test spans of strings
+  std::vector<std::string> strings{"goofy", "is", "smart", "!!!"};
+  strings.reserve(strings.size() + 1);
+
+  //   Partial view of strings with dynamic extent
+  dynamic_string_span strings_span{std::span{strings}.subspan(2)};
+
+  auto strings_span_it = strings_span.begin();
+
+  printf("break here");
+
+  //   Vector size doesn't increase, span should
+  //   print unchanged and the strings_span_it
+  //   remains valid
+  strings.emplace_back("???");
+
+  printf("break here");
+
+  // Now some empty spans
+  std::span<int, 0> static_zero_span;
+  std::span<int> dynamic_zero_span;
+
+  // Multiple spans
+  std::array span_arr{strings_span, strings_span};
+  std::span<std::span<std::string>, 2> nested = span_arr;
+
+  printf("break here");
+
+  return 0; // break here
+}
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/TestDataFormatterLibcxxSpan.py
@@ -0,0 +1,153 @@
+"""
+Test lldb data formatter subsystem for std::span
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class LibcxxSpanDataFormatterTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def findVariable(self, name):
+        var = self.frame().FindVariable(name)
+        self.assertTrue(var.IsValid())
+        return var
+
+    def check_size(self, var_name, size):
+        var = self.findVariable(var_name)
+        self.assertEqual(var.GetNumChildren(), size)
+
+    def check_numbers(self, var_name):
+        """Helper to check that data formatter sees contents of std::span correctly"""
+
+        expectedSize = 5
+        self.check_size(var_name, expectedSize)
+
+        self.expect_expr(
+                var_name, result_type=f'std::span<int, {expectedSize}>',
+                result_summary=f'size={expectedSize}',
+                result_children=[
+                    ValueCheck(name='[0]', value='1'),
+                    ValueCheck(name='[1]', value='12'),
+                    ValueCheck(name='[2]', value='123'),
+                    ValueCheck(name='[3]', value='1234'),
+                    ValueCheck(name='[4]', value='12345')
+        ])
+
+        # check access-by-index
+        self.expect_var_path(f'{var_name}[0]', type='int', value='1')
+        self.expect_var_path(f'{var_name}[1]', type='int', value='12')
+        self.expect_var_path(f'{var_name}[2]', type='int', value='123')
+        self.expect_var_path(f'{var_name}[3]', type='int', value='1234')
+        self.expect_var_path(f'{var_name}[4]', type='int', value='12345')
+
+    @add_test_categories(['libc++'])
+    @skipIf(compiler='clang', compiler_version=['<', '11.0'])
+    def test_with_run_command(self):
+        """Test that std::span variables are formatted correctly when printed."""
+        self.build()
+        (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, 'break here', lldb.SBFileSpec('main.cpp', False))
+
+        lldbutil.continue_to_breakpoint(process, bkpt)
+
+        # std::span of std::array with extents known at compile-time
+        self.check_numbers('numbers_span')
+
+        # check access to synthetic children for static spans
+        self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" -x "std::span<" span')
+        self.expect_expr('numbers_span',
+                         result_type='std::span<int, 5>',
+                         result_summary='item 0 is 1')
+
+        self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" -x "std::span<" span')
+        self.expect_expr('numbers_span',
+                         result_type='std::span<int, 5>',
+                         result_summary='item 0 is 1')
+
+        self.runCmd('type summary delete span')
+
+        # New span with strings
+        lldbutil.continue_to_breakpoint(process, bkpt)
+
+        expectedStringSpanChildren = [ ValueCheck(name='[0]', summary='"smart"'),
+                                       ValueCheck(name='[1]', summary='"!!!"') ]
+
+        self.expect_var_path('strings_span',
+                             summary='size=2',
+                             children=expectedStringSpanChildren)
+
+        # check access to synthetic children for dynamic spans
+        self.runCmd('type summary add --summary-string "item 0 is ${var[0]}" dynamic_string_span')
+        self.expect_var_path('strings_span', summary='item 0 is "smart"')
+
+        self.runCmd('type summary add --summary-string "item 0 is ${svar[0]}" dynamic_string_span')
+        self.expect_var_path('strings_span', summary='item 0 is "smart"')
+
+        self.runCmd('type summary delete dynamic_string_span')
+
+        # test summaries based on synthetic children
+        self.runCmd(
+                'type summary add --summary-string "span has ${svar%#} items" -e dynamic_string_span')
+
+        self.expect_var_path('strings_span', summary='span has 2 items')
+
+        self.expect_var_path('strings_span',
+                             summary='span has 2 items',
+                             children=expectedStringSpanChildren)
+
+        # check access-by-index
+        self.expect_var_path('strings_span[0]', summary='"smart"');
+        self.expect_var_path('strings_span[1]', summary='"!!!"');
+
+        # Newly inserted value not visible to span
+        lldbutil.continue_to_breakpoint(process, bkpt)
+
+        self.expect_expr('strings_span',
+                         result_summary='span has 2 items',
+                         result_children=expectedStringSpanChildren)
+
+        self.runCmd('type summary delete dynamic_string_span')
+
+        lldbutil.continue_to_breakpoint(process, bkpt)
+
+        # Empty spans
+        self.expect_expr('static_zero_span',
+                         result_type='std::span<int, 0>',
+                         result_summary='size=0')
+        self.check_size('static_zero_span', 0)
+
+        self.expect_expr('dynamic_zero_span',
+                         result_summary='size=0')
+        self.check_size('dynamic_zero_span', 0)
+
+        # Nested spans
+        self.expect_expr('nested',
+                         result_summary='size=2',
+                         result_children=[
+                             ValueCheck(name='[0]', summary='size=2',
+                                        children=expectedStringSpanChildren),
+                             ValueCheck(name='[1]', summary='size=2',
+                                        children=expectedStringSpanChildren)
+        ])
+        self.check_size('nested', 2)
+
+    @add_test_categories(['libc++'])
+    @skipIf(compiler='clang', compiler_version=['<', '11.0'])
+    def test_ref_and_ptr(self):
+        """Test that std::span is correctly formatted when passed by ref and ptr"""
+        self.build()
+        (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, 'Stop here to check by ref', lldb.SBFileSpec('main.cpp', False))
+
+        # The reference should display the same was as the value did
+        self.check_numbers('ref')
+
+        # The pointer should just show the right number of elements:
+
+        ptrAddr = self.findVariable('ptr').GetValue()
+        self.expect_expr('ptr', result_type='std::span<int, 5> *',
+                         result_summary=f'{ptrAddr} size=5')
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/span/Makefile
@@ -0,0 +1,7 @@
+CXX_SOURCES := main.cpp
+
+USE_LIBCPP := 1
+
+CXXFLAGS_EXTRAS := -std=c++20
+
+include Makefile.rules
Index: lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/Language/CPlusPlus/LibCxxSpan.cpp
@@ -0,0 +1,154 @@
+//===-- LibCxxSpan.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 LibcxxStdSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+  ~LibcxxStdSpanSyntheticFrontEnd() override;
+
+  size_t CalculateNumChildren() override;
+
+  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
+
+  /// Determines properties of the std::span<> associated with this object
+  //
+  // std::span can either be instantiated with a compile-time known
+  // extent or a std::dynamic_extent (this is the default if only the
+  // type template argument is provided). The layout of std::span
+  // depends on whether the extent is dynamic or not. For static
+  // extents (e.g., std::span<int, 9>):
+  //
+  // (std::__1::span<const int, 9>) s = {
+  //   __data = 0x000000016fdff494
+  // }
+  //
+  // For dynamic extents, e.g., std::span<int>, the layout is:
+  //
+  // (std::__1::span<const int, 18446744073709551615>) s = {
+  //   __data = 0x000000016fdff494
+  //   __size = 6
+  // }
+  //
+  // This function checks for a '__size' member to determine the number
+  // of elements in the span. If no such member exists, we get the size
+  // from the only other place it can be: the template argument.
+  bool Update() override;
+
+  bool MightHaveChildren() override;
+
+  size_t GetIndexOfChildWithName(ConstString name) override;
+
+private:
+  ValueObject *m_start = nullptr; ///< First element of span. Held, not owned.
+  CompilerType m_element_type{};  ///< Type of span elements.
+  size_t m_num_elements = 0;      ///< Number of elements in span.
+  uint32_t m_element_size = 0;    ///< Size in bytes of each span element.
+};
+
+lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::
+    LibcxxStdSpanSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+    : SyntheticChildrenFrontEnd(*valobj_sp) {
+  if (valobj_sp)
+    Update();
+}
+
+lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::
+    ~LibcxxStdSpanSyntheticFrontEnd() {}
+
+size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::
+    CalculateNumChildren() {
+  return m_num_elements;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::GetChildAtIndex(
+    size_t idx) {
+  if (!m_start)
+    return {};
+
+  uint64_t offset = idx * m_element_size;
+  offset = offset + m_start->GetValueAsUnsigned(0);
+  StreamString name;
+  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+  return CreateValueObjectFromAddress(name.GetString(), offset,
+                                      m_backend.GetExecutionContextRef(),
+                                      m_element_type);
+}
+
+bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::Update() {
+  // Get element type.
+  ValueObjectSP data_type_finder_sp(
+      m_backend.GetChildMemberWithName(ConstString("__data"), true));
+  if (!data_type_finder_sp)
+    return false;
+
+  m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType();
+
+  // Get element size.
+  if (llvm::Optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
+    m_element_size = *size;
+
+    // Get data.
+    if (m_element_size > 0) {
+      m_start = data_type_finder_sp.get();
+    }
+
+    // Get number of elements.
+    if (auto size_sp =
+            m_backend.GetChildMemberWithName(ConstString("__size"), true)) {
+      m_num_elements = size_sp->GetValueAsUnsigned(0);
+    } else if (auto arg =
+                   m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) {
+
+      m_num_elements = arg->value.getLimitedValue();
+    }
+  }
+
+  return true;
+}
+
+bool lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::
+    MightHaveChildren() {
+  return true;
+}
+
+size_t lldb_private::formatters::LibcxxStdSpanSyntheticFrontEnd::
+    GetIndexOfChildWithName(ConstString name) {
+  if (!m_start)
+    return UINT32_MAX;
+  return ExtractIndexFromString(name.GetCString());
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                      lldb::ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+  CompilerType type = valobj_sp->GetCompilerType();
+  if (!type.IsValid() || type.GetNumTemplateArguments() != 2)
+    return nullptr;
+  return new LibcxxStdSpanSyntheticFrontEnd(valobj_sp);
+}
+
+} // namespace formatters
+} // namespace lldb_private
Index: lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -74,6 +74,10 @@
 bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream,
                                     const TypeSummaryOptions &options);
 
+/// Formatter for libc++ std::span<>.
+bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream,
+                               const TypeSummaryOptions &options);
+
 class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
 public:
   LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
@@ -193,6 +197,10 @@
 LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
                              lldb::ValueObjectSP valobj_sp);
 
+SyntheticChildrenFrontEnd *
+LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                      lldb::ValueObjectSP);
+
 } // namespace formatters
 } // namespace lldb_private
 
Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -756,6 +756,12 @@
       lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator,
       "libc++ std::atomic synthetic children",
       ConstString("^std::__[[:alnum:]]+::atomic<.+>$"), stl_synth_flags, true);
+  AddCXXSynthetic(
+      cpp_category_sp,
+      lldb_private::formatters::LibcxxStdSpanSyntheticFrontEndCreator,
+      "libc++ std::span synthetic children",
+      ConstString("^std::__[[:alnum:]]+::span<.+>(( )?&)?$"), stl_deref_flags,
+      true);
 
   cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
       RegularExpression("^(std::__[[:alnum:]]+::)deque<.+>(( )?&)?$"),
@@ -869,6 +875,11 @@
                 "libc++ std::variant summary provider",
                 ConstString("^std::__[[:alnum:]]+::variant<.+>(( )?&)?$"),
                 stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp,
+                lldb_private::formatters::LibcxxContainerSummaryProvider,
+                "libc++ std::span summary provider",
+                ConstString("^std::__[[:alnum:]]+::span<.+>(( )?&)?$"),
+                stl_summary_flags, true);
 
   stl_summary_flags.SetSkipPointers(true);
 
Index: lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -11,6 +11,7 @@
   LibCxxList.cpp
   LibCxxMap.cpp
   LibCxxQueue.cpp
+  LibCxxSpan.cpp
   LibCxxTuple.cpp
   LibCxxUnorderedMap.cpp
   LibCxxVariant.cpp
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to