forked from OSchip/llvm-project
[analyzer] Add clang_analyzer_checkInlined for debugging purposes.
This check is also accessible through the debug.ExprInspection checker. Like clang_analyzer_eval, you can use it to test the analyzer engine's current state; the argument should be true or false to indicate whether or not you expect the function to be inlined. When used in the positive case (clang_analyzer_checkInlined(true)), the analyzer prints the message "TRUE" if the function is ever inlined. However, clang_analyzer_checkInlined(false) should never print a message; this asserts that there should be no paths on which the current function is inlined, but then there are no paths on which to print a message! (If the assertion is violated, the message "FALSE" will be printed.) This asymmetry comes from the fact that the only other chance to print a message is when the function is analyzed as a top-level function. However, when we do that, we can't be sure it isn't also inlined elsewhere (such as in a recursive function, or if we want to analyze in both general or specialized cases). Rather than have all checkInlined calls have an appended, meaningless "FALSE" or "TOP-LEVEL" case, there is just no message printed. void clang_analyzer_checkInlined(int); For debugging purposes only! llvm-svn: 161708
This commit is contained in:
parent
29622c1f0b
commit
13937b1d7a
|
@ -18,6 +18,13 @@ using namespace ento;
|
|||
namespace {
|
||||
class ExprInspectionChecker : public Checker< eval::Call > {
|
||||
mutable OwningPtr<BugType> BT;
|
||||
|
||||
void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
|
||||
void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
|
||||
|
||||
typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
|
||||
CheckerContext &C) const;
|
||||
|
||||
public:
|
||||
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
|
||||
};
|
||||
|
@ -26,57 +33,87 @@ public:
|
|||
bool ExprInspectionChecker::evalCall(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
// These checks should have no effect on the surrounding environment
|
||||
// (globals should not be evaluated, etc), hence the use of evalCall.
|
||||
ExplodedNode *N = C.getPredecessor();
|
||||
const LocationContext *LC = N->getLocationContext();
|
||||
// (globals should not be invalidated, etc), hence the use of evalCall.
|
||||
FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
|
||||
.Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
|
||||
.Case("clang_analyzer_checkInlined",
|
||||
&ExprInspectionChecker::analyzerCheckInlined)
|
||||
.Default(0);
|
||||
|
||||
if (!C.getCalleeName(CE).equals("clang_analyzer_eval"))
|
||||
if (!Handler)
|
||||
return false;
|
||||
|
||||
// A specific instantiation of an inlined function may have more constrained
|
||||
// values than can generally be assumed. Skip the check.
|
||||
if (LC->getParent() != 0)
|
||||
(this->*Handler)(CE, C);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *Msg = 0;
|
||||
|
||||
static const char *getArgumentValueString(const CallExpr *CE,
|
||||
CheckerContext &C) {
|
||||
if (CE->getNumArgs() == 0)
|
||||
Msg = "Missing assertion argument";
|
||||
else {
|
||||
return "Missing assertion argument";
|
||||
|
||||
ExplodedNode *N = C.getPredecessor();
|
||||
const LocationContext *LC = N->getLocationContext();
|
||||
ProgramStateRef State = N->getState();
|
||||
|
||||
const Expr *Assertion = CE->getArg(0);
|
||||
SVal AssertionVal = State->getSVal(Assertion, LC);
|
||||
|
||||
if (AssertionVal.isUndef())
|
||||
Msg = "UNDEFINED";
|
||||
else {
|
||||
return "UNDEFINED";
|
||||
|
||||
ProgramStateRef StTrue, StFalse;
|
||||
llvm::tie(StTrue, StFalse) =
|
||||
State->assume(cast<DefinedOrUnknownSVal>(AssertionVal));
|
||||
|
||||
if (StTrue) {
|
||||
if (StFalse)
|
||||
Msg = "UNKNOWN";
|
||||
return "UNKNOWN";
|
||||
else
|
||||
Msg = "TRUE";
|
||||
return "TRUE";
|
||||
} else {
|
||||
if (StFalse)
|
||||
Msg = "FALSE";
|
||||
return "FALSE";
|
||||
else
|
||||
llvm_unreachable("Invalid constraint; neither true or false.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(Msg);
|
||||
void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
ExplodedNode *N = C.getPredecessor();
|
||||
const LocationContext *LC = N->getLocationContext();
|
||||
|
||||
// A specific instantiation of an inlined function may have more constrained
|
||||
// values than can generally be assumed. Skip the check.
|
||||
if (LC->getCurrentStackFrame()->getParent() != 0)
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, Msg, N);
|
||||
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
|
||||
C.EmitReport(R);
|
||||
}
|
||||
|
||||
return true;
|
||||
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
ExplodedNode *N = C.getPredecessor();
|
||||
const LocationContext *LC = N->getLocationContext();
|
||||
|
||||
// An inlined function could conceivably also be analyzed as a top-level
|
||||
// function. We ignore this case and only emit a message (TRUE or FALSE)
|
||||
// when we are analyzing it as an inlined function. This means that
|
||||
// clang_analyzer_checkInlined(true) should always print TRUE, but
|
||||
// clang_analyzer_checkInlined(false) should never actually print anything.
|
||||
if (LC->getCurrentStackFrame()->getParent() == 0)
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
|
||||
|
||||
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
|
||||
C.EmitReport(R);
|
||||
}
|
||||
|
||||
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -analyzer-store region -verify %s
|
||||
|
||||
void clang_analyzer_eval(int);
|
||||
void clang_analyzer_checkInlined(int);
|
||||
|
||||
int test1_f1() {
|
||||
int y = 1;
|
||||
y++;
|
||||
clang_analyzer_checkInlined(1); // expected-warning{{TRUE}}
|
||||
return y;
|
||||
}
|
||||
|
||||
|
@ -103,3 +105,8 @@ int plus1(int x) {
|
|||
return x + 1;
|
||||
}
|
||||
|
||||
|
||||
void never_called_by_anyone() {
|
||||
clang_analyzer_checkInlined(0); // no-warning
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue