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 Index: include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h @@ -23,6 +23,8 @@ class SimpleConstraintManager : public ConstraintManager { SubEngine *SU; + +protected: SValBuilder &SVB; public: Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -94,9 +94,13 @@ SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); if (!Result.isUnknown()) { state = state->BindExpr(B, LCtx, Result); + ProgramStateRef state2 = + getConstraintManager().evalRangeOp(state, Result); + Bldr.generateNode(B, *it, state2 ? state2 : state); + } else { + Bldr.generateNode(B, *it, state); } - Bldr.generateNode(B, *it, state); continue; } 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,55 @@ } Out << nl; } + +ProgramStateRef RangeConstraintManager::evalRangeOp(ProgramStateRef St, + SVal V) { + const SymExpr *SE = V.getAsSymExpr(); + if (!SE) + return nullptr; + + const SymIntExpr *SIE = dyn_cast(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(); + + ConstraintRangeTy Ranges = St->get(); + for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E; + ++I) { + if (LHS != I.getKey()) + continue; + const auto D = I.getData(); + for (auto I = D.begin(); I != D.end(); ++I) { + NonLoc FromN = SVB.makeIntVal(I->From()).castAs(); + NonLoc ToN = SVB.makeIntVal(I->To()).castAs(); + NonLoc RHSN = SVB.makeIntVal(RHS).castAs(); + + // Calculate Lower value. + SVal Tmp = SVB.evalBinOpNN(St, Opc, FromN, RHSN, LHS->getType()); + const llvm::APSInt &Lower = Tmp.castAs().getValue(); + + // Calculate Upper value. + Tmp = SVB.evalBinOpNN(St, Opc, ToN, RHSN, LHS->getType()); + const llvm::APSInt &Upper = Tmp.castAs().getValue(); + + // TODO: Handle truncations better + if (Lower > Upper) + continue; + + // Set Range for symbol. + 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(Sym, RS); + } + } + return nullptr; +} Index: test/Analysis/range_calc.c =================================================================== --- test/Analysis/range_calc.c +++ test/Analysis/range_calc.c @@ -0,0 +1,143 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(int); + +#define INT_MAX ((signed int)((~0U)>>1)) +#define INT_MIN ((signed int)(~((~0U)>>1))) + +void addInts(int X) +{ + if (X >= 10 && X <= 50) { + int Y = X + 2; + clang_analyzer_eval(Y >= 12 && Y <= 52); // expected-warning{{TRUE}} + } + + if (X < 5) { + int Y = X + 1; + clang_analyzer_eval(Y < 6); // expected-warning{{TRUE}} + } + + if (X >= 1000) { + int Y = X + 1; // might overflow + clang_analyzer_eval(Y >= 1001); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(Y == INT_MIN); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(Y == INT_MIN || Y >= 1001); // expected-warning{{TRUE}} + } +} + +void addU8(unsigned char X) +{ + if (X >= 10 && X <= 50) { + unsigned char Y = X + 2; + clang_analyzer_eval(Y >= 12 && Y <= 52); // expected-warning{{TRUE}} + } + + if (X < 5) { + unsigned char Y = X + 1; + clang_analyzer_eval(Y < 6); // expected-warning{{TRUE}} + } + + // TODO + if (X >= 10 && X <= 50) { + unsigned char Y = X + (-256); // truncation + clang_analyzer_eval(Y >= 10 && Y <= 50); // expected-warning{{FALSE}} + } + + // TODO + if (X >= 10 && X <= 50) { + unsigned char Y = X + 256; // truncation + clang_analyzer_eval(Y >= 10 && Y <= 50); // expected-warning{{FALSE}} expected-warning{{UNKNOWN}} + } + + // TODO + if (X >= 100) { + unsigned char Y = X + 1; // truncation + clang_analyzer_eval(Y == 0); // expected-warning{{FALSE}}}} + clang_analyzer_eval(Y >= 101); // expected-warning{{TRUE}} + clang_analyzer_eval(Y == 0 || Y >= 101); // expected-warning{{TRUE}} + } + + if (X >= 100) { + unsigned short Y = X + 1; + clang_analyzer_eval(Y >= 101 && Y <= 256); // expected-warning{{TRUE}} + } +} + +void sub1(int X) +{ + if (X >= 10 && X <= 50) { + int Y = X - 2; + clang_analyzer_eval(Y >= 8 && Y <= 48); // expected-warning{{TRUE}} + } + + if (X >= 10 && X <= 50) { + unsigned char Y = (unsigned int)X - 20; // truncation + clang_analyzer_eval(Y <= 30 || Y >= 246); // expected-warning{{TRUE}} + } + + // TODO + if (X >= 10 && X <= 50) { + unsigned char Y = (unsigned int)X - 256; // truncation + clang_analyzer_eval(Y >= 10 && Y <= 50); // expected-warning{{FALSE}} expected-warning{{UNKNOWN}} + } + + if (X < 5) { + int Y = X - 1; // might overflow + clang_analyzer_eval(Y < 4); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(Y == INT_MAX); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(Y == INT_MAX || Y < 4); // expected-warning{{TRUE}} + } + + if (X >= 1000) { + int Y = X - 1; + clang_analyzer_eval(Y >= 999); // expected-warning{{TRUE}} + } +} + +void subU8(unsigned char X) +{ + if (X >= 10 && X <= 50) { + unsigned char Y = X - 2; + clang_analyzer_eval(Y >= 8 && Y <= 48); // expected-warning{{TRUE}} + } + + if (X >= 100) { + unsigned char Y = X - 1; + clang_analyzer_eval(Y >= 99 && Y <= 254); // expected-warning{{TRUE}} + } + + if (X < 5) { + unsigned char Y = X - 1; // overflow + clang_analyzer_eval(Y < 4 || Y == 255); // expected-warning{{TRUE}} + } + + // TODO + if (X >= 10 && X <= 50) { + unsigned char Y = X - (-256); // truncation + clang_analyzer_eval(Y >= 10 && Y <= 50); // expected-warning{{FALSE}} expected-warning{{UNKNOWN}} + } + + // TODO + if (X >= 10 && X <= 50) { + unsigned char Y = X - 256; // truncation + clang_analyzer_eval(Y >= 10 && Y <= 50); // expected-warning{{FALSE}} + } + + if (X >= 100) { + unsigned short Y = X + 1; + clang_analyzer_eval(Y >= 101 && Y <= 256); // expected-warning{{TRUE}} + } +} + +void div(int X) +{ + if (X >= 10 && X <= 50) { + int Y = X / 2; + clang_analyzer_eval(Y >= 5); // expected-warning{{TRUE}} + clang_analyzer_eval(Y <= 25); // expected-warning{{TRUE}} + } + + // No overflows +} + +