Add `ostream<<` and `AbslStringify` to `absl::StatusOr`.

These methods will only be defined if they're defined for `T`.  Additionally,
we add jitter to the output to discourage people relying on the output format.

PiperOrigin-RevId: 590598988
Change-Id: I4e7173b5f0c66fd3a1cdd3392944e20b8a26641f
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index a5fad4d..981b37f 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -105,6 +105,8 @@
         "//absl/base:raw_logging_internal",
         "//absl/meta:type_traits",
         "//absl/strings",
+        "//absl/strings:has_ostream_operator",
+        "//absl/strings:str_format",
         "//absl/types:variant",
         "//absl/utility",
     ],
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index d9d1683..00415ab 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -77,9 +77,11 @@
     absl::base
     absl::config
     absl::core_headers
+    absl::has_ostream_operator
     absl::nullability
     absl::raw_logging_internal
     absl::status
+    absl::str_format
     absl::strings
     absl::type_traits
     absl::utility
@@ -97,5 +99,6 @@
   DEPS
     absl::status
     absl::statusor
+    absl::strings
     GTest::gmock_main
 )
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 25c1147..5be9490 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -14,6 +14,7 @@
 #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
 #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
 
+#include <cstdint>
 #include <type_traits>
 #include <utility>
 
@@ -21,6 +22,7 @@
 #include "absl/base/nullability.h"
 #include "absl/meta/type_traits.h"
 #include "absl/status/status.h"
+#include "absl/strings/string_view.h"
 #include "absl/utility/utility.h"
 
 namespace absl {
@@ -379,6 +381,53 @@
 
 ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
 
+// Used to introduce jitter into the output of printing functions for
+// `StatusOr` (i.e. `AbslStringify` and `operator<<`).
+class StringifyRandom {
+  enum BracesType {
+    kBareParens = 0,
+    kSpaceParens,
+    kBareBrackets,
+    kSpaceBrackets,
+  };
+
+  // Returns a random `BracesType` determined once per binary load.
+  static BracesType RandomBraces() {
+    static const BracesType kRandomBraces = static_cast<BracesType>(
+        (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
+    return kRandomBraces;
+  }
+
+ public:
+  static inline absl::string_view OpenBrackets() {
+    switch (RandomBraces()) {
+      case kBareParens:
+        return "(";
+      case kSpaceParens:
+        return "( ";
+      case kBareBrackets:
+        return "[";
+      case kSpaceBrackets:
+        return "[ ";
+    }
+    return "(";
+  }
+
+  static inline absl::string_view CloseBrackets() {
+    switch (RandomBraces()) {
+      case kBareParens:
+        return ")";
+      case kSpaceParens:
+        return " )";
+      case kBareBrackets:
+        return "]";
+      case kSpaceBrackets:
+        return " ]";
+    }
+    return ")";
+  }
+};
+
 }  // namespace internal_statusor
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index 33a2cf3..cd35e5b 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -39,6 +39,7 @@
 #include <exception>
 #include <initializer_list>
 #include <new>
+#include <ostream>
 #include <string>
 #include <type_traits>
 #include <utility>
@@ -49,6 +50,9 @@
 #include "absl/meta/type_traits.h"
 #include "absl/status/internal/statusor_internal.h"
 #include "absl/status/status.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/has_ostream_operator.h"
+#include "absl/strings/str_format.h"
 #include "absl/types/variant.h"
 #include "absl/utility/utility.h"
 
@@ -651,6 +655,41 @@
   return !(lhs == rhs);
 }
 
+// Prints the `value` or the status in brackets to `os`.
+//
+// Requires `T` supports `operator<<`.  Do not rely on the output format which
+// may change without notice.
+template <typename T, typename std::enable_if<
+                          absl::HasOstreamOperator<T>::value, int>::type = 0>
+std::ostream& operator<<(std::ostream& os, const StatusOr<T>& status_or) {
+  if (status_or.ok()) {
+    os << status_or.value();
+  } else {
+    os << internal_statusor::StringifyRandom::OpenBrackets()
+       << status_or.status()
+       << internal_statusor::StringifyRandom::CloseBrackets();
+  }
+  return os;
+}
+
+// As above, but supports `StrCat`, `StrFormat`, etc.
+//
+// Requires `T` has `AbslStringify`.  Do not rely on the output format which
+// may change without notice.
+template <
+    typename Sink, typename T,
+    typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type = 0>
+void AbslStringify(Sink& sink, const StatusOr<T>& status_or) {
+  if (status_or.ok()) {
+    absl::Format(&sink, "%v", status_or.value());
+  } else {
+    absl::Format(&sink, "%s%v%s",
+                 internal_statusor::StringifyRandom::OpenBrackets(),
+                 status_or.status(),
+                 internal_statusor::StringifyRandom::CloseBrackets());
+  }
+}
+
 //------------------------------------------------------------------------------
 // Implementation details for StatusOr<T>
 //------------------------------------------------------------------------------
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index b4967e4..09ffc65 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -19,6 +19,8 @@
 #include <initializer_list>
 #include <map>
 #include <memory>
+#include <ostream>
+#include <sstream>
 #include <string>
 #include <type_traits>
 #include <utility>
@@ -29,6 +31,7 @@
 #include "absl/base/casts.h"
 #include "absl/memory/memory.h"
 #include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/any.h"
 #include "absl/types/variant.h"
@@ -37,13 +40,16 @@
 namespace {
 
 using ::testing::AllOf;
+using ::testing::AnyOf;
 using ::testing::AnyWith;
 using ::testing::ElementsAre;
+using ::testing::EndsWith;
 using ::testing::Field;
 using ::testing::HasSubstr;
 using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Pointee;
+using ::testing::StartsWith;
 using ::testing::VariantWith;
 
 #ifdef GTEST_HAS_STATUS_MATCHERS
@@ -1881,4 +1887,35 @@
   EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
 }
 
+struct PrintTestStruct {
+  friend std::ostream& operator<<(std::ostream& os, const PrintTestStruct&) {
+    return os << "ostream";
+  }
+
+  template <typename Sink>
+  friend void AbslStringify(Sink& sink, const PrintTestStruct&) {
+    sink.Append("stringify");
+  }
+};
+
+TEST(StatusOr, OkPrinting) {
+  absl::StatusOr<PrintTestStruct> print_me = PrintTestStruct{};
+  std::stringstream stream;
+  stream << print_me;
+  EXPECT_EQ(stream.str(), "ostream");
+  EXPECT_EQ(absl::StrCat(print_me), "stringify");
+}
+
+TEST(StatusOr, ErrorPrinting) {
+  absl::StatusOr<PrintTestStruct> print_me = absl::UnknownError("error");
+  std::stringstream stream;
+  stream << print_me;
+  const auto error_matcher =
+      AllOf(HasSubstr("UNKNOWN"), HasSubstr("error"),
+            AnyOf(AllOf(StartsWith("("), EndsWith(")")),
+                  AllOf(StartsWith("["), EndsWith("]"))));
+  EXPECT_THAT(stream.str(), error_matcher);
+  EXPECT_THAT(absl::StrCat(print_me), error_matcher);
+}
+
 }  // namespace