Skip to content

Commit

Permalink
[Support] Add ExitOnError utility to support tools that use the exit-…
Browse files Browse the repository at this point in the history
…on-error

idiom.

Most LLVM tool code exits immediately when an error is encountered and prints an
error message to stderr. The ExitOnError class supports this by providing two
call operators - one for Errors, and one for Expected<T>s. Calls to code that
can return Errors (or Expected<T>s) can use these calls to bail out on error,
and otherwise continue as if the operation had succeeded. E.g.

Error foo();
Expected<int> bar();

int main(int argc, char *argv[]) {
  ExitOnError ExitOnErr;

  ExitOnErr.setBanner(std::string("Error in ") + argv[0] + ":");

  // Exit if foo returns an error. No need to manually check error return.
  ExitOnErr(foo());

  // Exit if bar returns an error, otherwise unwrap the contained int and
  // continue.
  int X = ExitOnErr(bar());

  // ...

  return 0;
}



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@263749 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
lhames committed Mar 17, 2016
1 parent 3d86278 commit 0583c93
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
52 changes: 51 additions & 1 deletion include/llvm/Support/Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,8 @@ inline void handleAllErrors(Error E) {
/// (allowing clean deallocation of resources, etc.), while reporting error
/// information to the user.
template <typename... HandlerTs>
void logAllUnhandledErrors(Error E, raw_ostream &OS, std::string ErrorBanner) {
void logAllUnhandledErrors(Error E, raw_ostream &OS,
const std::string &ErrorBanner) {
if (!E)
return;
OS << ErrorBanner;
Expand Down Expand Up @@ -742,6 +743,55 @@ inline std::error_code errorToErrorCode(Error Err) {
return EC;
}

/// Helper for check-and-exit error handling.
///
/// For tool use only. NOT FOR USE IN LIBRARY CODE.
///
class ExitOnError {
public:

/// Create an error on exit helper.
ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1)
: Banner(Banner),
GetExitCode([=](const Error&) { return DefaultErrorExitCode; }) {}

/// Set the banner string for any errors caught by operator().
void setBanner(std::string Banner) {
this->Banner = Banner;
}

/// Set the exit-code mapper function.
void setExitCodeMapper(std::function<int(const Error&)> GetExitCode) {
this->GetExitCode = GetExitCode;
}

/// Check Err. If it's in a failure state log the error(s) and exit.
void operator()(Error Err) const {
checkError(std::move(Err));
}

/// Check E. If it's in a success state return the contained value. If it's
/// in a failure state log the error(s) and exit.
template <typename T>
T operator()(Expected<T> E) const {
checkError(E.takeError());
return std::move(*E);
}

private:

void checkError(Error Err) const {
if (Err) {
int ExitCode = GetExitCode(Err);
logAllUnhandledErrors(std::move(Err), errs(), Banner);
exit(ExitCode);
}
}

std::string Banner;
std::function<int(const Error&)> GetExitCode;
};

} // namespace llvm

#endif // LLVM_SUPPORT_ERROR_H
28 changes: 28 additions & 0 deletions unittests/Support/ErrorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace {
// - consume_error to consume a "safe" error without any output.
// - handleAllUnhandledErrors to assert that all errors are handled.
// - logAllUnhandledErrors to log errors to a stream.
// - ExitOnError tests.
//
// Expected tests:
// - Expected<T> with T.
Expand All @@ -50,6 +51,7 @@ namespace {
// - std::error_code to Error (ECError) in failure mode.
// - Error to std::error_code in success mode.
// - Error (ECError) to std::error_code in failure mode.
//

// Custom error class with a default base class and some random 'info' attached.
class CustomError : public ErrorInfo<CustomError> {
Expand Down Expand Up @@ -376,6 +378,32 @@ TEST(Error, CheckErrorUtilities) {
EXPECT_EQ(ErrorInfo, 7)
<< "Failed to handle Error returned from handleErrors.";
}

// Test ExitOnError
{
ExitOnError ExitOnErr;
ExitOnErr.setBanner("Error in tool:");
ExitOnErr.setExitCodeMapper(
[](const Error &E) {
if (E.isA<CustomSubError>())
return 2;
return 1;
});

// Make sure we don't bail on success.
ExitOnErr(Error::success());
EXPECT_EQ(ExitOnErr(Expected<int>(7)), 7)
<< "exitOnError returned an invalid value for Expected";

// Exit tests.
EXPECT_EXIT(ExitOnErr(make_error<CustomError>(7)),
::testing::ExitedWithCode(1), "Error in tool:")
<< "exitOnError returned an unexpected error result";

EXPECT_EXIT(ExitOnErr(Expected<int>(make_error<CustomSubError>(0, 0))),
::testing::ExitedWithCode(2), "Error in tool:")
<< "exitOnError returned an unexpected error result";
}
}

// Test Expected behavior.
Expand Down

0 comments on commit 0583c93

Please sign in to comment.