diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -159,6 +159,7 @@ // pointers out of this class to add to the error list. friend class ErrorList; friend class FileError; + friend class SourceLocationError; // handleErrors needs to be able to set the Checked flag. template @@ -1286,6 +1287,62 @@ Error createFileError(const Twine &F, ErrorSuccess) = delete; +/// This class wraps a source location, filename plus line number, 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 SourceLocationError final : public ErrorInfo { + + friend Error createSourceLocationError(Error, const Twine &, size_t); + +public: + void log(raw_ostream &OS) const override { + assert(Err && !FileName.empty() && "Trying to log after takeError()."); + Err->log(OS); + OS << " at " << FileName << ":" << Line; + } + + Error takeError() { return Error(std::move(Err)); } + + std::error_code convertToErrorCode() const override; + + // Used by ErrorInfo::classID. + static char ID; + +private: + SourceLocationError(std::unique_ptr E, const Twine &F, + size_t Line) + : Err(std::move(E)), FileName(F.str()), Line(Line) { + assert(Err && + "Cannot create SourceLocationError from Error success value."); + assert(!FileName.empty() && + "The file name provided to SourceLocationError must not be empty."); + } + + static Error build(Error E, const Twine &F, size_t Line) { + return Error(std::unique_ptr( + new SourceLocationError(E.takePayload(), F, Line))); + } + + std::unique_ptr Err; + std::string FileName; + size_t Line; +}; + +/// Concatenate a source file path and/or name with line number and an Error. +/// The resulting Error is unchecked. +inline Error createSourceLocationError(Error E, const Twine &F, size_t Line) { + return SourceLocationError::build(std::move(E), F, Line); +} + +#ifndef NDEBUG +#define llvm_SourceLocationError(E) \ + ::llvm::createSourceLocationError(E, __FILE__, __LINE__) +#else +#define llvm_SourceLocationError(E) E +#endif + /// 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 --- a/llvm/lib/Support/Error.cpp +++ b/llvm/lib/Support/Error.cpp @@ -19,6 +19,7 @@ enum class ErrorErrorCode : int { MultipleErrors = 1, FileError, + SourceLocationError, InconvertibleError }; @@ -39,6 +40,8 @@ "bug."; case ErrorErrorCode::FileError: return "A file error occurred."; + case ErrorErrorCode::SourceLocationError: + return "A source location error occurred."; } llvm_unreachable("Unhandled error code"); } @@ -57,6 +60,7 @@ char ECError::ID = 0; char StringError::ID = 0; char FileError::ID = 0; +char SourceLocationError::ID = 0; void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) { if (!E) @@ -84,6 +88,11 @@ *ErrorErrorCat); } +std::error_code SourceLocationError::convertToErrorCode() const { + return std::error_code(static_cast(ErrorErrorCode::SourceLocationError), + *ErrorErrorCat); +} + Error errorCodeToError(std::error_code EC) { if (!EC) return Error::success(); diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp --- a/llvm/unittests/Support/ErrorTest.cpp +++ b/llvm/unittests/Support/ErrorTest.cpp @@ -906,6 +906,25 @@ 0); } +TEST(Error, SourceLocationErrorTest) { + std::string ErrStr; + raw_string_ostream OS(ErrStr); + OS << "^CustomError \\{1\\}"; + +#if defined(NDEBUG) + // __FILE__ and __LINE_ not added + OS << "$"; + auto E = llvm_SourceLocationError(make_error(1)); + EXPECT_THAT(toString(std::move(FE1)), ::testing::ContainsRegex(OS.str())); +#else + // __FILE__ and __LINE__ added + int Line = __LINE__; + OS << " at " << __FILE__ << ":" << (Line + 2) << "$"; + auto E = llvm_SourceLocationError(make_error(1)); + EXPECT_THAT(toString(std::move(E)), ::testing::ContainsRegex(OS.str())); +#endif +} + enum class test_error_code { unspecified = 1, error_1,