diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1443,30 +1443,35 @@ return RangeFactory.deletePoint(Domain, IntType.getZeroValue()); } - // FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to - // obtain the negated symbolic expression instead of constructing the - // symbol manually. This will allow us to support finding ranges of not - // only negated SymSymExpr-type expressions, but also of other, simpler - // expressions which we currently do not know how to negate. Optional getRangeForNegatedSub(SymbolRef Sym) { - if (const SymSymExpr *SSE = dyn_cast(Sym)) { + // Do not negate if the type cannot be meaningfully negated. + if (!Sym->getType()->isUnsignedIntegerOrEnumerationType() && + !Sym->getType()->isSignedIntegerOrEnumerationType()) + return llvm::None; + + const RangeSet *NegatedRange = nullptr; + SymbolManager &SymMgr = State->getSymbolManager(); + if (const auto *USE = dyn_cast(Sym)) { + if (USE->getOpcode() == UO_Minus) { + // Just get the operand when we negate a symbol that is already negated. + // -(-a) == a + NegatedRange = getConstraint(State, USE->getOperand()); + } + } else if (const SymSymExpr *SSE = dyn_cast(Sym)) { if (SSE->getOpcode() == BO_Sub) { QualType T = Sym->getType(); - - // Do not negate unsigned ranges - if (!T->isUnsignedIntegerOrEnumerationType() && - !T->isSignedIntegerOrEnumerationType()) - return llvm::None; - - SymbolManager &SymMgr = State->getSymbolManager(); SymbolRef NegatedSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T); - - if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) { - return RangeFactory.negate(*NegatedRange); - } + NegatedRange = getConstraint(State, NegatedSym); } + } else { + SymbolRef NegatedSym = + SymMgr.getUnarySymExpr(Sym, UO_Minus, Sym->getType()); + NegatedRange = getConstraint(State, NegatedSym); } + + if (NegatedRange) + return RangeFactory.negate(*NegatedRange); return llvm::None; } diff --git a/clang/test/Analysis/constraint_manager_negate.c b/clang/test/Analysis/constraint_manager_negate.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/constraint_manager_negate.c @@ -0,0 +1,113 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection,core.builtin \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -verify %s + +void clang_analyzer_eval(int); +void clang_analyzer_dump(int); + +void exit(int); + +#define UINT_MIN (0U) +#define UINT_MAX (~UINT_MIN) +#define UINT_MID (UINT_MAX / 2 + 1) +#define INT_MAX (UINT_MAX & (UINT_MAX >> 1)) +#define INT_MIN (UINT_MAX & ~(UINT_MAX >> 1)) + +extern void abort() __attribute__((__noreturn__)); +#define assert(expr) ((expr) ? (void)(0) : abort()) + +void negate_positive_range(int a) { + assert(-a > 0); + // -a: [1, INT_MAX] + // a: [INT_MIN + 1, -1] + clang_analyzer_eval(a < 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a > INT_MIN); // expected-warning{{TRUE}} +} + +void negate_positive_range2(int a) { + assert(a > 0); + // a: [1, INT_MAX] + // -a: [INT_MIN + 1, -1] + clang_analyzer_eval(-a < 0); // expected-warning{{TRUE}} + clang_analyzer_eval(-a > INT_MIN); // expected-warning{{TRUE}} +} + +// INT_MIN equals 0b100...00. +// Its two's compelement is 0b011...11 + 1 = 0b100...00 (itself). +_Static_assert(INT_MIN == -INT_MIN, ""); +void negate_int_min(int a) { + assert(a == INT_MIN); + clang_analyzer_eval(-a == INT_MIN); // expected-warning{{TRUE}} +} + +void negate_mixed(int a) { + assert(a > 0 || a == INT_MIN); + clang_analyzer_eval(-a <= 0); // expected-warning{{TRUE}} +} + +void effective_range(int a) { + assert(a >= 0); + assert(-a >= 0); + clang_analyzer_eval(a == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(-a == 0); // expected-warning{{TRUE}} +} + +_Static_assert(-INT_MIN == INT_MIN, ""); +void effective_range_2(int a) { + assert(a <= 0); + assert(-a <= 0); + clang_analyzer_eval(a == 0 || a == INT_MIN); // expected-warning{{TRUE}} +} + +void negate_symexpr(int a, int b) { + assert(3 <= a * b && a * b <= 5); + clang_analyzer_eval(-(a * b) >= -5); // expected-warning{{TRUE}} + clang_analyzer_eval(-(a * b) <= -3); // expected-warning{{TRUE}} +} + +void negate_unsigned_min(unsigned a) { + assert(a == UINT_MIN); + clang_analyzer_eval(-a == UINT_MIN); // expected-warning{{TRUE}} + clang_analyzer_eval(-a != UINT_MIN); // expected-warning{{FALSE}} + clang_analyzer_eval(-a > UINT_MIN); // expected-warning{{FALSE}} + clang_analyzer_eval(-a < UINT_MIN); // expected-warning{{FALSE}} +} + +_Static_assert(7u - 3u != 3u - 7u, ""); +_Static_assert(-4u == UINT_MAX - 3u, ""); +void negate_unsigned_4(unsigned a) { + assert(a == 4u); + clang_analyzer_eval(-a == 4u); // expected-warning{{FALSE}} + clang_analyzer_eval(-a != 4u); // expected-warning{{TRUE}} + clang_analyzer_eval(-a == UINT_MAX - 3u); // expected-warning{{TRUE}} +} + +// UINT_MID equals 0b100...00. +// Its two's compelement is 0b011...11 + 1 = 0b100...00 (itself). +_Static_assert(UINT_MID == -UINT_MID, ""); +void negate_unsigned_mid(unsigned a) { + assert(a == UINT_MID); + clang_analyzer_eval(-a == UINT_MID); // expected-warning{{TRUE}} + clang_analyzer_eval(-a != UINT_MID); // expected-warning{{FALSE}} +} + +void negate_unsigned_mid2(unsigned a) { + assert(UINT_MIN < a && a < UINT_MID); + // a: [UINT_MIN+1, UINT_MID-1] + // -a: [UINT_MID+1, UINT_MAX] + clang_analyzer_eval(-a > UINT_MID); // expected-warning{{TRUE}} + clang_analyzer_eval(-a <= UINT_MAX); // expected-warning{{TRUE}} +} + +_Static_assert(1u - 2u == UINT_MAX, ""); +_Static_assert(2u - 1u == 1, ""); +void negate_unsigned_max(unsigned a) { + assert(a == UINT_MAX); + clang_analyzer_eval(-a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(-a != 1); // expected-warning{{FALSE}} +} +void negate_unsigned_one(unsigned a) { + assert(a == 1); + clang_analyzer_eval(-a == UINT_MAX); // expected-warning{{TRUE}} + clang_analyzer_eval(-a < UINT_MAX); // expected-warning{{FALSE}} +} diff --git a/clang/test/Analysis/constraint_manager_negate_difference.c b/clang/test/Analysis/constraint_manager_negate_difference.c --- a/clang/test/Analysis/constraint_manager_negate_difference.c +++ b/clang/test/Analysis/constraint_manager_negate_difference.c @@ -1,4 +1,6 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection,core.builtin -analyzer-config aggressive-binary-operation-simplification=true -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection,core.builtin \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -verify %s void clang_analyzer_eval(int); @@ -10,11 +12,8 @@ #define INT_MAX (UINT_MAX & (UINT_MAX >> 1)) #define INT_MIN (UINT_MAX & ~(UINT_MAX >> 1)) -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__)) +extern void abort() __attribute__((__noreturn__)); +#define assert(expr) ((expr) ? (void)(0) : abort()) void assert_in_range(int x) { assert(x <= ((int)INT_MAX / 4)); @@ -33,69 +32,60 @@ void equal(int m, int n) { assert_in_range_2(m, n); - if (m != n) - return; + assert(m == n); assert_in_wide_range(m - n); clang_analyzer_eval(n == m); // expected-warning{{TRUE}} } void non_equal(int m, int n) { assert_in_range_2(m, n); - if (m == n) - return; + assert(m != n); assert_in_wide_range(m - n); clang_analyzer_eval(n != m); // expected-warning{{TRUE}} } void less_or_equal(int m, int n) { assert_in_range_2(m, n); - if (m < n) - return; + assert(m >= n); assert_in_wide_range(m - n); clang_analyzer_eval(n <= m); // expected-warning{{TRUE}} } void less(int m, int n) { assert_in_range_2(m, n); - if (m <= n) - return; + assert(m > n); assert_in_wide_range(m - n); clang_analyzer_eval(n < m); // expected-warning{{TRUE}} } void greater_or_equal(int m, int n) { assert_in_range_2(m, n); - if (m > n) - return; + assert(m <= n); assert_in_wide_range(m - n); clang_analyzer_eval(n >= m); // expected-warning{{TRUE}} } void greater(int m, int n) { assert_in_range_2(m, n); - if (m >= n) - return; + assert(m < n); assert_in_wide_range(m - n); clang_analyzer_eval(n > m); // expected-warning{{TRUE}} } void negate_positive_range(int m, int n) { - if (m - n <= 0) - return; + assert(m - n > 0); clang_analyzer_eval(n - m < 0); // expected-warning{{TRUE}} clang_analyzer_eval(n - m > INT_MIN); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m == INT_MIN); // expected-warning{{FALSE}} } +_Static_assert(INT_MIN == -INT_MIN, ""); void negate_int_min(int m, int n) { - if (m - n != INT_MIN) - return; + assert(m - n == INT_MIN); clang_analyzer_eval(n - m == INT_MIN); // expected-warning{{TRUE}} } void negate_mixed(int m, int n) { - if (m -n > INT_MIN && m - n <= 0) - return; + assert(m - n > 0 || m - n == INT_MIN); clang_analyzer_eval(n - m <= 0); // expected-warning{{TRUE}} } @@ -106,54 +96,59 @@ clang_analyzer_eval(n - m == 0); // expected-warning{{TRUE}} } +_Static_assert(INT_MIN == -INT_MIN, ""); void effective_range_2(int m, int n) { assert(m - n <= 0); assert(n - m <= 0); - clang_analyzer_eval(m - n == 0); // expected-warning{{TRUE}} expected-warning{{FALSE}} - clang_analyzer_eval(n - m == 0); // expected-warning{{TRUE}} expected-warning{{FALSE}} + clang_analyzer_eval(m - n == 0 || m - n == INT_MIN); // expected-warning{{TRUE}} } void negate_unsigned_min(unsigned m, unsigned n) { - if (m - n == UINT_MIN) { - clang_analyzer_eval(n - m == UINT_MIN); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m != UINT_MIN); // expected-warning{{FALSE}} - clang_analyzer_eval(n - m > UINT_MIN); // expected-warning{{FALSE}} - clang_analyzer_eval(n - m < UINT_MIN); // expected-warning{{FALSE}} - } + assert(m - n == UINT_MIN); + clang_analyzer_eval(n - m == UINT_MIN); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m != UINT_MIN); // expected-warning{{FALSE}} + clang_analyzer_eval(n - m > UINT_MIN); // expected-warning{{FALSE}} + clang_analyzer_eval(n - m < UINT_MIN); // expected-warning{{FALSE}} } +_Static_assert(7u - 3u != 3u - 7u, ""); +void negate_unsigned_4(unsigned m, unsigned n) { + assert(m - n == 4u); + clang_analyzer_eval(n - m == 4u); // expected-warning{{FALSE}} + clang_analyzer_eval(n - m != 4u); // expected-warning{{TRUE}} +} + +_Static_assert(UINT_MID == -UINT_MID, ""); void negate_unsigned_mid(unsigned m, unsigned n) { - if (m - n == UINT_MID) { - clang_analyzer_eval(n - m == UINT_MID); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m != UINT_MID); // expected-warning{{FALSE}} - } + assert(m - n == UINT_MID); + clang_analyzer_eval(n - m == UINT_MID); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m != UINT_MID); // expected-warning{{FALSE}} } void negate_unsigned_mid2(unsigned m, unsigned n) { - if (m - n < UINT_MID && m - n > UINT_MIN) { - clang_analyzer_eval(n - m > UINT_MID); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m < UINT_MID); // expected-warning{{FALSE}} - } + assert(UINT_MIN < m - n && m - n < UINT_MID); + clang_analyzer_eval(n - m > UINT_MID); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m <= UINT_MAX); // expected-warning{{TRUE}} } +_Static_assert(1u - 2u == UINT_MAX, ""); +_Static_assert(2u - 1u == 1, ""); void negate_unsigned_max(unsigned m, unsigned n) { - if (m - n == UINT_MAX) { - clang_analyzer_eval(n - m == 1); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m != 1); // expected-warning{{FALSE}} - } + assert(m - n == UINT_MAX); + clang_analyzer_eval(n - m == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m != 1); // expected-warning{{FALSE}} } void negate_unsigned_one(unsigned m, unsigned n) { - if (m - n == 1) { - clang_analyzer_eval(n - m == UINT_MAX); // expected-warning{{TRUE}} - clang_analyzer_eval(n - m < UINT_MAX); // expected-warning{{FALSE}} - } + assert(m - n == 1); + clang_analyzer_eval(n - m == UINT_MAX); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m < UINT_MAX); // expected-warning{{FALSE}} } // The next code is a repro for the bug PR41588 void negated_unsigned_range(unsigned x, unsigned y) { - clang_analyzer_eval(x - y != 0); // expected-warning{{FALSE}} expected-warning{{TRUE}} - clang_analyzer_eval(y - x != 0); // expected-warning{{FALSE}} expected-warning{{TRUE}} + clang_analyzer_eval(x - y != 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(y - x != 0); // expected-warning{{UNKNOWN}} // expected no assertion on the next line - clang_analyzer_eval(x - y != 0); // expected-warning{{FALSE}} expected-warning{{TRUE}} + clang_analyzer_eval(x - y != 0); // expected-warning{{UNKNOWN}} } diff --git a/clang/test/Analysis/unary-sym-expr.c b/clang/test/Analysis/unary-sym-expr.c --- a/clang/test/Analysis/unary-sym-expr.c +++ b/clang/test/Analysis/unary-sym-expr.c @@ -28,3 +28,12 @@ return; clang_analyzer_eval(-(x + y) == -3); // expected-warning{{TRUE}} } + +int test_fp(int flag) { + int value; + if (flag == 0) + value = 1; + if (-flag == 0) + return value; // no-warning + return 42; +}