// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "common/error.h" #include #include #include #include "common/error_test_helpers.h" #include "common/raw_string_ostream.h" namespace Carbon { namespace { using ::Carbon::Testing::IsError; using ::Carbon::Testing::IsSuccess; using ::testing::_; using ::testing::Eq; using ::testing::VariantWith; TEST(ErrorTest, Error) { Error err("test"); EXPECT_EQ(err.message(), "test"); } TEST(ErrorTest, ErrorEmptyString) { ASSERT_DEATH({ Error err(""); }, "CHECK failure at"); } auto IndirectError() -> Error { return Error("test"); } TEST(ErrorTest, IndirectError) { EXPECT_EQ(IndirectError().message(), "test"); } TEST(ErrorTest, ErrorBuilderOperatorImplicitCast) { ErrorOr result = ErrorBuilder() << "msg"; EXPECT_THAT(result, IsError("msg")); } // Make sure a custom error type can be forward declared and used with `ErrorOr` // until the `ErrorOr` is required to be complete itself. class CustomError; auto TestFunction() -> ErrorOr; class CustomError : public ErrorBase { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Custom test error!"; } }; auto TestFunction() -> ErrorOr { return CustomError(); } TEST(ErrorTest, UseErrorOrWithCustomError) { // Uses `TestFunction` to ensure it compiles correctly with forward // declarations above. EXPECT_THAT(TestFunction(), IsError("Custom test error!")); } template class ErrorOrTest : public ::testing::Test { public: auto ErrorStr() -> std::string { if constexpr (std::same_as) { return "test error"; } else if constexpr (std::same_as) { return CustomError().ToString(); } else { static_assert(false, "Unsupported custom error type!"); } } auto MakeError() -> ErrorT { if constexpr (std::same_as) { return Error("test error"); } else if constexpr (std::same_as) { return CustomError(); } else { static_assert(false, "Unsupported custom error type!"); } } }; using ErrorOrTestParams = ::testing::Types; TYPED_TEST_SUITE(ErrorOrTest, ErrorOrTestParams); TYPED_TEST(ErrorOrTest, ErrorOr) { using TestErrorOr = ErrorOr; TestErrorOr err(this->MakeError()); EXPECT_THAT(err, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, ErrorOrValue) { using TestErrorOr = ErrorOr; EXPECT_TRUE(TestErrorOr(0).ok()); } template auto IndirectErrorOrTest(Fixture& fixture) -> ErrorOr { return fixture.MakeError(); } TYPED_TEST(ErrorOrTest, IndirectErrorOr) { EXPECT_FALSE(IndirectErrorOrTest(*this).ok()); } struct Val { int val; }; TYPED_TEST(ErrorOrTest, ErrorOrArrowOp) { using TestErrorOr = ErrorOr; TestErrorOr err({1}); EXPECT_EQ(err->val, 1); } TYPED_TEST(ErrorOrTest, ErrorOrReference) { using TestErrorOr = ErrorOr; Val val = {1}; TestErrorOr maybe_val(val); EXPECT_EQ(maybe_val->val, 1); } template auto IndirectErrorOrSuccessTest() -> ErrorOr { return Success(); } TYPED_TEST(ErrorOrTest, IndirectErrorOrSuccess) { EXPECT_TRUE(IndirectErrorOrSuccessTest().ok()); } TYPED_TEST(ErrorOrTest, MoveValue) { using TestErrorOr = ErrorOr, TypeParam>; auto make_value = []() -> TestErrorOr { return std::make_unique(42); }; std::unique_ptr p = *make_value(); EXPECT_THAT(*p, Eq(42)); auto result = make_value(); std::unique_ptr p2 = *std::move(result); EXPECT_THAT(*p2, Eq(42)); } TYPED_TEST(ErrorOrTest, UnprintableValue) { struct X { int i; }; using TestErrorOr = ErrorOr; TestErrorOr value(X{.i = 42}); EXPECT_THAT(value, IsSuccess(_)); TestErrorOr error = this->MakeError(); EXPECT_THAT(error, IsError(this->ErrorStr())); } // Note that this is more of a test of `IsSuccess` than `ErrorOr` itself. TYPED_TEST(ErrorOrTest, NestedMatching) { using TestErrorOr = ErrorOr, TypeParam>; TestErrorOr i(42); EXPECT_THAT(i, IsSuccess(VariantWith(Eq(42)))); TestErrorOr f(0.42F); EXPECT_THAT(f, IsSuccess(VariantWith(Eq(0.42F)))); } TYPED_TEST(ErrorOrTest, ReturnIfErrorNoError) { using TestErrorOr = ErrorOr; auto result = []() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); return Success(); }(); EXPECT_TRUE(result.ok()); } TYPED_TEST(ErrorOrTest, ReturnIfErrorHasError) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(Success())); CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return Success(); }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnNoError) { using TestErrorOr = ErrorOr; auto result = []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, TestErrorOr(2)); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasDirectError) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_RETURN_IF_ERROR(TestErrorOr(this->MakeError())); return 0; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } TYPED_TEST(ErrorOrTest, AssignOrReturnHasErrorInExpected) { using TestErrorOr = ErrorOr; auto result = [this]() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(this->MakeError())); return a; }(); EXPECT_THAT(result, IsError(this->ErrorStr())); } class AnotherCustomError : public ErrorBase { public: auto Print(llvm::raw_ostream& os) const -> void { os << "Another custom test error!"; } explicit operator CustomError() { return CustomError(); } }; TYPED_TEST(ErrorOrTest, AssignOrReturnNoErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr; auto result = []() -> ErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN(int inner, (ErrorOr(2))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); EXPECT_THAT(result, IsSuccess(Eq(6))); } TYPED_TEST(ErrorOrTest, AssignOrReturnErrorAcrossErrorTypes) { using TestErrorOr = ErrorOr; auto result = []() -> ErrorOr { CARBON_ASSIGN_OR_RETURN(int a, TestErrorOr(1)); CARBON_ASSIGN_OR_RETURN(const int b, []() -> TestErrorOr { CARBON_ASSIGN_OR_RETURN( int inner, (ErrorOr(AnotherCustomError()))); return inner; }()); int c = 0; CARBON_ASSIGN_OR_RETURN(c, TestErrorOr(3)); return a + b + c; }(); // When directly using the `Error` type, the explicit custom type above has // its message preserved. When testing against `CustomError`, that one // overrides the message. if constexpr (std::same_as) { EXPECT_THAT(result, IsError("Another custom test error!")); } else { EXPECT_THAT(result, IsError("Custom test error!")); } } } // namespace } // namespace Carbon