1 //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ClangSACheckers.h" 11 #include "clang/StaticAnalyzer/Core/Checker.h" 12 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 13 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 14 #include "llvm/ADT/StringSwitch.h" 15 16 using namespace clang; 17 using namespace ento; 18 19 namespace { 20 class ExprInspectionChecker : public Checker< eval::Call > { 21 mutable OwningPtr<BugType> BT; 22 23 void analyzerEval(const CallExpr *CE, CheckerContext &C) const; 24 void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; 25 26 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, 27 CheckerContext &C) const; 28 29 public: 30 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 31 }; 32 } 33 34 bool ExprInspectionChecker::evalCall(const CallExpr *CE, 35 CheckerContext &C) const { 36 // These checks should have no effect on the surrounding environment 37 // (globals should not be invalidated, etc), hence the use of evalCall. 38 FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) 39 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) 40 .Case("clang_analyzer_checkInlined", 41 &ExprInspectionChecker::analyzerCheckInlined) 42 .Default(0); 43 44 if (!Handler) 45 return false; 46 47 (this->*Handler)(CE, C); 48 return true; 49 } 50 51 static const char *getArgumentValueString(const CallExpr *CE, 52 CheckerContext &C) { 53 if (CE->getNumArgs() == 0) 54 return "Missing assertion argument"; 55 56 ExplodedNode *N = C.getPredecessor(); 57 const LocationContext *LC = N->getLocationContext(); 58 ProgramStateRef State = N->getState(); 59 60 const Expr *Assertion = CE->getArg(0); 61 SVal AssertionVal = State->getSVal(Assertion, LC); 62 63 if (AssertionVal.isUndef()) 64 return "UNDEFINED"; 65 66 ProgramStateRef StTrue, StFalse; 67 llvm::tie(StTrue, StFalse) = 68 State->assume(cast<DefinedOrUnknownSVal>(AssertionVal)); 69 70 if (StTrue) { 71 if (StFalse) 72 return "UNKNOWN"; 73 else 74 return "TRUE"; 75 } else { 76 if (StFalse) 77 return "FALSE"; 78 else 79 llvm_unreachable("Invalid constraint; neither true or false."); 80 } 81 } 82 83 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 84 CheckerContext &C) const { 85 ExplodedNode *N = C.getPredecessor(); 86 const LocationContext *LC = N->getLocationContext(); 87 88 // A specific instantiation of an inlined function may have more constrained 89 // values than can generally be assumed. Skip the check. 90 if (LC->getCurrentStackFrame()->getParent() != 0) 91 return; 92 93 if (!BT) 94 BT.reset(new BugType("Checking analyzer assumptions", "debug")); 95 96 BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); 97 C.emitReport(R); 98 } 99 100 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 101 CheckerContext &C) const { 102 ExplodedNode *N = C.getPredecessor(); 103 const LocationContext *LC = N->getLocationContext(); 104 105 // An inlined function could conceivably also be analyzed as a top-level 106 // function. We ignore this case and only emit a message (TRUE or FALSE) 107 // when we are analyzing it as an inlined function. This means that 108 // clang_analyzer_checkInlined(true) should always print TRUE, but 109 // clang_analyzer_checkInlined(false) should never actually print anything. 110 if (LC->getCurrentStackFrame()->getParent() == 0) 111 return; 112 113 if (!BT) 114 BT.reset(new BugType("Checking analyzer assumptions", "debug")); 115 116 BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); 117 C.emitReport(R); 118 } 119 120 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 121 Mgr.registerChecker<ExprInspectionChecker>(); 122 } 123 124