//===- TypeSwitchTest.cpp - TypeSwitch unit tests -------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/TypeSwitch.h" #include "gtest/gtest.h" using namespace llvm; namespace { /// Utility classes to setup casting functionality. struct Base { enum Kind { DerivedA, DerivedB, DerivedC, DerivedD, DerivedE }; Kind kind; }; template struct DerivedImpl : Base { DerivedImpl() : Base{DerivedKind} {} static bool classof(const Base *base) { return base->kind == DerivedKind; } }; struct DerivedA : public DerivedImpl {}; struct DerivedB : public DerivedImpl {}; struct DerivedC : public DerivedImpl {}; struct DerivedD : public DerivedImpl {}; struct DerivedE : public DerivedImpl {}; } // end anonymous namespace TEST(TypeSwitchTest, CaseResult) { auto translate = [](auto value) { return TypeSwitch(&value) .Case([](DerivedA *) { return 0; }) .Case([](DerivedB *) { return 1; }) .Case([](DerivedC *) { return 2; }) .Default([](Base *) { return -1; }); }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(1, translate(DerivedB())); EXPECT_EQ(2, translate(DerivedC())); EXPECT_EQ(-1, translate(DerivedD())); } TEST(TypeSwitchTest, CasesResult) { auto translate = [](auto value) { return TypeSwitch(&value) .Case([](auto *) { return 0; }) .Case([](DerivedC *) { return 1; }) .Default(-1); }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(0, translate(DerivedB())); EXPECT_EQ(1, translate(DerivedC())); EXPECT_EQ(0, translate(DerivedD())); EXPECT_EQ(-1, translate(DerivedE())); } TEST(TypeSwitchTest, CaseVoid) { auto translate = [](auto value) { int result = -2; TypeSwitch(&value) .Case([&](DerivedA *) { result = 0; }) .Case([&](DerivedB *) { result = 1; }) .Case([&](DerivedC *) { result = 2; }) .Default([&](Base *) { result = -1; }); return result; }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(1, translate(DerivedB())); EXPECT_EQ(2, translate(DerivedC())); EXPECT_EQ(-1, translate(DerivedD())); } TEST(TypeSwitchTest, CasesVoid) { auto translate = [](auto value) { int result = -1; TypeSwitch(&value) .Case([&](auto *) { result = 0; }) .Case([&](DerivedC *) { result = 1; }); return result; }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(0, translate(DerivedB())); EXPECT_EQ(1, translate(DerivedC())); EXPECT_EQ(0, translate(DerivedD())); EXPECT_EQ(-1, translate(DerivedE())); } TEST(TypeSwitchTest, CaseOptional) { auto translate = [](auto value) { return TypeSwitch>(&value) .Case([](DerivedA *) { return 0; }) .Case([](DerivedC *) { return std::nullopt; }) .Default([](Base *) { return -1; }); }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(std::nullopt, translate(DerivedC())); EXPECT_EQ(-1, translate(DerivedD())); EXPECT_EQ(std::nullopt, (TypeSwitch>(nullptr).Default( [](Base *) { return std::nullopt; }))); } TEST(TypeSwitchTest, CasesOptional) { auto translate = [](auto value) { return TypeSwitch>(&value) .Case([](auto *) { return std::nullopt; }) .Case([](DerivedA *) { return 0; }) .Default([](Base *) { return -1; }); }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(std::nullopt, translate(DerivedB())); EXPECT_EQ(std::nullopt, translate(DerivedC())); EXPECT_EQ(-1, translate(DerivedD())); } TEST(TypeSwitchTest, DefaultUnreachableWithValue) { auto translate = [](auto value) { return TypeSwitch(&value) .Case([](DerivedA *) { return 0; }) .DefaultUnreachable("Unhandled type"); }; EXPECT_EQ(0, translate(DerivedA())); #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) EXPECT_DEATH((void)translate(DerivedD()), "Unhandled type"); #endif } TEST(TypeSwitchTest, DefaultUnreachableWithVoid) { auto translate = [](auto value) { int result = -1; TypeSwitch(&value) .Case([&result](DerivedA *) { result = 0; }) .DefaultUnreachable("Unhandled type"); return result; }; EXPECT_EQ(0, translate(DerivedA())); #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) EXPECT_DEATH((void)translate(DerivedD()), "Unhandled type"); #endif } TEST(TypeSwitchTest, DefaultNullopt) { auto translate = [](auto value) { return TypeSwitch>(&value) .Case([](DerivedA *) { return 0; }) .Default(std::nullopt); }; EXPECT_EQ(0, translate(DerivedA())); EXPECT_EQ(std::nullopt, translate(DerivedD())); } TEST(TypeSwitchTest, DefaultNullptr) { float foo = 0.0f; auto translate = [&](auto value) { return TypeSwitch(&value) .Case([&](DerivedA *) { return &foo; }) .Default(nullptr); }; EXPECT_EQ(&foo, translate(DerivedA())); EXPECT_EQ(nullptr, translate(DerivedD())); } TEST(TypeSwitchTest, DefaultNullptrForPointerLike) { struct Value { void *ptr; Value(const Value &other) = default; Value(std::nullptr_t) : ptr(nullptr) {} Value() : Value(nullptr) {} }; float foo = 0.0f; Value fooVal; fooVal.ptr = &foo; auto translate = [&](auto value) { return TypeSwitch(&value) .Case([&](DerivedA *) { return fooVal; }) .Default(nullptr); }; EXPECT_EQ(&foo, translate(DerivedA()).ptr); EXPECT_EQ(nullptr, translate(DerivedD()).ptr); }