forked from OSchip/llvm-project
[analyzer] Add new function `clang_analyzer_value` to ExprInspectionChecker
Summary: Introduce a new function 'clang_analyzer_value'. It emits a report that in turn prints a RangeSet or APSInt associated with SVal. If there is no associated value, prints "n/a".
This commit is contained in:
parent
4c85a01758
commit
bc08c3cb7f
|
@ -309,6 +309,33 @@ ExprInspection checks
|
|||
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.
|
||||
Supported argument types are integers, enums and pointers.
|
||||
The value can be represented either as a range set or as a concrete integer.
|
||||
For the rest of the types function prints ``n/a`` (aka not available).
|
||||
|
||||
**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. <https://llvm.org/docs/TestingGuide.html#constraining-test-execution>`_
|
||||
|
||||
* 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
|
||||
==========
|
||||
|
|
|
@ -119,6 +119,9 @@ public:
|
|||
const char *NL, unsigned int Space,
|
||||
bool IsDot) const = 0;
|
||||
|
||||
virtual void printValue(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) {
|
||||
|
|
|
@ -169,6 +169,11 @@ public:
|
|||
/// 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.
|
||||
|
|
|
@ -40,6 +40,7 @@ class ExprInspectionChecker
|
|||
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 @@ class ExprInspectionChecker
|
|||
Optional<SVal> ExprVal = None) const;
|
||||
ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
|
||||
Optional<SVal> ExprVal = None) const;
|
||||
template <typename T> 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 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
|
|||
&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 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
|
|||
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().printValue(Out, C.getState(), Sym);
|
||||
}
|
||||
|
||||
static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
|
||||
SVal V) {
|
||||
Out << V;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 @@ void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
|
|||
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 @@ void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,
|
|||
|
||||
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 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
|
|||
|
||||
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,
|
||||
|
|
|
@ -1819,6 +1819,8 @@ public:
|
|||
|
||||
void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
|
||||
unsigned int Space = 0, bool IsDot = false) const override;
|
||||
void printValue(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;
|
||||
|
@ -3172,6 +3174,13 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
|
|||
printDisequalities(Out, State, NL, Space, IsDot);
|
||||
}
|
||||
|
||||
void RangeConstraintManager::printValue(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);
|
||||
|
|
|
@ -109,6 +109,14 @@ SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
|
|||
return getAsLocSymbol(IncludeBaseRegions);
|
||||
}
|
||||
|
||||
const llvm::APSInt *SVal::getAsInteger() const {
|
||||
if (auto CI = getAs<nonloc::ConcreteInt>())
|
||||
return &CI->getValue();
|
||||
if (auto CI = getAs<loc::ConcreteInt>())
|
||||
return &CI->getValue();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const MemRegion *SVal::getAsRegion() const {
|
||||
if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
|
||||
return X->getRegion();
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config eagerly-assume=false -verify %s
|
||||
// UNSUPPORTED z3
|
||||
|
||||
template <typename T>
|
||||
void clang_analyzer_value(T x);
|
||||
void clang_analyzer_value();
|
||||
template <typename T1, typename T2>
|
||||
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}}
|
||||
}
|
Loading…
Reference in New Issue