Index: clang/docs/analyzer/developer-docs/DebugChecks.rst =================================================================== --- clang/docs/analyzer/developer-docs/DebugChecks.rst +++ clang/docs/analyzer/developer-docs/DebugChecks.rst @@ -309,6 +309,32 @@ clang_analyzer_dumpExtent(a); // expected-warning {{8 S64b}} clang_analyzer_dumpElementCount(a); // expected-warning {{2 S64b}} } + +- ``clang_analyzer_value(a single argument of integer or pointer type)`` + + Prints an associated value for the given argument. + The value can be represented either as a range set or as a concrete integer. + The argument shall be of an integer or pointer type. + + **Note:** This function will print nothing for clang built with Z3 constraint manager. + This may cause crashes of your tests. To manage this use one of the test constraining + techniques: + + * llvm-lit commands ``REQUIRES no-z3`` or ``UNSUPPORTED z3`` `See for details. `_ + + * a preprocessor directive ``#ifndef ANALYZER_CM_Z3`` + + * a clang command argument ``-analyzer-constraints=range`` + + Example usage:: + + void print(char c, unsigned u) { + clang_analyzer_value(c); // expected-warning {{8s:{ [-128, 127] }}} + if(u != 42) + clang_analyzer_value(u); // expected-warning {{32u:{ [0, 41], [43, 4294967295] }}} + else + clang_analyzer_value(u); // expected-warning {{32u:42}} + } Statistics ========== Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -123,6 +123,9 @@ const char *NL, unsigned int Space, bool IsDot) const = 0; + virtual void printRange(raw_ostream &Out, ProgramStateRef State, + SymbolRef Sym) {} + /// Convenience method to query the state to see if a symbol is null or /// not null, or if neither assumption can be made. ConditionTruthVal isNull(ProgramStateRef State, SymbolRef Sym) { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -169,6 +169,11 @@ /// should continue to the base regions if the region is not symbolic. SymbolRef getAsSymbol(bool IncludeBaseRegions = false) const; + /// If this SVal is loc::ConcreteInt or nonloc::ConcreteInt, + /// return a pointer to APSInt which is held in it. + /// Otherwise, return nullptr. + const llvm::APSInt *getAsInteger() const; + const MemRegion *getAsRegion() const; /// printJson - Pretty-prints in JSON format. Index: clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -40,6 +40,7 @@ void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; + void analyzerValue(const CallExpr *CE, CheckerContext &C) const; void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const; void analyzerDump(const CallExpr *CE, CheckerContext &C) const; void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; @@ -60,6 +61,7 @@ Optional ExprVal = None) const; ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, Optional ExprVal = None) const; + template void printAndReport(CheckerContext &C, T What) const; const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const; @@ -99,6 +101,7 @@ &ExprInspectionChecker::analyzerDumpExtent) .Case("clang_analyzer_dumpElementCount", &ExprInspectionChecker::analyzerDumpElementCount) + .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue) .StartsWith("clang_analyzer_dumpSvalType", &ExprInspectionChecker::analyzerDumpSValType) .StartsWith("clang_analyzer_dump", @@ -258,6 +261,45 @@ reportBug(Ex.Visit(V), C); } +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + const llvm::APSInt &I) { + Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:"); + Out << I; +} + +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + SymbolRef Sym) { + C.getConstraintManager().printRange(Out, C.getState(), Sym); +} + +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + SVal V) { + Out << V; +} + +template +void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const { + llvm::SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + printHelper(OS, C, What); + reportBug(OS.str(), C); +} + +void ExprInspectionChecker::analyzerValue(const CallExpr *CE, + CheckerContext &C) const { + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) + return; + + SVal V = C.getSVal(Arg); + if (const SymbolRef Sym = V.getAsSymbol()) + printAndReport(C, Sym); + else if (const llvm::APSInt *I = V.getAsInteger()) + printAndReport(C, *I); + else + reportBug("n/a", C); +} + void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const { const Expr *Arg = getArgExpr(CE, C); @@ -275,11 +317,7 @@ return; SVal V = C.getSVal(Arg); - - llvm::SmallString<32> Str; - llvm::raw_svector_ostream OS(Str); - V.dumpToStream(OS); - reportBug(OS.str(), C); + printAndReport(C, V); } void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, @@ -303,11 +341,7 @@ DefinedOrUnknownSVal Size = getDynamicExtent(C.getState(), MR, C.getSValBuilder()); - - SmallString<64> Msg; - llvm::raw_svector_ostream Out(Msg); - Out << Size; - reportBug(Out.str(), C); + printAndReport(C, Size); } void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, @@ -328,11 +362,7 @@ DefinedOrUnknownSVal ElementCount = getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); - - SmallString<128> Msg; - llvm::raw_svector_ostream Out(Msg); - Out << ElementCount; - reportBug(Out.str(), C); + printAndReport(C, ElementCount); } void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1801,6 +1801,8 @@ void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false) const override; + void printRange(raw_ostream &Out, ProgramStateRef State, + SymbolRef Sym) override; void printConstraints(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false) const; @@ -3154,6 +3156,13 @@ printDisequalities(Out, State, NL, Space, IsDot); } +void RangeConstraintManager::printRange(raw_ostream &Out, ProgramStateRef State, + SymbolRef Sym) { + const RangeSet RS = getRange(State, Sym); + Out << RS.getBitWidth() << (RS.isUnsigned() ? "u:" : "s:"); + RS.dump(Out); +} + static std::string toString(const SymbolRef &Sym) { std::string S; llvm::raw_string_ostream O(S); Index: clang/lib/StaticAnalyzer/Core/SVals.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SVals.cpp +++ clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -109,6 +109,14 @@ return getAsLocSymbol(IncludeBaseRegions); } +const llvm::APSInt *SVal::getAsInteger() const { + if (auto CI = getAs()) + return &CI->getValue(); + if (auto CI = getAs()) + return &CI->getValue(); + return nullptr; +} + const MemRegion *SVal::getAsRegion() const { if (Optional X = getAs()) return X->getRegion(); Index: clang/test/Analysis/print-ranges.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/print-ranges.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config eagerly-assume=false -verify %s +// REQUIRES: no-z3 + +template +void clang_analyzer_value(T x); +void clang_analyzer_value(); +template +void clang_analyzer_value(T1 x, T2 y); + +void test1(char x) { + clang_analyzer_value(x); // expected-warning{{8s:{ [-128, 127] }}} + if (x > 42) + clang_analyzer_value(x); // expected-warning{{8s:{ [43, 127] }}} + if (x == 42) + clang_analyzer_value(x); // expected-warning{{8s:42}} +} + +void test2(short x) { + clang_analyzer_value(x); // expected-warning{{16s:{ [-32768, 32767] }}} + if (x < 4200) + clang_analyzer_value(x); // expected-warning{{16s:{ [-32768, 4199] }}} + if (x == 4200) + clang_analyzer_value(x); // expected-warning{{16s:4200}} +} + +void test3(unsigned long long x) { + clang_analyzer_value(x); // expected-warning{{64u:{ [0, 18446744073709551615] }}} + if (x != 42000000) + clang_analyzer_value(x); // expected-warning{{64u:{ [0, 41999999], [42000001, 18446744073709551615] }}} + if (x == 18446744073709551615ull) + clang_analyzer_value(x); // expected-warning{{64u:18446744073709551615}} +} + +struct S {}; +void test4(S s) { + clang_analyzer_value(s); // expected-warning{{n/a}} +} + +void test5() { + clang_analyzer_value(); // expected-warning{{Missing argument}} +} + +void test6(int x, int y) { + if (x == 42 && y == 24) + // Ignore 'y'. + clang_analyzer_value(x, y); // expected-warning{{32s:42}} +}