From 8ca99100ba26d534a0cda05028afb41b0ed49647 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 6 Jul 2018 05:45:45 +0000 Subject: [PATCH] [Support] Make support types more easily printable. Summary: Error's new operator<< is the first way to print an error without consuming it. formatv() can now print objects with an operator<< that works with raw_ostream. Reviewers: bkramer Subscribers: mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D48966 llvm-svn: 336412 --- llvm/include/llvm/Support/Error.h | 8 ++++ llvm/include/llvm/Support/FormatVariadic.h | 9 +--- llvm/include/llvm/Support/FormatVariadicDetails.h | 50 +++++++++++++++++++++-- llvm/unittests/Support/ErrorTest.cpp | 39 +++++++++++++----- llvm/unittests/Support/FormatVariadicTest.cpp | 9 ++++ 5 files changed, 95 insertions(+), 20 deletions(-) diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h index e697b32..b026dae 100644 --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -302,6 +302,14 @@ private: return Tmp; } + friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { + if (auto P = E.getPtr()) + P->log(OS); + else + OS << "success"; + return OS; + } + ErrorInfoBase *Payload = nullptr; }; diff --git a/llvm/include/llvm/Support/FormatVariadic.h b/llvm/include/llvm/Support/FormatVariadic.h index eb69761..b0f5825 100644 --- a/llvm/include/llvm/Support/FormatVariadic.h +++ b/llvm/include/llvm/Support/FormatVariadic.h @@ -237,6 +237,8 @@ public: // for type T containing a method whose signature is: // void format(const T &Obj, raw_ostream &Stream, StringRef Options) // Then this method is invoked as described in Step 1. +// 3. If an appropriate operator<< for raw_ostream exists, it will be used. +// For this to work, (raw_ostream& << const T&) must return raw_ostream&. // // If a match cannot be found through either of the above methods, a compiler // error is generated. @@ -258,13 +260,6 @@ inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object(Vals))...)); } -// Allow a formatv_object to be formatted (no options supported). -template struct format_provider> { - static void format(const formatv_object &V, raw_ostream &OS, StringRef) { - OS << V; - } -}; - } // end namespace llvm #endif // LLVM_SUPPORT_FORMATVARIADIC_H diff --git a/llvm/include/llvm/Support/FormatVariadicDetails.h b/llvm/include/llvm/Support/FormatVariadicDetails.h index 9b60462..394decf 100644 --- a/llvm/include/llvm/Support/FormatVariadicDetails.h +++ b/llvm/include/llvm/Support/FormatVariadicDetails.h @@ -38,6 +38,17 @@ public: } }; +template +class stream_operator_format_adapter : public format_adapter { + T Item; + +public: + explicit stream_operator_format_adapter(T &&Item) + : Item(std::forward(Item)) {} + + void format(llvm::raw_ostream &S, StringRef Options) override { S << Item; } +}; + template class missing_format_adapter; // Test if format_provider is defined on T and contains a member function @@ -59,6 +70,23 @@ public: (sizeof(test>(nullptr)) == 1); }; +// Test if raw_ostream& << T -> raw_ostream& is findable via ADL. +template class has_StreamOperator { +public: + using ConstRefT = const typename std::decay::type &; + + template + static char test(typename std::enable_if< + std::is_same() + << std::declval()), + llvm::raw_ostream &>::value, + int *>::type); + + template static double test(...); + + static bool const value = (sizeof(test(nullptr)) == 1); +}; + // Simple template that decides whether a type T should use the member-function // based format() invocation. template @@ -77,15 +105,24 @@ struct uses_format_provider bool, !uses_format_member::value && has_FormatProvider::value> { }; +// Simple template that decides whether a type T should use the operator<< +// based format() invocation. This takes last priority. +template +struct uses_stream_operator + : public std::integral_constant::value && + !uses_format_provider::value && + has_StreamOperator::value> {}; + // Simple template that decides whether a type T has neither a member-function // nor format_provider based implementation that it can use. Mostly used so // that the compiler spits out a nice diagnostic when a type with no format // implementation can be located. template struct uses_missing_provider - : public std::integral_constant::value && - !uses_format_provider::value> {}; + : public std::integral_constant::value && + !uses_format_provider::value && + !uses_stream_operator::value> { +}; template typename std::enable_if::value, T>::type @@ -101,6 +138,13 @@ build_format_adapter(T &&Item) { } template +typename std::enable_if::value, + stream_operator_format_adapter>::type +build_format_adapter(T &&Item) { + return stream_operator_format_adapter(std::forward(Item)); +} + +template typename std::enable_if::value, missing_format_adapter>::type build_format_adapter(T &&Item) { diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp index 529439f..2f9ce2d 100644 --- a/llvm/unittests/Support/ErrorTest.cpp +++ b/llvm/unittests/Support/ErrorTest.cpp @@ -32,7 +32,7 @@ public: // Log this error to a stream. void log(raw_ostream &OS) const override { - OS << "CustomError { " << getInfo() << "}"; + OS << "CustomError {" << getInfo() << "}"; } std::error_code convertToErrorCode() const override { @@ -702,25 +702,44 @@ TEST(Error, ErrorMessage) { EXPECT_EQ(toString(Error::success()).compare(""), 0); Error E1 = make_error(0); - EXPECT_EQ(toString(std::move(E1)).compare("CustomError { 0}"), 0); + EXPECT_EQ(toString(std::move(E1)).compare("CustomError {0}"), 0); Error E2 = make_error(0); handleAllErrors(std::move(E2), [](const CustomError &CE) { - EXPECT_EQ(CE.message().compare("CustomError { 0}"), 0); + EXPECT_EQ(CE.message().compare("CustomError {0}"), 0); }); Error E3 = joinErrors(make_error(0), make_error(1)); EXPECT_EQ(toString(std::move(E3)) - .compare("CustomError { 0}\n" - "CustomError { 1}"), + .compare("CustomError {0}\n" + "CustomError {1}"), 0); } +TEST(Error, Stream) { + { + Error OK = Error::success(); + std::string Buf; + llvm::raw_string_ostream S(Buf); + S << OK; + EXPECT_EQ("success", S.str()); + consumeError(std::move(OK)); + } + { + Error E1 = make_error(0); + std::string Buf; + llvm::raw_string_ostream S(Buf); + S << E1; + EXPECT_EQ("CustomError {0}", S.str()); + consumeError(std::move(E1)); + } +} + TEST(Error, ErrorMatchers) { EXPECT_THAT_ERROR(Error::success(), Succeeded()); EXPECT_NONFATAL_FAILURE( EXPECT_THAT_ERROR(make_error(0), Succeeded()), - "Expected: succeeded\n Actual: failed (CustomError { 0})"); + "Expected: succeeded\n Actual: failed (CustomError {0})"); EXPECT_THAT_ERROR(make_error(0), Failed()); EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed()), @@ -748,14 +767,14 @@ TEST(Error, ErrorMatchers) { Failed(testing::Property(&CustomError::getInfo, 1))), "Expected: failed with Error of given type and the error is an object " "whose given property is equal to 1\n" - " Actual: failed (CustomError { 0})"); + " Actual: failed (CustomError {0})"); EXPECT_THAT_ERROR(make_error(0), Failed()); EXPECT_THAT_EXPECTED(Expected(0), Succeeded()); EXPECT_NONFATAL_FAILURE( EXPECT_THAT_EXPECTED(Expected(make_error(0)), Succeeded()), - "Expected: succeeded\n Actual: failed (CustomError { 0})"); + "Expected: succeeded\n Actual: failed (CustomError {0})"); EXPECT_THAT_EXPECTED(Expected(make_error(0)), Failed()); EXPECT_NONFATAL_FAILURE( @@ -767,7 +786,7 @@ TEST(Error, ErrorMatchers) { EXPECT_THAT_EXPECTED(Expected(make_error(0)), HasValue(0)), "Expected: succeeded with value (is equal to 0)\n" - " Actual: failed (CustomError { 0})"); + " Actual: failed (CustomError {0})"); EXPECT_NONFATAL_FAILURE( EXPECT_THAT_EXPECTED(Expected(1), HasValue(0)), "Expected: succeeded with value (is equal to 0)\n" @@ -787,7 +806,7 @@ TEST(Error, ErrorMatchers) { EXPECT_THAT_EXPECTED(Expected(make_error(0)), HasValue(testing::Gt(1))), "Expected: succeeded with value (is > 1)\n" - " Actual: failed (CustomError { 0})"); + " Actual: failed (CustomError {0})"); } } // end anon namespace diff --git a/llvm/unittests/Support/FormatVariadicTest.cpp b/llvm/unittests/Support/FormatVariadicTest.cpp index 54373da..6d62146 100644 --- a/llvm/unittests/Support/FormatVariadicTest.cpp +++ b/llvm/unittests/Support/FormatVariadicTest.cpp @@ -671,3 +671,12 @@ TEST(FormatVariadicTest, CopiesAndMoves) { EXPECT_EQ(0, R.Copied); EXPECT_EQ(0, R.Moved); } + +namespace adl { +struct X {}; +raw_ostream &operator<<(raw_ostream &OS, const X &) { return OS << "X"; } +} // namespace adl +TEST(FormatVariadicTest, FormatStreamable) { + adl::X X; + EXPECT_EQ("X", formatv("{0}", X).str()); +} -- 2.7.4