From: Pavel Labath Date: Fri, 18 Jan 2019 12:52:03 +0000 (+0000) Subject: [ADT] Add streaming operators for llvm::Optional X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ab7227994db737baac132bdd810eae0cc4f195a0;p=llvm [ADT] Add streaming operators for llvm::Optional 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. 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 --- diff --git a/include/llvm/ADT/Optional.h b/include/llvm/ADT/Optional.h index 76937d632ae..56eafb156e8 100644 --- a/include/llvm/ADT/Optional.h +++ b/include/llvm/ADT/Optional.h @@ -27,6 +27,8 @@ namespace llvm { +class raw_ostream; + namespace optional_detail { /// Storage for any type. template ::value> struct OptionalStorage { @@ -323,6 +325,18 @@ template bool operator>=(const T &X, const Optional &Y) { return !(X < Y); } +raw_ostream &operator<<(raw_ostream &OS, NoneType); + +template () + << std::declval())> +raw_ostream &operator<<(raw_ostream &OS, const Optional &O) { + if (O) + OS << *O; + else + OS << None; + return OS; +} + } // end namespace llvm #endif // LLVM_ADT_OPTIONAL_H diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 2a6810672b1..7e791594587 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -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 index 00000000000..765b25df13d --- /dev/null +++ b/lib/Support/Optional.cpp @@ -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"; +} diff --git a/unittests/ADT/OptionalTest.cpp b/unittests/ADT/OptionalTest.cpp index 20bc9da4d59..e6997ef8be7 100644 --- a/unittests/ADT/OptionalTest.cpp +++ b/unittests/ADT/OptionalTest.cpp @@ -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(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 get() { + return ComparableAndStreamable(); + } +}; + +TEST_F(OptionalTest, StreamOperator) { + auto to_string = [](Optional 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 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 diff --git a/utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h b/utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h index fd993db1602..f4430f8de47 100644 --- a/utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h +++ b/utils/unittest/googletest/include/gtest/internal/custom/raw-ostream.h @@ -42,8 +42,9 @@ auto printable(const T &V) -> decltype(StreamSwitch::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 namespace llvm_gtest { @@ -68,6 +69,18 @@ struct StreamSwitch() << ConvertibleTo()))> { static const RawStreamProxy 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 +struct StreamSwitch, + decltype((void)(std::declval() + << std::declval>()))> { + static const RawStreamProxy> + printable(const llvm::Optional &V) { + return {V}; + } +}; } // namespace llvm_gtest #endif // !GTEST_NO_LLVM_RAW_OSTREAM