googletest: Add universal printer for `std::span`

Fixes #4318

PiperOrigin-RevId: 560089120
Change-Id: I9d0d098140033520266747a1689e953ee8307c47
diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h
index d1766e6..9ccbff7 100644
--- a/googletest/include/gtest/gtest-printers.h
+++ b/googletest/include/gtest/gtest-printers.h
@@ -122,6 +122,10 @@
 #include "gtest/internal/gtest-internal.h"
 #include "gtest/internal/gtest-port.h"
 
+#if GTEST_INTERNAL_HAS_STD_SPAN
+#include <span>  // NOLINT
+#endif  // GTEST_INTERNAL_HAS_STD_SPAN
+
 namespace testing {
 
 // Definitions in the internal* namespaces are subject to change without notice.
@@ -131,13 +135,32 @@
 template <typename T>
 void UniversalPrint(const T& value, ::std::ostream* os);
 
+template <typename T>
+struct IsStdSpan {
+  static constexpr bool value = false;
+};
+
+#if GTEST_INTERNAL_HAS_STD_SPAN
+template <typename E>
+struct IsStdSpan<std::span<E>> {
+  static constexpr bool value = true;
+};
+#endif  // GTEST_INTERNAL_HAS_STD_SPAN
+
 // Used to print an STL-style container when the user doesn't define
 // a PrintTo() for it.
+//
+// NOTE: Since std::span does not have const_iterator until C++23, it would
+// fail IsContainerTest before C++23. However, IsContainerTest only uses
+// the presence of const_iterator to avoid treating iterators as containers
+// because of iterator::iterator. Which means std::span satisfies the *intended*
+// condition of IsContainerTest.
 struct ContainerPrinter {
   template <typename T,
             typename = typename std::enable_if<
-                (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
-                !IsRecursiveContainer<T>::value>::type>
+                ((sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
+                 !IsRecursiveContainer<T>::value) ||
+                IsStdSpan<T>::value>::type>
   static void PrintValue(const T& container, std::ostream* os) {
     const size_t kMaxCount = 32;  // The maximum number of elements to print.
     *os << '{';
diff --git a/googletest/include/gtest/internal/gtest-port.h b/googletest/include/gtest/internal/gtest-port.h
index b5570b8..35544a0 100644
--- a/googletest/include/gtest/internal/gtest-port.h
+++ b/googletest/include/gtest/internal/gtest-port.h
@@ -208,6 +208,8 @@
 //   or
 //                                 UniversalPrinter<absl::optional>
 //                                 specializations. Always defined to 0 or 1.
+//   GTEST_INTERNAL_HAS_STD_SPAN - for enabling UniversalPrinter<std::span>
+//                                 specializations. Always defined to 0 or 1
 //   GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or
 //                                    Matcher<absl::string_view>
 //                                    specializations. Always defined to 0 or 1.
@@ -2407,6 +2409,16 @@
 #define GTEST_INTERNAL_HAS_OPTIONAL 0
 #endif
 
+#ifdef __has_include
+#if __has_include(<span>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L
+#define GTEST_INTERNAL_HAS_STD_SPAN 1
+#endif  // __has_include(<span>) && GTEST_INTERNAL_CPLUSPLUS_LANG >= 202002L
+#endif  // __has_include
+
+#ifndef GTEST_INTERNAL_HAS_STD_SPAN
+#define GTEST_INTERNAL_HAS_STD_SPAN 0
+#endif
+
 #ifdef GTEST_HAS_ABSL
 // Always use absl::string_view for Matcher<> specializations if googletest
 // is built with absl support.
diff --git a/googletest/test/googletest-printers-test.cc b/googletest/test/googletest-printers-test.cc
index bee0ca4..d5061be 100644
--- a/googletest/test/googletest-printers-test.cc
+++ b/googletest/test/googletest-printers-test.cc
@@ -54,11 +54,16 @@
 
 #include "gtest/gtest-printers.h"
 #include "gtest/gtest.h"
+#include "gtest/internal/gtest-port.h"
 
 #ifdef GTEST_HAS_ABSL
 #include "absl/strings/str_format.h"
 #endif
 
+#if GTEST_INTERNAL_HAS_STD_SPAN
+#include <span>  // NOLINT
+#endif  // GTEST_INTERNAL_HAS_STD_SPAN
+
 // Some user-defined types for testing the universal value printer.
 
 // An anonymous enum type.
@@ -1179,6 +1184,17 @@
   EXPECT_EQ("{ 1, 2 }", Print(v));
 }
 
+TEST(PrintStlContainerTest, StdSpan) {
+#if GTEST_INTERNAL_HAS_STD_SPAN
+  int a[] = {3, 6, 5};
+  std::span<int> s = a;
+
+  EXPECT_EQ("{ 3, 6, 5 }", Print(s));
+#else
+  GTEST_SKIP() << "Does not have std::span.";
+#endif  // GTEST_INTERNAL_HAS_STD_SPAN
+}
+
 TEST(PrintStlContainerTest, LongSequence) {
   const int a[100] = {1, 2, 3};
   const vector<int> v(a, a + 100);