aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-08-10 22:26:29 +0000
committerJordan Rose <jordan_rose@apple.com>2012-08-10 22:26:29 +0000
commite5399f1375f8571bdd821ae08291af1c895adfd3 (patch)
treef042c6dd6e828e8cac217e6f67c6430b12f5c19a /lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
parent1afc5f27383304dfb50de92d40e6580fcd3b162c (diff)
[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! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161708 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp113
1 files changed, 75 insertions, 38 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index f638dda2d8..7acf223e63 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -18,65 +18,102 @@ 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;
};
}
bool ExprInspectionChecker::evalCall(const CallExpr *CE,
- CheckerContext &C) const {
+ CheckerContext &C) const {
// These checks should have no effect on the surrounding environment
- // (globals should not be evaluated, etc), hence the use of evalCall.
+ // (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 (!Handler)
+ return false;
+
+ (this->*Handler)(CE, C);
+ return true;
+}
+
+static const char *getArgumentValueString(const CallExpr *CE,
+ CheckerContext &C) {
+ if (CE->getNumArgs() == 0)
+ 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())
+ return "UNDEFINED";
+
+ ProgramStateRef StTrue, StFalse;
+ llvm::tie(StTrue, StFalse) =
+ State->assume(cast<DefinedOrUnknownSVal>(AssertionVal));
+
+ if (StTrue) {
+ if (StFalse)
+ return "UNKNOWN";
+ else
+ return "TRUE";
+ } else {
+ if (StFalse)
+ return "FALSE";
+ else
+ llvm_unreachable("Invalid constraint; neither true or false.");
+ }
+}
- if (!C.getCalleeName(CE).equals("clang_analyzer_eval"))
- return false;
+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->getParent() != 0)
- return true;
+ if (LC->getCurrentStackFrame()->getParent() != 0)
+ return;
- const char *Msg = 0;
+ if (!BT)
+ BT.reset(new BugType("Checking analyzer assumptions", "debug"));
- if (CE->getNumArgs() == 0)
- Msg = "Missing assertion argument";
- else {
- ProgramStateRef State = N->getState();
- const Expr *Assertion = CE->getArg(0);
- SVal AssertionVal = State->getSVal(Assertion, LC);
-
- if (AssertionVal.isUndef())
- Msg = "UNDEFINED";
- else {
- ProgramStateRef StTrue, StFalse;
- llvm::tie(StTrue, StFalse) =
- State->assume(cast<DefinedOrUnknownSVal>(AssertionVal));
-
- if (StTrue) {
- if (StFalse)
- Msg = "UNKNOWN";
- else
- Msg = "TRUE";
- } else {
- if (StFalse)
- Msg = "FALSE";
- else
- llvm_unreachable("Invalid constraint; neither true or false.");
- }
- }
- }
+ BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
+ C.EmitReport(R);
+}
+
+void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.getPredecessor();
+ const LocationContext *LC = N->getLocationContext();
- assert(Msg);
+ // 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, Msg, N);
+ BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
C.EmitReport(R);
-
- return true;
}
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {