//===- unittests/StaticAnalyzer/RangeSetTest.cpp ----------------------===// // // 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 "clang/Basic/Builtins.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" #include "clang/Tooling/Tooling.h" #include "gtest/gtest.h" namespace clang { namespace ento { namespace { // TestCase contains to lists of ranges. // Original one has to be negated. // Expected one has to be compared to negated original range. template struct TestCase { RangeSet original; RangeSet expected; TestCase(BasicValueFactory &BVF, RangeSet::Factory &F, const std::initializer_list &originalList, const std::initializer_list &expectedList) : original(createRangeSetFromList(BVF, F, originalList)), expected(createRangeSetFromList(BVF, F, expectedList)) {} private: RangeSet createRangeSetFromList(BasicValueFactory &BVF, RangeSet::Factory &F, const std::initializer_list rangeList) { llvm::APSInt from(sizeof(T) * 8, std::is_unsigned::value); llvm::APSInt to = from; RangeSet rangeSet = F.getEmptySet(); for (auto it = rangeList.begin(); it != rangeList.end(); it += 2) { from = *it; to = *(it + 1); rangeSet = rangeSet.addRange( F, RangeSet(F, BVF.getValue(from), BVF.getValue(to))); } return rangeSet; } void printNegate(const TestCase &TestCase) { TestCase.original.print(llvm::dbgs()); llvm::dbgs() << " => "; TestCase.expected.print(llvm::dbgs()); } }; class RangeSetTest : public testing::Test { protected: // Init block std::unique_ptr AST = tooling::buildASTFromCode("struct foo;"); ASTContext &context = AST->getASTContext(); llvm::BumpPtrAllocator alloc; BasicValueFactory BVF{context, alloc}; RangeSet::Factory F; // End init block template void checkNegate() { using type = T; // Use next values of the range {MIN, A, B, MID, C, D, MAX}. // MID is a value in the middle of the range // which unary minus does not affect on, // e.g. int8/int32(0), uint8(128), uint32(2147483648). constexpr type MIN = std::numeric_limits::min(); constexpr type MAX = std::numeric_limits::max(); constexpr type MID = std::is_signed::value ? 0 : ~(static_cast(-1) / static_cast(2)); constexpr type A = MID - static_cast(42 + 42); constexpr type B = MID - static_cast(42); constexpr type C = -B; constexpr type D = -A; static_assert(MIN < A && A < B && B < MID && MID < C && C < D && D < MAX, "Values shall be in an ascending order"); // Left {[x, y], [x, y]} is what shall be negated. // Right {[x, y], [x, y]} is what shall be compared to a negation result. TestCase cases[] = { {BVF, F, {MIN, A}, {MIN, MIN, D, MAX}}, {BVF, F, {MIN, C}, {MIN, MIN, B, MAX}}, {BVF, F, {MIN, MID}, {MIN, MIN, MID, MAX}}, {BVF, F, {MIN, MAX}, {MIN, MAX}}, {BVF, F, {A, D}, {A, D}}, {BVF, F, {A, B}, {C, D}}, {BVF, F, {MIN, A, D, MAX}, {MIN, A, D, MAX}}, {BVF, F, {MIN, B, MID, D}, {MIN, MIN, A, MID, C, MAX}}, {BVF, F, {MIN, MID, C, D}, {MIN, MIN, A, B, MID, MAX}}, {BVF, F, {MIN, MID, C, MAX}, {MIN, B, MID, MAX}}, {BVF, F, {A, MID, D, MAX}, {MIN + 1, A, MID, D}}, {BVF, F, {A, A}, {D, D}}, {BVF, F, {MID, MID}, {MID, MID}}, {BVF, F, {MAX, MAX}, {MIN + 1, MIN + 1}}, }; for (const auto &c : cases) { // Negate original and check with expected. RangeSet negatedFromOriginal = c.original.Negate(BVF, F); EXPECT_EQ(negatedFromOriginal, c.expected); // Negate negated back and check with original. RangeSet negatedBackward = negatedFromOriginal.Negate(BVF, F); EXPECT_EQ(negatedBackward, c.original); } } }; TEST_F(RangeSetTest, RangeSetNegateTest) { checkNegate(); checkNegate(); checkNegate(); checkNegate(); checkNegate(); checkNegate(); checkNegate(); checkNegate(); } } // namespace } // namespace ento } // namespace clang