Denys Petrov ba92b27422 [analyzer] Improved RangeSet::Negate support of unsigned ranges
Summary:
This fixes https://bugs.llvm.org/show_bug.cgi?id=41588
RangeSet Negate function shall handle unsigned ranges as well as signed ones.
RangeSet getRangeForMinusSymbol function shall use wider variety of ranges, not only concrete value ranges.
RangeSet Intersect functions shall not produce assertions.

Changes:
Improved safety of RangeSet::Intersect function. Added isEmpty() check to prevent an assertion.
Added support of handling unsigned ranges to RangeSet::Negate and RangeSet::getRangeForMinusSymbol.
Extended RangeSet::getRangeForMinusSymbol to return not only range sets with single value [n,n], but with wide ranges [n,m].
Added unit test for Negate function.
Added regression tests for unsigned values.

Differential Revision: https://reviews.llvm.org/D77802
2020-05-25 18:52:22 +03:00

131 lines
4.5 KiB
C++

//===- 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 <typename T> struct TestCase {
RangeSet original;
RangeSet expected;
TestCase(BasicValueFactory &BVF, RangeSet::Factory &F,
const std::initializer_list<T> &originalList,
const std::initializer_list<T> &expectedList)
: original(createRangeSetFromList(BVF, F, originalList)),
expected(createRangeSetFromList(BVF, F, expectedList)) {}
private:
RangeSet createRangeSetFromList(BasicValueFactory &BVF, RangeSet::Factory &F,
const std::initializer_list<T> rangeList) {
llvm::APSInt from(sizeof(T) * 8, std::is_unsigned<T>::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<ASTUnit> AST = tooling::buildASTFromCode("struct foo;");
ASTContext &context = AST->getASTContext();
llvm::BumpPtrAllocator alloc;
BasicValueFactory BVF{context, alloc};
RangeSet::Factory F;
// End init block
template <typename T> 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<type>::min();
constexpr type MAX = std::numeric_limits<type>::max();
constexpr type MID = std::is_signed<type>::value
? 0
: ~(static_cast<type>(-1) / static_cast<type>(2));
constexpr type A = MID - static_cast<type>(42 + 42);
constexpr type B = MID - static_cast<type>(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<type> 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<int8_t>();
checkNegate<uint8_t>();
checkNegate<int16_t>();
checkNegate<uint16_t>();
checkNegate<int32_t>();
checkNegate<uint32_t>();
checkNegate<int64_t>();
checkNegate<uint64_t>();
}
} // namespace
} // namespace ento
} // namespace clang