martong updated this revision to Diff 428683.
martong added a comment.

- Add false positive test


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D125395/new/

https://reviews.llvm.org/D125395

Files:
  clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  clang/test/Analysis/constraint_manager_negate.c
  clang/test/Analysis/constraint_manager_negate_difference.c
  clang/test/Analysis/unary-sym-expr.c

Index: clang/test/Analysis/unary-sym-expr.c
===================================================================
--- clang/test/Analysis/unary-sym-expr.c
+++ 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;
+}
Index: clang/test/Analysis/constraint_manager_negate_difference.c
===================================================================
--- clang/test/Analysis/constraint_manager_negate_difference.c
+++ 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}}
 }
Index: clang/test/Analysis/constraint_manager_negate.c
===================================================================
--- /dev/null
+++ 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}}
+  }
+}
Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ 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<RangeSet> getRangeForNegatedSub(SymbolRef Sym) {
-    if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(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<UnarySymExpr>(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<SymSymExpr>(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;
   }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to