]> granicus.if.org Git - llvm/commitdiff
[ADT] Add streaming operators for llvm::Optional
authorPavel Labath <pavel@labath.sk>
Fri, 18 Jan 2019 12:52:03 +0000 (12:52 +0000)
committerPavel Labath <pavel@labath.sk>
Fri, 18 Jan 2019 12:52:03 +0000 (12:52 +0000)
Summary:
The operators simply print the underlying value or "None".

The trickier part of this patch is making sure the streaming operators
work even in unit tests (which was my primary motivation, though I can
also see them being useful elsewhere). Since the stream operator was a
template, implicit conversions did not kick in, and our gtest glue code
was explicitly introducing an implicit conversion to make sure other
implicit conversions do not kick in :P. I resolve that by specializing
llvm_gtest::StreamSwitch for llvm:Optional<T>.

Reviewers: sammccall, dblaikie

Reviewed By: sammccall

Subscribers: mgorny, dexonsmith, kristina, llvm-commits

Differential Revision: https://reviews.llvm.org/D56795

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@351548 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/Optional.h
lib/Support/CMakeLists.txt
lib/Support/Optional.cpp [new file with mode: 0644]
unittests/ADT/OptionalTest.cpp
utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h

index 76937d632ae13a6f5325deb1c4473578a024d242..56eafb156e8209c4763ef081e8be9dea84d67a8d 100644 (file)
@@ -27,6 +27,8 @@
 
 namespace llvm {
 
+class raw_ostream;
+
 namespace optional_detail {
 /// Storage for any type.
 template <typename T, bool = isPodLike<T>::value> struct OptionalStorage {
@@ -323,6 +325,18 @@ template <typename T> bool operator>=(const T &X, const Optional<T> &Y) {
   return !(X < Y);
 }
 
+raw_ostream &operator<<(raw_ostream &OS, NoneType);
+
+template <typename T, typename = decltype(std::declval<raw_ostream &>()
+                                          << std::declval<const T &>())>
+raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
+  if (O)
+    OS << *O;
+  else
+    OS << None;
+  return OS;
+}
+
 } // end namespace llvm
 
 #endif // LLVM_ADT_OPTIONAL_H
index 2a6810672b1da92a5146df21f5b761197ba1f29f..7e791594587e43aea1de92a85dea4b14d558eb99 100644 (file)
@@ -106,6 +106,7 @@ add_llvm_library(LLVMSupport
   MemoryBuffer.cpp
   MD5.cpp
   NativeFormatting.cpp
+  Optional.cpp
   Options.cpp
   Parallel.cpp
   PluginLoader.cpp
diff --git a/lib/Support/Optional.cpp b/lib/Support/Optional.cpp
new file mode 100644 (file)
index 0000000..765b25d
--- /dev/null
@@ -0,0 +1,15 @@
+//===- Optional.cpp - Optional values ---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/raw_ostream.h"
+
+llvm::raw_ostream &llvm::operator<<(raw_ostream &OS, NoneType) {
+  return OS << "None";
+}
index 20bc9da4d594f4d11fa51379949e8298e5db2b34..e6997ef8be7aeebe6418d68866b52ad67f91b9d3 100644 (file)
@@ -8,6 +8,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest-spi.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -518,5 +521,52 @@ TEST_F(OptionalTest, OperatorGreaterEqual) {
   CheckRelation<GreaterEqual>(InequalityLhs, InequalityRhs, !IsLess);
 }
 
-} // end anonymous namespace
+struct ComparableAndStreamable {
+  friend bool operator==(ComparableAndStreamable,
+                         ComparableAndStreamable) LLVM_ATTRIBUTE_USED {
+    return true;
+  }
+
+  friend raw_ostream &operator<<(raw_ostream &OS, ComparableAndStreamable) {
+    return OS << "ComparableAndStreamable";
+  }
 
+  static Optional<ComparableAndStreamable> get() {
+    return ComparableAndStreamable();
+  }
+};
+
+TEST_F(OptionalTest, StreamOperator) {
+  auto to_string = [](Optional<ComparableAndStreamable> O) {
+    SmallString<16> S;
+    raw_svector_ostream OS(S);
+    OS << O;
+    return S;
+  };
+  EXPECT_EQ("ComparableAndStreamable",
+            to_string(ComparableAndStreamable::get()));
+  EXPECT_EQ("None", to_string(None));
+}
+
+struct Comparable {
+  friend bool operator==(Comparable, Comparable) LLVM_ATTRIBUTE_USED {
+    return true;
+  }
+  static Optional<Comparable> get() { return Comparable(); }
+};
+
+TEST_F(OptionalTest, UseInUnitTests) {
+  // Test that we invoke the streaming operators when pretty-printing values in
+  // EXPECT macros.
+  EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, ComparableAndStreamable::get()),
+                          R"(Expected: llvm::None
+      Which is: None
+To be equal to: ComparableAndStreamable::get()
+      Which is: ComparableAndStreamable)");
+
+  // Test that it is still possible to compare objects which do not have a
+  // custom streaming operator.
+  EXPECT_NONFATAL_FAILURE(EXPECT_EQ(llvm::None, Comparable::get()), "object");
+}
+
+} // end anonymous namespace
index fd993db1602888cc42b98508542116f5416098a3..f4430f8de47f4cc869a3942b762b93d4a3b48d8c 100644 (file)
@@ -42,8 +42,9 @@ auto printable(const T &V) -> decltype(StreamSwitch<T>::printable(V)) {
 // If raw_ostream support is enabled, we specialize for types with operator<<
 // that takes a raw_ostream.
 #if !GTEST_NO_LLVM_RAW_OSTREAM
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/Support/raw_os_ostream.h"
+#include "llvm/Support/raw_ostream.h"
 #include <ostream>
 namespace llvm_gtest {
 
@@ -68,6 +69,18 @@ struct StreamSwitch<T, decltype((void)(std::declval<llvm::raw_ostream &>()
                                        << ConvertibleTo<const T &>()))> {
   static const RawStreamProxy<T> printable(const T &V) { return {V}; }
 };
+
+// llvm::Optional has a template operator<<, which means it will not accept any
+// implicit conversions, so we need to special-case it here.
+template <typename T>
+struct StreamSwitch<llvm::Optional<T>,
+                    decltype((void)(std::declval<llvm::raw_ostream &>()
+                                    << std::declval<llvm::Optional<T>>()))> {
+  static const RawStreamProxy<llvm::Optional<T>>
+  printable(const llvm::Optional<T> &V) {
+    return {V};
+  }
+};
 } // namespace llvm_gtest
 #endif  // !GTEST_NO_LLVM_RAW_OSTREAM