Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions src/util/exception_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ Author: Fotis Koutoulakis, fotis.koutoulakis@diffblue.com
#ifndef CPROVER_UTIL_EXCEPTION_UTILS_H
#define CPROVER_UTIL_EXCEPTION_UTILS_H

#include <string>

#include "invariant.h"
#include "source_location.h"

#include <sstream>
#include <string>

/// Base class for exceptions thrown in the cprover project.
/// Intended to be used as a convenient way to have a
/// "catch all and report errors" from application entry points.
Expand Down Expand Up @@ -190,4 +191,64 @@ class invalid_source_file_exceptiont : public invalid_input_exceptiont
source_locationt source_location;
};

/// an exception class with optional source_locationt and exit code
class error_exceptiont
{
public:
error_exceptiont() = default;
error_exceptiont(const error_exceptiont &other)
{
// ostringstream does not have a copy constructor
message << other.message.str();
__exit_code = other.__exit_code;
__location = other.__location;
}
error_exceptiont(error_exceptiont &&) = default;

std::string what() const
{
return message.str();
}

std::ostringstream &message_ostream()
{
return message;
}

std::optional<int> exit_code() const
{
return __exit_code;
}

error_exceptiont with_exit_code(int exit_code) &&
{
__exit_code = exit_code;
return std::move(*this);
}

error_exceptiont with_location(source_locationt _location) &&
{
__location = std::move(_location);
return std::move(*this);
}

const source_locationt &location() const
{
return __location;
}

protected:
std::ostringstream message;
std::optional<int> __exit_code = {};
source_locationt __location = source_locationt::nil();
};

/// add to the diagnostic information in the given error_exceptiont exception
template <typename T>
error_exceptiont operator<<(error_exceptiont &&e, const T &message)
{
e.message_ostream() << message;
return std::move(e);
}

#endif // CPROVER_UTIL_EXCEPTION_UTILS_H
1 change: 1 addition & 0 deletions unit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ SRC += analyses/ai/ai.cpp \
util/dense_integer_map.cpp \
util/dstring.cpp \
util/edit_distance.cpp \
util/exception_utils.cpp \
util/expr_cast/expr_cast.cpp \
util/expr_initializer.cpp \
util/expr.cpp \
Expand Down
89 changes: 89 additions & 0 deletions unit/util/exception_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*******************************************************************\

Module: Unit tests for error_exceptiont

Author: Daniel Kroening

\*******************************************************************/

#include <util/exception_utils.h>

#include <testing-utils/use_catch.h>

TEST_CASE(
"error_exceptiont default construction",
"[core][util][exception_utils]")
{
error_exceptiont e;
CHECK(e.what().empty());
CHECK_FALSE(e.exit_code().has_value());
CHECK(e.location().is_nil());
}

TEST_CASE(
"error_exceptiont with message via operator<<",
"[core][util][exception_utils]")
{
auto e = error_exceptiont{} << "something went wrong";
CHECK(e.what() == "something went wrong");
}

TEST_CASE(
"error_exceptiont streaming multiple values",
"[core][util][exception_utils]")
{
auto e = error_exceptiont{} << "error " << 42 << " occurred";
CHECK(e.what() == "error 42 occurred");
}

TEST_CASE("error_exceptiont with exit code", "[core][util][exception_utils]")
{
auto e = error_exceptiont{}.with_exit_code(1);
REQUIRE(e.exit_code().has_value());
CHECK(e.exit_code().value() == 1);
}

TEST_CASE("error_exceptiont with location", "[core][util][exception_utils]")
{
source_locationt loc;
loc.set_file("test.c");
loc.set_line("10");

auto e = error_exceptiont{}.with_location(loc);
CHECK(e.location().get_file() == "test.c");
CHECK(e.location().get_line() == "10");
}

TEST_CASE(
"error_exceptiont chaining all features",
"[core][util][exception_utils]")
{
source_locationt loc;
loc.set_file("main.c");
loc.set_line("5");

auto e = error_exceptiont{}.with_exit_code(2).with_location(loc)
<< "parse error";

CHECK(e.what() == "parse error");
REQUIRE(e.exit_code().has_value());
CHECK(e.exit_code().value() == 2);
CHECK(e.location().get_file() == "main.c");
CHECK(e.location().get_line() == "5");
}

TEST_CASE("error_exceptiont copy constructor", "[core][util][exception_utils]")
{
auto original = error_exceptiont{}.with_exit_code(3);
original.message_ostream() << "copy test";

error_exceptiont copy(original);
CHECK(copy.what() == "copy test");
REQUIRE(copy.exit_code().has_value());
CHECK(copy.exit_code().value() == 3);

// Modifying the copy doesn't affect the original
copy.message_ostream() << " extra";
CHECK(copy.what() == "copy test extra");
CHECK(original.what() == "copy test");
}
Loading