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