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,39 @@ 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)) { - if (SSE->getOpcode() == BO_Sub) { - QualType T = Sym->getType(); + // Do not negate if the type cannot be meaningfully negated. + if (!Sym->getType()->isUnsignedIntegerOrEnumerationType() && + !Sym->getType()->isSignedIntegerOrEnumerationType()) + return llvm::None; - // Do not negate unsigned ranges - if (!T->isUnsignedIntegerOrEnumerationType() && - !T->isSignedIntegerOrEnumerationType()) - return llvm::None; + const RangeSet *NegatedRange = nullptr; + if (const UnarySymExpr *USE = dyn_cast(Sym)) { + if (USE->getOpcode() == UO_Minus) { + // Just get the operand when we negate a symbol that is already negated. + // -(-a) == a, ~(~a) == a + SymbolRef NegatedSym = USE->getOperand(); + NegatedRange = getConstraint(State, NegatedSym); + } + } else if (const SymSymExpr *SSE = dyn_cast(Sym)) { + if (SSE->getOpcode() == BO_Sub) { + QualType T = Sym->getType(); 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 { + SymbolManager &SymMgr = State->getSymbolManager(); + 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,120 @@ +// 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 __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 negate_positive_range(int a) { + if (-a <= 0) + return; + // -a: [1, INT_MAX] + // a: [INT_MIN, -1] + clang_analyzer_eval(a < 0); // expected-warning{{TRUE}} + clang_analyzer_eval(a >= INT_MIN); // expected-warning{{TRUE}} + clang_analyzer_eval(a == INT_MIN); // expected-warning{{FALSE}} +} + +void negate_positive_range2(int a) { + if (a <= 0) + return; + // a: [1, INT_MAX] + // -a: [INT_MIN, -1] + clang_analyzer_eval(-a < 0); // expected-warning{{TRUE}} + clang_analyzer_eval(-a >= INT_MIN); // expected-warning{{TRUE}} + clang_analyzer_eval(-a == INT_MIN); // expected-warning{{FALSE}} +} + +_Static_assert(INT_MIN == -INT_MIN, ""); +void negate_int_min(int a) { + if (a != INT_MIN) + return; + clang_analyzer_eval(-a == INT_MIN); // expected-warning{{TRUE}} +} + +void negate_mixed(int a) { + if (INT_MIN < a && a <= 0) + return; + 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) { + if (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, ""); +void negate_unsigned_4(unsigned a) { + if (a == 4u) { + clang_analyzer_eval(-a == 4u); // expected-warning{{FALSE}} + clang_analyzer_eval(-a != 4u); // expected-warning{{TRUE}} + } +} + +_Static_assert(UINT_MID == -UINT_MID, ""); +void negate_unsigned_mid(unsigned a) { + if (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) { + if (a < UINT_MID && a > UINT_MIN) { + clang_analyzer_eval(-a > UINT_MID); // expected-warning{{TRUE}} + clang_analyzer_eval(-a < UINT_MID); // expected-warning{{FALSE}} + } +} + +_Static_assert(1u - 2u == UINT_MAX, ""); +_Static_assert(2u - 1u == 1, ""); +void negate_unsigned_max(unsigned a) { + if (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) { + if (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); @@ -87,6 +89,7 @@ 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; @@ -106,11 +109,11 @@ 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) { @@ -122,6 +125,15 @@ } } +_Static_assert(7u - 3u != 3u - 7u, ""); +void negate_unsigned_4(unsigned m, unsigned n) { + if (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}} @@ -136,6 +148,8 @@ } } +_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}} @@ -152,8 +166,8 @@ // 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}} }