danielmarjamaki created this revision.

In the code below the division result should be a value between 5 and 25.

  if (a >= 10 && a <= 50) {
    int b = a / 2;
  }

This patch will calculate results for additions, subtractions and divisions.

I intentionally do not try to handle all possible cases that can be handled. I 
want to know if my approach is ok.


Repository:
  rL LLVM

https://reviews.llvm.org/D36471

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
  lib/StaticAnalyzer/Core/ExprEngineC.cpp
  lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
  test/Analysis/eval-range.c

Index: test/Analysis/eval-range.c
===================================================================
--- test/Analysis/eval-range.c
+++ test/Analysis/eval-range.c
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s
+
+void clang_analyzer_eval(int);
+
+void test1(int a) {
+  if (a >= 10 && a <= 50) {
+    int b;
+
+    b = a + 2;
+    clang_analyzer_eval(b >= 12 && b <= 52); // expected-warning{{TRUE}}
+
+    b = a - 2;
+    clang_analyzer_eval(b >= 8 && b <= 48); // expected-warning{{TRUE}}
+
+    b = a / 2;
+    clang_analyzer_eval(b >= 5 && b <= 25); // expected-warning{{TRUE}}
+  }
+}
Index: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -304,6 +304,8 @@
   void print(ProgramStateRef State, raw_ostream &Out, const char *nl,
              const char *sep) override;
 
+  ProgramStateRef evalRangeOp(ProgramStateRef state, SVal V) override;
+
   //===------------------------------------------------------------------===//
   // Implementation for interface from RangedConstraintManager.
   //===------------------------------------------------------------------===//
@@ -741,3 +743,65 @@
   }
   Out << nl;
 }
+
+ProgramStateRef RangeConstraintManager::evalRangeOp(ProgramStateRef St,
+                                                    SVal V) {
+  const SymExpr *SE = V.getAsSymExpr();
+  if (!SE)
+    return nullptr;
+
+  const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE);
+  if (!SIE)
+    return nullptr;
+
+  const clang::BinaryOperatorKind Opc = SIE->getOpcode();
+
+  if (Opc != BO_Add && Opc != BO_Sub && Opc != BO_Div)
+    return nullptr;
+
+  const SymExpr *LHS = SIE->getLHS();
+  const llvm::APSInt &RHS = SIE->getRHS();
+
+  if (RHS.isNegative())
+    // TODO: Handle negative values.
+    return nullptr;
+
+  ConstraintRangeTy Ranges = St->get<ConstraintRange>();
+  for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E;
+       ++I) {
+    if (LHS == I.getKey()) {
+      const auto D = I.getData();
+      for (auto I = D.begin(); I != D.end(); ++I) {
+        if (I->From().isUnsigned() != RHS.isUnsigned())
+          // TODO: Handle sign conversions.
+          return nullptr;
+        if (I->From().getBitWidth() != RHS.getBitWidth())
+          // TODO: Promote values.
+          return nullptr;
+        if (I->From().isNegative())
+          // TODO: Handle negative range values
+          return nullptr;
+        llvm::APSInt Lower;
+        llvm::APSInt Upper;
+        if (Opc == BO_Add) {
+          Lower = I->From() + RHS;
+          Upper = I->To() + RHS;
+        } else if (Opc == BO_Sub) {
+          if (RHS > I->From())
+            return nullptr;
+          Lower = I->From() - RHS;
+          Upper = I->To() - RHS;
+        } else if (Opc == BO_Div) {
+          Lower = I->From() / RHS;
+          Upper = I->To() / RHS;
+        }
+        SymbolRef Sym = V.getAsSymbol();
+        RangeSet RS =
+            getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+        // TODO: This only evaluates the first range. Evaluate all ranges.
+        return RS.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, RS);
+      }
+    }
+  }
+  return nullptr;
+}
Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -98,6 +98,14 @@
       }
 
       state = state->BindExpr(B, LCtx, Result);
+
+      {
+        ProgramStateRef St2 = getConstraintManager().evalRangeOp(state, Result);
+        if (St2) {
+          Bldr.generateNode(B, *it, St2);
+          continue;
+        }
+      }
       Bldr.generateNode(B, *it, state);
       continue;
     }
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h
@@ -142,13 +142,15 @@
   /// Scan all symbols referenced by the constraints. If the symbol is not
   /// alive, remove it.
   virtual ProgramStateRef removeDeadBindings(ProgramStateRef state,
-                                                 SymbolReaper& SymReaper) = 0;
+                                             SymbolReaper &SymReaper) = 0;
 
-  virtual void print(ProgramStateRef state,
-                     raw_ostream &Out,
-                     const char* nl,
+  virtual void print(ProgramStateRef state, raw_ostream &Out, const char *nl,
                      const char *sep) = 0;
 
+  virtual ProgramStateRef evalRangeOp(ProgramStateRef state, SVal V) {
+    return nullptr;
+  }
+
   virtual void EndPath(ProgramStateRef state) {}
 
   /// Convenience method to query the state to see if a symbol is null or
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to