summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/Support/Error.h86
-rw-r--r--llvm/lib/Support/Error.cpp24
-rw-r--r--llvm/unittests/Support/ErrorTest.cpp112
3 files changed, 217 insertions, 5 deletions
diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index b82663d5d87..9c49fa989e0 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -156,9 +156,10 @@ private:
/// they're moved-assigned or constructed from Success values that have already
/// been checked. This enforces checking through all levels of the call stack.
class LLVM_NODISCARD Error {
- // ErrorList needs to be able to yank ErrorInfoBase pointers out of this
- // class to add to the error list.
+ // Both ErrorList and FileError need to be able to yank ErrorInfoBase
+ // pointers out of this class to add to the error list.
friend class ErrorList;
+ friend class FileError;
// handleErrors needs to be able to set the Checked flag.
template <typename... HandlerTs>
@@ -343,6 +344,8 @@ template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) {
template <typename ThisErrT, typename ParentErrT = ErrorInfoBase>
class ErrorInfo : public ParentErrT {
public:
+ using ParentErrT::ParentErrT; // inherit constructors
+
static const void *classID() { return &ThisErrT::ID; }
const void *dynamicClassID() const override { return &ThisErrT::ID; }
@@ -1110,10 +1113,33 @@ template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) {
/// StringError is useful in cases where the client is not expected to be able
/// to consume the specific error message programmatically (for example, if the
/// error message is to be presented to the user).
+///
+/// StringError can also be used when additional information is to be printed
+/// along with a error_code message. Depending on the constructor called, this
+/// class can either display:
+/// 1. the error_code message (ECError behavior)
+/// 2. a string
+/// 3. the error_code message and a string
+///
+/// These behaviors are useful when subtyping is required; for example, when a
+/// specific library needs an explicit error type. In the example below,
+/// PDBError is derived from StringError:
+///
+/// @code{.cpp}
+/// Expected<int> foo() {
+/// return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading,
+/// "Additional information");
+/// }
+/// @endcode
+///
class StringError : public ErrorInfo<StringError> {
public:
static char ID;
+ // Prints EC + S and converts to EC
+ StringError(std::error_code EC, const Twine &S = Twine());
+
+ // Prints S and converts to EC
StringError(const Twine &S, std::error_code EC);
void log(raw_ostream &OS) const override;
@@ -1124,6 +1150,7 @@ public:
private:
std::string Msg;
std::error_code EC;
+ const bool PrintMsgOnly = false;
};
/// Create formatted StringError object.
@@ -1138,6 +1165,61 @@ Error createStringError(std::error_code EC, char const *Fmt,
Error createStringError(std::error_code EC, char const *Msg);
+/// This class wraps a filename and another Error.
+///
+/// In some cases, an error needs to live along a 'source' name, in order to
+/// show more detailed information to the user.
+class FileError final : public ErrorInfo<FileError> {
+
+ template <class Err>
+ friend Error createFileError(
+ std::string, Err,
+ typename std::enable_if<std::is_base_of<Error, Err>::value &&
+ !std::is_base_of<ErrorSuccess, Err>::value>::type
+ *);
+
+public:
+ void log(raw_ostream &OS) const override {
+ assert(Err && !FileName.empty() && "Trying to log after takeError().");
+ OS << "'" << FileName << "': ";
+ Err->log(OS);
+ }
+
+ Error takeError() { return Error(std::move(Err)); }
+
+ std::error_code convertToErrorCode() const override;
+
+ // Used by ErrorInfo::classID.
+ static char ID;
+
+private:
+ FileError(std::string F, std::unique_ptr<ErrorInfoBase> E) {
+ assert(E && "Cannot create FileError from Error success value.");
+ assert(!F.empty() &&
+ "The file name provided to FileError must not be empty.");
+ FileName = F;
+ Err = std::move(E);
+ }
+
+ static Error build(std::string F, Error E) {
+ return Error(std::unique_ptr<FileError>(new FileError(F, E.takePayload())));
+ }
+
+ std::string FileName;
+ std::unique_ptr<ErrorInfoBase> Err;
+};
+
+/// Concatenate a source file path and/or name with an Error. The resulting
+/// Error is unchecked.
+template <class Err>
+inline Error createFileError(
+ std::string F, Err E,
+ typename std::enable_if<std::is_base_of<Error, Err>::value &&
+ !std::is_base_of<ErrorSuccess, Err>::value>::type
+ * = nullptr) {
+ return FileError::build(F, std::move(E));
+}
+
/// Helper for check-and-exit error handling.
///
/// For tool use only. NOT FOR USE IN LIBRARY CODE.
diff --git a/llvm/lib/Support/Error.cpp b/llvm/lib/Support/Error.cpp
index d66e0e55456..ad2443db9af 100644
--- a/llvm/lib/Support/Error.cpp
+++ b/llvm/lib/Support/Error.cpp
@@ -19,6 +19,7 @@ namespace {
enum class ErrorErrorCode : int {
MultipleErrors = 1,
+ FileError,
InconvertibleError
};
@@ -37,6 +38,8 @@ namespace {
return "Inconvertible error value. An error has occurred that could "
"not be converted to a known std::error_code. Please file a "
"bug.";
+ case ErrorErrorCode::FileError:
+ return "A file error occurred.";
}
llvm_unreachable("Unhandled error code");
}
@@ -53,6 +56,7 @@ char ErrorInfoBase::ID = 0;
char ErrorList::ID = 0;
char ECError::ID = 0;
char StringError::ID = 0;
+char FileError::ID = 0;
void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) {
if (!E)
@@ -75,6 +79,11 @@ std::error_code inconvertibleErrorCode() {
*ErrorErrorCat);
}
+std::error_code FileError::convertToErrorCode() const {
+ return std::error_code(static_cast<int>(ErrorErrorCode::FileError),
+ *ErrorErrorCat);
+}
+
Error errorCodeToError(std::error_code EC) {
if (!EC)
return Error::success();
@@ -103,10 +112,21 @@ void Error::fatalUncheckedError() const {
}
#endif
-StringError::StringError(const Twine &S, std::error_code EC)
+StringError::StringError(std::error_code EC, const Twine &S)
: Msg(S.str()), EC(EC) {}
-void StringError::log(raw_ostream &OS) const { OS << Msg; }
+StringError::StringError(const Twine &S, std::error_code EC)
+ : Msg(S.str()), EC(EC), PrintMsgOnly(true) {}
+
+void StringError::log(raw_ostream &OS) const {
+ if (PrintMsgOnly) {
+ OS << Msg;
+ } else {
+ OS << EC.message();
+ if (!Msg.empty())
+ OS << (" " + Msg);
+ }
+}
std::error_code StringError::convertToErrorCode() const {
return EC;
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 0b1a489eb44..d3d26df8ceb 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -13,6 +13,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest-spi.h"
#include "gtest/gtest.h"
@@ -104,7 +105,7 @@ TEST(Error, CheckedSuccess) {
EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";
}
-// Test that unchecked succes values cause an abort.
+// Test that unchecked success values cause an abort.
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
TEST(Error, UncheckedSuccess) {
EXPECT_DEATH({ Error E = Error::success(); },
@@ -864,4 +865,113 @@ TEST(Error, C_API) {
EXPECT_TRUE(GotCE) << "Failed to round-trip ErrorList via C API";
}
+TEST(Error, FileErrorTest) {
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+ EXPECT_DEATH(
+ {
+ Error S = Error::success();
+ createFileError("file.bin", std::move(S));
+ },
+ "");
+#endif
+ Error E1 = make_error<CustomError>(1);
+ Error FE1 = createFileError("file.bin", std::move(E1));
+ EXPECT_EQ(toString(std::move(FE1)).compare("'file.bin': CustomError {1}"), 0);
+
+ Error E2 = make_error<CustomError>(2);
+ Error FE2 = createFileError("file.bin", std::move(E2));
+ handleAllErrors(std::move(FE2), [](const FileError &F) {
+ EXPECT_EQ(F.message().compare("'file.bin': CustomError {2}"), 0);
+ });
+
+ Error E3 = make_error<CustomError>(3);
+ Error FE3 = createFileError("file.bin", std::move(E3));
+ auto E31 = handleErrors(std::move(FE3), [](std::unique_ptr<FileError> F) {
+ return F->takeError();
+ });
+ handleAllErrors(std::move(E31), [](const CustomError &C) {
+ EXPECT_EQ(C.message().compare("CustomError {3}"), 0);
+ });
+
+ Error FE4 =
+ joinErrors(createFileError("file.bin", make_error<CustomError>(41)),
+ createFileError("file2.bin", make_error<CustomError>(42)));
+ EXPECT_EQ(toString(std::move(FE4))
+ .compare("'file.bin': CustomError {41}\n"
+ "'file2.bin': CustomError {42}"),
+ 0);
+}
+
+enum class test_error_code {
+ unspecified = 1,
+ error_1,
+ error_2,
+};
+
} // end anon namespace
+
+namespace std {
+ template <>
+ struct is_error_code_enum<test_error_code> : std::true_type {};
+} // namespace std
+
+namespace {
+
+const std::error_category &TErrorCategory();
+
+inline std::error_code make_error_code(test_error_code E) {
+ return std::error_code(static_cast<int>(E), TErrorCategory());
+}
+
+class TestDebugError : public ErrorInfo<TestDebugError, StringError> {
+public:
+ using ErrorInfo<TestDebugError, StringError >::ErrorInfo; // inherit constructors
+ TestDebugError(const Twine &S) : ErrorInfo(S, test_error_code::unspecified) {}
+ static char ID;
+};
+
+class TestErrorCategory : public std::error_category {
+public:
+ const char *name() const noexcept override { return "error"; }
+ std::string message(int Condition) const override {
+ switch (static_cast<test_error_code>(Condition)) {
+ case test_error_code::unspecified:
+ return "An unknown error has occurred.";
+ case test_error_code::error_1:
+ return "Error 1.";
+ case test_error_code::error_2:
+ return "Error 2.";
+ }
+ llvm_unreachable("Unrecognized test_error_code");
+ }
+};
+
+static llvm::ManagedStatic<TestErrorCategory> TestErrCategory;
+const std::error_category &TErrorCategory() { return *TestErrCategory; }
+
+char TestDebugError::ID;
+
+TEST(Error, SubtypeStringErrorTest) {
+ auto E1 = make_error<TestDebugError>(test_error_code::error_1);
+ EXPECT_EQ(toString(std::move(E1)).compare("Error 1."), 0);
+
+ auto E2 = make_error<TestDebugError>(test_error_code::error_1,
+ "Detailed information");
+ EXPECT_EQ(toString(std::move(E2)).compare("Error 1. Detailed information"),
+ 0);
+
+ auto E3 = make_error<TestDebugError>(test_error_code::error_2);
+ handleAllErrors(std::move(E3), [](const TestDebugError &F) {
+ EXPECT_EQ(F.message().compare("Error 2."), 0);
+ });
+
+ auto E4 = joinErrors(make_error<TestDebugError>(test_error_code::error_1,
+ "Detailed information"),
+ make_error<TestDebugError>(test_error_code::error_2));
+ EXPECT_EQ(toString(std::move(E4))
+ .compare("Error 1. Detailed information\n"
+ "Error 2."),
+ 0);
+}
+
+} // namespace
OpenPOWER on IntegriCloud