543 changes: 457 additions & 86 deletions clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
if (LHSValue == 0)
return evalCastFromNonLoc(lhs, resultTy);
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
case BO_Rem:
// 0 % x == 0
if (LHSValue == 0)
return makeZeroVal(resultTy);
LLVM_FALLTHROUGH;
default:
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
Expand Down
48 changes: 15 additions & 33 deletions clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,45 +34,27 @@ using namespace ento;

void SymExpr::anchor() {}

LLVM_DUMP_METHOD void SymExpr::dump() const {
dumpToStream(llvm::errs());
}
LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }

void SymIntExpr::dumpToStream(raw_ostream &os) const {
os << '(';
getLHS()->dumpToStream(os);
os << ") "
<< BinaryOperator::getOpcodeStr(getOpcode()) << ' ';
if (getRHS().isUnsigned())
os << getRHS().getZExtValue();
else
os << getRHS().getSExtValue();
if (getRHS().isUnsigned())
os << 'U';
void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
OS << '(';
Sym->dumpToStream(OS);
OS << ')';
}

void IntSymExpr::dumpToStream(raw_ostream &os) const {
if (getLHS().isUnsigned())
os << getLHS().getZExtValue();
void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
const llvm::APSInt &Value) {
if (Value.isUnsigned())
OS << Value.getZExtValue();
else
os << getLHS().getSExtValue();
if (getLHS().isUnsigned())
os << 'U';
os << ' '
<< BinaryOperator::getOpcodeStr(getOpcode())
<< " (";
getRHS()->dumpToStream(os);
os << ')';
OS << Value.getSExtValue();
if (Value.isUnsigned())
OS << 'U';
}

void SymSymExpr::dumpToStream(raw_ostream &os) const {
os << '(';
getLHS()->dumpToStream(os);
os << ") "
<< BinaryOperator::getOpcodeStr(getOpcode())
<< " (";
getRHS()->dumpToStream(os);
os << ')';
void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
BinaryOperator::Opcode Op) {
OS << ' ' << BinaryOperator::getOpcodeStr(Op) << ' ';
}

void SymbolCast::dumpToStream(raw_ostream &os) const {
Expand Down
28 changes: 28 additions & 0 deletions clang/test/Analysis/PR35418.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s

// expected-no-diagnostics

void halt() __attribute__((__noreturn__));
void assert(int b) {
if (!b)
halt();
}

void decode(unsigned width) {
assert(width > 0);

int base;
bool inited = false;

int i = 0;

if (i % width == 0) {
base = 512;
inited = true;
}

base += 1; // no-warning

if (base >> 10)
assert(false);
}
172 changes: 152 additions & 20 deletions clang/test/Analysis/constant-folding.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s

#define UINT_MAX (~0U)
#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1))
#define INT_MIN (int)(UINT_MAX & ~(UINT_MAX >> 1))

void clang_analyzer_eval(int);

// There should be no warnings unless otherwise indicated.
Expand Down Expand Up @@ -77,45 +81,173 @@ void testMixedTypeComparisons (char a, unsigned long b) {
clang_analyzer_eval(a != b); // expected-warning{{TRUE}}
}

void testBitwiseRules(unsigned int a, int b) {
clang_analyzer_eval((a | 1) >= 1); // expected-warning{{TRUE}}
void testBitwiseRules(unsigned int a, int b, int c) {
clang_analyzer_eval((a | 1) >= 1); // expected-warning{{TRUE}}
clang_analyzer_eval((a | -1) >= -1); // expected-warning{{TRUE}}
clang_analyzer_eval((a | 2) >= 2); // expected-warning{{TRUE}}
clang_analyzer_eval((a | 5) >= 5); // expected-warning{{TRUE}}
clang_analyzer_eval((a | 2) >= 2); // expected-warning{{TRUE}}
clang_analyzer_eval((a | 5) >= 5); // expected-warning{{TRUE}}
clang_analyzer_eval((a | 10) >= 10); // expected-warning{{TRUE}}

// Argument order should not influence this
clang_analyzer_eval((1 | a) >= 1); // expected-warning{{TRUE}}

clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 2) <= 2); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 5) <= 5); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 10) <= 10); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 1) >= 0); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 2) <= 2); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 5) <= 5); // expected-warning{{TRUE}}
clang_analyzer_eval((a & 10) <= 10); // expected-warning{{TRUE}}
clang_analyzer_eval((a & -10) <= 10); // expected-warning{{UNKNOWN}}

// Again, check for different argument order.
clang_analyzer_eval((1 & a) <= 1); // expected-warning{{TRUE}}

unsigned int c = a;
c |= 1;
clang_analyzer_eval((c | 0) == 0); // expected-warning{{FALSE}}
unsigned int d = a;
d |= 1;
clang_analyzer_eval((d | 0) == 0); // expected-warning{{FALSE}}

// Rules don't apply to signed typed, as the values might be negative.
clang_analyzer_eval((b | 1) > 0); // expected-warning{{UNKNOWN}}

// Even for signed values, bitwise OR with a non-zero is always non-zero.
clang_analyzer_eval((b | 1) == 0); // expected-warning{{FALSE}}
clang_analyzer_eval((b | 1) == 0); // expected-warning{{FALSE}}
clang_analyzer_eval((b | -2) == 0); // expected-warning{{FALSE}}
clang_analyzer_eval((b | 10) == 0); // expected-warning{{FALSE}}
clang_analyzer_eval((b | 0) == 0); // expected-warning{{UNKNOWN}}
#ifdef ANALYZER_CM_Z3
clang_analyzer_eval((b | 0) == 0); // expected-warning{{UNKNOWN}}
clang_analyzer_eval((b | -2) >= 0); // expected-warning{{FALSE}}
#else
clang_analyzer_eval((b | -2) >= 0); // expected-warning{{UNKNOWN}}
#endif

// Check that we can operate with negative ranges
if (b < 0) {
clang_analyzer_eval((b | -1) == -1); // expected-warning{{TRUE}}
clang_analyzer_eval((b | -10) >= -10); // expected-warning{{TRUE}}
clang_analyzer_eval((b & 0) == 0); // expected-warning{{TRUE}}
clang_analyzer_eval((b & -10) <= -10); // expected-warning{{TRUE}}
clang_analyzer_eval((b & 5) >= 0); // expected-warning{{TRUE}}

int e = (b | -5);
clang_analyzer_eval(e >= -5 && e <= -1); // expected-warning{{TRUE}}

if (b < -20) {
clang_analyzer_eval((b | e) >= -5); // expected-warning{{TRUE}}
clang_analyzer_eval((b & -10) < -20); // expected-warning{{TRUE}}
clang_analyzer_eval((b & e) < -20); // expected-warning{{TRUE}}
clang_analyzer_eval((b & -30) <= -30); // expected-warning{{TRUE}}

if (c >= -30 && c <= -10) {
clang_analyzer_eval((b & c) <= -20); // expected-warning{{TRUE}}
}
}

if (a <= 40) {
int g = (int)a & b;
clang_analyzer_eval(g <= 40 && g >= 0); // expected-warning{{TRUE}}
}

// Check that we can reason about the result even if know nothing
// about one of the operands.
clang_analyzer_eval((b | c) != 0); // expected-warning{{TRUE}}
}

if (a <= 30 && b >= 10 && c >= 20) {
// Check that we can reason about non-constant operands.
clang_analyzer_eval((b | c) >= 20); // expected-warning{{TRUE}}

// Check that we can reason about the resulting range even if
// the types are not the same, but we still can convert operand
// ranges.
clang_analyzer_eval((a | b) >= 10); // expected-warning{{TRUE}}
clang_analyzer_eval((a & b) <= 30); // expected-warning{{TRUE}}

if (b <= 20) {
clang_analyzer_eval((a & b) <= 20); // expected-warning{{TRUE}}
}
}

// Check that dynamically computed constants also work.
int constant = 1 << 3;
unsigned int d = a | constant;
clang_analyzer_eval(constant > 0); // expected-warning{{TRUE}}
unsigned int constant = 1 << 3;
unsigned int f = a | constant;
clang_analyzer_eval(f >= constant); // expected-warning{{TRUE}}

// Check that nested expressions also work.
clang_analyzer_eval(((a | 10) | 5) >= 10); // expected-warning{{TRUE}}

if (a < 10) {
clang_analyzer_eval((a | 20) >= 20); // expected-warning{{TRUE}}
}

if (a > 10) {
clang_analyzer_eval((a & 1) <= 1); // expected-warning{{TRUE}}
}
}

void testRemainderRules(unsigned int a, unsigned int b, int c, int d) {
// Check that we know that remainder of zero divided by any number is still 0.
clang_analyzer_eval((0 % c) == 0); // expected-warning{{TRUE}}

clang_analyzer_eval((10 % a) <= 10); // expected-warning{{TRUE}}

if (a <= 30 && b <= 50) {
clang_analyzer_eval((40 % a) < 30); // expected-warning{{TRUE}}
clang_analyzer_eval((a % b) < 50); // expected-warning{{TRUE}}
clang_analyzer_eval((b % a) < 30); // expected-warning{{TRUE}}

if (a >= 10) {
// Even though it seems like a valid assumption, it is not.
// Check that we are not making this mistake.
clang_analyzer_eval((a % b) >= 10); // expected-warning{{UNKNOWN}}

// Check that we can we can infer when remainder is equal
// to the dividend.
clang_analyzer_eval((4 % a) == 4); // expected-warning{{TRUE}}
if (b < 7) {
clang_analyzer_eval((b % a) < 7); // expected-warning{{TRUE}}
}
}
}

if (c > -10) {
clang_analyzer_eval((d % c) < INT_MAX); // expected-warning{{TRUE}}
clang_analyzer_eval((d % c) > INT_MIN + 1); // expected-warning{{TRUE}}
}

// Check that we can reason about signed integers when they are
// known to be positive.
if (c >= 10 && c <= 30 && d >= 20 && d <= 50) {
clang_analyzer_eval((5 % c) == 5); // expected-warning{{TRUE}}
clang_analyzer_eval((c % d) <= 30); // expected-warning{{TRUE}}
clang_analyzer_eval((c % d) >= 0); // expected-warning{{TRUE}}
clang_analyzer_eval((d % c) < 30); // expected-warning{{TRUE}}
clang_analyzer_eval((d % c) >= 0); // expected-warning{{TRUE}}
}

if (c >= -30 && c <= -10 && d >= -20 && d <= 50) {
// Test positive LHS with negative RHS.
clang_analyzer_eval((40 % c) < 30); // expected-warning{{TRUE}}
clang_analyzer_eval((40 % c) > -30); // expected-warning{{TRUE}}

// Test negative LHS with possibly negative RHS.
clang_analyzer_eval((-10 % d) < 50); // expected-warning{{TRUE}}
clang_analyzer_eval((-20 % d) > -50); // expected-warning{{TRUE}}

// Check that we don't make wrong assumptions
clang_analyzer_eval((-20 % d) > -20); // expected-warning{{UNKNOWN}}

// Check that we can reason about negative ranges...
clang_analyzer_eval((c % d) < 50); // expected-warning{{TRUE}}
/// ...both ways
clang_analyzer_eval((d % c) < 30); // expected-warning{{TRUE}}

if (a <= 10) {
// Result is unsigned. This means that 'c' is casted to unsigned.
// We don't want to reason about ranges changing boundaries with
// conversions.
clang_analyzer_eval((a % c) < 30); // expected-warning{{UNKNOWN}}
}
}

// Check that we work correctly when minimal unsigned value from a range is
// equal to the signed minimum for the same bit width.
unsigned int x = INT_MIN;
if (a >= x && a <= x + 10) {
clang_analyzer_eval((b % a) < x + 10); // expected-warning{{TRUE}}
}
}
22 changes: 22 additions & 0 deletions clang/test/Analysis/double-ranges-bug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %clang_analyze_cc1 -verify %s -analyzer-checker=core

// expected-no-diagnostics

typedef unsigned long int A;

extern int fill(A **values, int *nvalues);

void foo() {
A *values;
int nvalues;
fill(&values, &nvalues);

int i = 1;
double x, y;

y = values[i - 1];
x = values[i];

if (x <= y) {
}
}
196 changes: 193 additions & 3 deletions clang/test/Analysis/hangs.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
// RUN: %clang_analyze_cc1 -analyzer-checker core -verify %s

// expected-no-diagnostics
// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker core,debug.ExprInspection

// Stuff that used to hang.

extern void __assert_fail(__const char *__assertion, __const char *__file,
unsigned int __line, __const char *__function)
__attribute__((__noreturn__));
#define assert(expr) \
((expr) ? (void)(0) : __assert_fail(#expr, __FILE__, __LINE__, __func__))

void clang_analyzer_eval(int);

int g();

int f(int y) {
Expand All @@ -28,3 +35,186 @@ void produce_an_exponentially_exploding_symbol(int x, int y) {
x += y; y += x + g();
x += y; y += x + g();
}

void produce_an_exponentially_exploding_symbol_2(int x, int y) {
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
if (x > 1) {
if (x > 2) {
if (x > 3) {
if (x > 4) {
if (x > 5) {
if (x > 6) {
if (x > 7) {
if (x > 8) {
if (x > 9) {
if (x > 10) {
}
}
}
}
}
}
}
}
}
}
}

void produce_an_exponentially_exploding_symbol_3(int x, int y) {
assert(0 < x && x < 10);
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
x &= y;
y &= x & g();
clang_analyzer_eval(0 < x && x < 10); // expected-warning{{TRUE}}
// expected-warning@-1{{FALSE}}
}
11 changes: 11 additions & 0 deletions clang/test/Analysis/switch-case.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,14 @@ void testConstant() {
break;
}
}

void testExhaustiveSwitch(unsigned int a) {
switch (a & 5) {
case 0 ... 5:
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
break;
default:
clang_analyzer_warnIfReached(); // no-warning
break;
}
}
27 changes: 27 additions & 0 deletions clang/test/Analysis/uninit-bug-first-iteration-init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s

// rdar://problem/44978988
// expected-no-diagnostics

int foo();

int gTotal;

double bar(int start, int end) {
int i, cnt, processed, size;
double result, inc;

result = 0;
processed = start;
size = gTotal * 2;
cnt = (end - start + 1) * size;

for (i = 0; i < cnt; i += 2) {
if ((i % size) == 0) {
inc = foo();
processed++;
}
result += inc * inc; // no-warning
}
return result;
}
20 changes: 20 additions & 0 deletions clang/test/Analysis/uninit-exhaustive-switch-bug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s

// rdar://problem/54359410
// expected-no-diagnostics

int rand();

void test() {
int offset = 0;
int value;
int test = rand();
switch (test & 0x1) {
case 0:
case 1:
value = 0;
break;
}

offset += value; // no-warning
}