Only using that change in StringRef already decreases the number of preoprocessed lines from 7837621 to 7776151 for LLVMSupport Perhaps more interestingly, it shows that many files were relying on the inclusion of StringRef.h to have the declaration from STLExtras.h. This patch tries hard to patch relevant part of llvm-project impacted by this hidden dependency removal. Potential impact: - "llvm/ADT/StringRef.h" no longer includes <memory>, "llvm/ADT/Optional.h" nor "llvm/ADT/STLExtras.h" Related Discourse thread: https://llvm.discourse.group/t/include-what-you-use-include-cleanup/5831
300 lines
8.9 KiB
C++
300 lines
8.9 KiB
C++
//===- SequenceTest.cpp - Unit tests for a sequence abstraciton -----------===//
|
|
//
|
|
// 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/Sequence.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
|
|
using namespace llvm;
|
|
|
|
using testing::ElementsAre;
|
|
using testing::IsEmpty;
|
|
|
|
namespace {
|
|
|
|
using detail::canTypeFitValue;
|
|
using detail::CheckedInt;
|
|
|
|
using IntegralTypes = testing::Types<uint8_t, // 0
|
|
uint16_t, // 1
|
|
uint32_t, // 2
|
|
uint64_t, // 3
|
|
uintmax_t, // 4
|
|
int8_t, // 5
|
|
int16_t, // 6
|
|
int32_t, // 7
|
|
int64_t, // 8
|
|
intmax_t // 9
|
|
>;
|
|
|
|
template <class T> class StrongIntTest : public testing::Test {};
|
|
TYPED_TEST_SUITE(StrongIntTest, IntegralTypes, );
|
|
TYPED_TEST(StrongIntTest, Operations) {
|
|
using T = TypeParam;
|
|
auto Max = std::numeric_limits<T>::max();
|
|
auto Min = std::numeric_limits<T>::min();
|
|
|
|
// We bail out for types that are not entirely representable within intmax_t.
|
|
if (!canTypeFitValue<intmax_t>(Max) || !canTypeFitValue<intmax_t>(Min))
|
|
return;
|
|
|
|
// All representable values convert back and forth.
|
|
EXPECT_EQ(CheckedInt::from(Min).template to<T>(), Min);
|
|
EXPECT_EQ(CheckedInt::from(Max).template to<T>(), Max);
|
|
|
|
// Addition -2, -1, 0, 1, 2.
|
|
const T Expected = Max / 2;
|
|
const CheckedInt Actual = CheckedInt::from(Expected);
|
|
EXPECT_EQ((Actual + -2).template to<T>(), Expected - 2);
|
|
EXPECT_EQ((Actual + -1).template to<T>(), Expected - 1);
|
|
EXPECT_EQ((Actual + 0).template to<T>(), Expected);
|
|
EXPECT_EQ((Actual + 1).template to<T>(), Expected + 1);
|
|
EXPECT_EQ((Actual + 2).template to<T>(), Expected + 2);
|
|
|
|
// EQ/NEQ
|
|
EXPECT_EQ(Actual, Actual);
|
|
EXPECT_NE(Actual, Actual + 1);
|
|
|
|
// Difference
|
|
EXPECT_EQ(Actual - Actual, 0);
|
|
EXPECT_EQ((Actual + 1) - Actual, 1);
|
|
EXPECT_EQ(Actual - (Actual + 2), -2);
|
|
}
|
|
|
|
#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
|
|
TEST(StrongIntDeathTest, OutOfBounds) {
|
|
// Values above 'INTMAX_MAX' are not representable.
|
|
EXPECT_DEATH(CheckedInt::from<uintmax_t>(INTMAX_MAX + 1ULL), "Out of bounds");
|
|
EXPECT_DEATH(CheckedInt::from<uintmax_t>(UINTMAX_MAX), "Out of bounds");
|
|
// Casting to narrower type asserts when out of bounds.
|
|
EXPECT_DEATH(CheckedInt::from(-1).to<uint8_t>(), "Out of bounds");
|
|
EXPECT_DEATH(CheckedInt::from(256).to<uint8_t>(), "Out of bounds");
|
|
// Operations leading to intmax_t overflow assert.
|
|
EXPECT_DEATH(CheckedInt::from(INTMAX_MAX) + 1, "Out of bounds");
|
|
EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) + -1, "Out of bounds");
|
|
EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) - CheckedInt::from(INTMAX_MAX),
|
|
"Out of bounds");
|
|
}
|
|
#endif
|
|
|
|
TEST(SafeIntIteratorTest, Operations) {
|
|
detail::SafeIntIterator<int, false> Forward(0);
|
|
detail::SafeIntIterator<int, true> Reverse(0);
|
|
|
|
const auto SetToZero = [&]() {
|
|
Forward = detail::SafeIntIterator<int, false>(0);
|
|
Reverse = detail::SafeIntIterator<int, true>(0);
|
|
};
|
|
|
|
// Equality / Comparisons
|
|
SetToZero();
|
|
EXPECT_EQ(Forward, Forward);
|
|
EXPECT_LT(Forward - 1, Forward);
|
|
EXPECT_LE(Forward, Forward);
|
|
EXPECT_LE(Forward - 1, Forward);
|
|
EXPECT_GT(Forward + 1, Forward);
|
|
EXPECT_GE(Forward, Forward);
|
|
EXPECT_GE(Forward + 1, Forward);
|
|
|
|
EXPECT_EQ(Reverse, Reverse);
|
|
EXPECT_LT(Reverse - 1, Reverse);
|
|
EXPECT_LE(Reverse, Reverse);
|
|
EXPECT_LE(Reverse - 1, Reverse);
|
|
EXPECT_GT(Reverse + 1, Reverse);
|
|
EXPECT_GE(Reverse, Reverse);
|
|
EXPECT_GE(Reverse + 1, Reverse);
|
|
|
|
// Dereference
|
|
SetToZero();
|
|
EXPECT_EQ(*Forward, 0);
|
|
EXPECT_EQ(*Reverse, 0);
|
|
|
|
// Indexing
|
|
SetToZero();
|
|
EXPECT_EQ(Forward[2], 2);
|
|
EXPECT_EQ(Reverse[2], -2);
|
|
|
|
// Pre-increment
|
|
SetToZero();
|
|
++Forward;
|
|
EXPECT_EQ(*Forward, 1);
|
|
++Reverse;
|
|
EXPECT_EQ(*Reverse, -1);
|
|
|
|
// Pre-decrement
|
|
SetToZero();
|
|
--Forward;
|
|
EXPECT_EQ(*Forward, -1);
|
|
--Reverse;
|
|
EXPECT_EQ(*Reverse, 1);
|
|
|
|
// Post-increment
|
|
SetToZero();
|
|
EXPECT_EQ(*(Forward++), 0);
|
|
EXPECT_EQ(*Forward, 1);
|
|
EXPECT_EQ(*(Reverse++), 0);
|
|
EXPECT_EQ(*Reverse, -1);
|
|
|
|
// Post-decrement
|
|
SetToZero();
|
|
EXPECT_EQ(*(Forward--), 0);
|
|
EXPECT_EQ(*Forward, -1);
|
|
EXPECT_EQ(*(Reverse--), 0);
|
|
EXPECT_EQ(*Reverse, 1);
|
|
|
|
// Compound assignment operators
|
|
SetToZero();
|
|
Forward += 1;
|
|
EXPECT_EQ(*Forward, 1);
|
|
Reverse += 1;
|
|
EXPECT_EQ(*Reverse, -1);
|
|
SetToZero();
|
|
Forward -= 2;
|
|
EXPECT_EQ(*Forward, -2);
|
|
Reverse -= 2;
|
|
EXPECT_EQ(*Reverse, 2);
|
|
|
|
// Arithmetic
|
|
SetToZero();
|
|
EXPECT_EQ(*(Forward + 3), 3);
|
|
EXPECT_EQ(*(Reverse + 3), -3);
|
|
SetToZero();
|
|
EXPECT_EQ(*(Forward - 4), -4);
|
|
EXPECT_EQ(*(Reverse - 4), 4);
|
|
|
|
// Difference
|
|
SetToZero();
|
|
EXPECT_EQ(Forward - Forward, 0);
|
|
EXPECT_EQ(Reverse - Reverse, 0);
|
|
EXPECT_EQ((Forward + 1) - Forward, 1);
|
|
EXPECT_EQ(Forward - (Forward + 1), -1);
|
|
EXPECT_EQ((Reverse + 1) - Reverse, 1);
|
|
EXPECT_EQ(Reverse - (Reverse + 1), -1);
|
|
}
|
|
|
|
TEST(SequenceTest, Iteration) {
|
|
EXPECT_THAT(seq(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4));
|
|
EXPECT_THAT(reverse(seq(-4, 5)), ElementsAre(4, 3, 2, 1, 0, -1, -2, -3, -4));
|
|
|
|
EXPECT_THAT(seq_inclusive(-4, 5),
|
|
ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4, 5));
|
|
EXPECT_THAT(reverse(seq_inclusive(-4, 5)),
|
|
ElementsAre(5, 4, 3, 2, 1, 0, -1, -2, -3, -4));
|
|
}
|
|
|
|
TEST(SequenceTest, Distance) {
|
|
const auto Forward = seq(0, 10);
|
|
EXPECT_EQ(std::distance(Forward.begin(), Forward.end()), 10);
|
|
EXPECT_EQ(std::distance(Forward.rbegin(), Forward.rend()), 10);
|
|
}
|
|
|
|
TEST(SequenceTest, Dereference) {
|
|
const auto Forward = seq(0, 10).begin();
|
|
EXPECT_EQ(Forward[0], 0);
|
|
EXPECT_EQ(Forward[2], 2);
|
|
const auto Backward = seq(0, 10).rbegin();
|
|
EXPECT_EQ(Backward[0], 9);
|
|
EXPECT_EQ(Backward[2], 7);
|
|
}
|
|
|
|
enum UntypedEnum { A = 3 };
|
|
enum TypedEnum : uint32_t { B = 3 };
|
|
|
|
namespace X {
|
|
enum class ScopedEnum : uint16_t { C = 3 };
|
|
} // namespace X
|
|
|
|
struct S {
|
|
enum NestedEnum { D = 4 };
|
|
enum NestedEnum2 { E = 5 };
|
|
|
|
private:
|
|
enum NestedEnum3 { F = 6 };
|
|
friend struct llvm::enum_iteration_traits<NestedEnum3>;
|
|
|
|
public:
|
|
static auto getNestedEnum3() { return NestedEnum3::F; }
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace llvm {
|
|
|
|
template <> struct enum_iteration_traits<UntypedEnum> {
|
|
static constexpr bool is_iterable = true;
|
|
};
|
|
|
|
template <> struct enum_iteration_traits<TypedEnum> {
|
|
static constexpr bool is_iterable = true;
|
|
};
|
|
|
|
template <> struct enum_iteration_traits<X::ScopedEnum> {
|
|
static constexpr bool is_iterable = true;
|
|
};
|
|
|
|
template <> struct enum_iteration_traits<S::NestedEnum> {
|
|
static constexpr bool is_iterable = true;
|
|
};
|
|
|
|
template <> struct enum_iteration_traits<S::NestedEnum3> {
|
|
static constexpr bool is_iterable = true;
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
namespace {
|
|
|
|
TEST(StrongIntTest, Enums) {
|
|
EXPECT_EQ(CheckedInt::from(A).to<UntypedEnum>(), A);
|
|
EXPECT_EQ(CheckedInt::from(B).to<TypedEnum>(), B);
|
|
EXPECT_EQ(CheckedInt::from(X::ScopedEnum::C).to<X::ScopedEnum>(),
|
|
X::ScopedEnum::C);
|
|
}
|
|
|
|
TEST(SequenceTest, IterableEnums) {
|
|
EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A), IsEmpty());
|
|
EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A),
|
|
ElementsAre(UntypedEnum::A));
|
|
|
|
EXPECT_THAT(enum_seq(TypedEnum::B, TypedEnum::B), IsEmpty());
|
|
EXPECT_THAT(enum_seq_inclusive(TypedEnum::B, TypedEnum::B),
|
|
ElementsAre(TypedEnum::B));
|
|
|
|
EXPECT_THAT(enum_seq(X::ScopedEnum::C, X::ScopedEnum::C), IsEmpty());
|
|
EXPECT_THAT(enum_seq_inclusive(X::ScopedEnum::C, X::ScopedEnum::C),
|
|
ElementsAre(X::ScopedEnum::C));
|
|
|
|
EXPECT_THAT(enum_seq_inclusive(S::NestedEnum::D, S::NestedEnum::D),
|
|
ElementsAre(S::NestedEnum::D));
|
|
EXPECT_THAT(enum_seq_inclusive(S::getNestedEnum3(), S::getNestedEnum3()),
|
|
ElementsAre(S::getNestedEnum3()));
|
|
}
|
|
|
|
TEST(SequenceTest, NonIterableEnums) {
|
|
EXPECT_THAT(enum_seq(S::NestedEnum2::E, S::NestedEnum2::E,
|
|
force_iteration_on_noniterable_enum),
|
|
IsEmpty());
|
|
EXPECT_THAT(enum_seq_inclusive(S::NestedEnum2::E, S::NestedEnum2::E,
|
|
force_iteration_on_noniterable_enum),
|
|
ElementsAre(S::NestedEnum2::E));
|
|
|
|
// Check that this also works with enums marked as iterable.
|
|
EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A,
|
|
force_iteration_on_noniterable_enum),
|
|
IsEmpty());
|
|
EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A,
|
|
force_iteration_on_noniterable_enum),
|
|
ElementsAre(UntypedEnum::A));
|
|
}
|
|
|
|
} // namespace
|