aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp113
-rw-r--r--test/Analysis/inline.c7
2 files changed, 82 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) {
diff --git a/test/Analysis/inline.c b/test/Analysis/inline.c
index 73d629a04a..944e1e2985 100644
--- a/test/Analysis/inline.c
+++ b/test/Analysis/inline.c
@@ -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
+}
+