From: Jordan Rose Date: Fri, 10 Aug 2012 22:26:29 +0000 (+0000) Subject: [analyzer] Add clang_analyzer_checkInlined for debugging purposes. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e5399f1375f8571bdd821ae08291af1c895adfd3;p=clang [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 --- 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 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(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(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(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 +} +