clang API Documentation
00001 //==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 00010 #include "ClangSACheckers.h" 00011 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00012 #include "clang/StaticAnalyzer/Core/Checker.h" 00013 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00014 #include "llvm/ADT/StringSwitch.h" 00015 00016 using namespace clang; 00017 using namespace ento; 00018 00019 namespace { 00020 class ExprInspectionChecker : public Checker< eval::Call > { 00021 mutable std::unique_ptr<BugType> BT; 00022 00023 void analyzerEval(const CallExpr *CE, CheckerContext &C) const; 00024 void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; 00025 void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; 00026 void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; 00027 00028 typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, 00029 CheckerContext &C) const; 00030 00031 public: 00032 bool evalCall(const CallExpr *CE, CheckerContext &C) const; 00033 }; 00034 } 00035 00036 bool ExprInspectionChecker::evalCall(const CallExpr *CE, 00037 CheckerContext &C) const { 00038 // These checks should have no effect on the surrounding environment 00039 // (globals should not be invalidated, etc), hence the use of evalCall. 00040 FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) 00041 .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) 00042 .Case("clang_analyzer_checkInlined", 00043 &ExprInspectionChecker::analyzerCheckInlined) 00044 .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) 00045 .Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached) 00046 .Default(nullptr); 00047 00048 if (!Handler) 00049 return false; 00050 00051 (this->*Handler)(CE, C); 00052 return true; 00053 } 00054 00055 static const char *getArgumentValueString(const CallExpr *CE, 00056 CheckerContext &C) { 00057 if (CE->getNumArgs() == 0) 00058 return "Missing assertion argument"; 00059 00060 ExplodedNode *N = C.getPredecessor(); 00061 const LocationContext *LC = N->getLocationContext(); 00062 ProgramStateRef State = N->getState(); 00063 00064 const Expr *Assertion = CE->getArg(0); 00065 SVal AssertionVal = State->getSVal(Assertion, LC); 00066 00067 if (AssertionVal.isUndef()) 00068 return "UNDEFINED"; 00069 00070 ProgramStateRef StTrue, StFalse; 00071 std::tie(StTrue, StFalse) = 00072 State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); 00073 00074 if (StTrue) { 00075 if (StFalse) 00076 return "UNKNOWN"; 00077 else 00078 return "TRUE"; 00079 } else { 00080 if (StFalse) 00081 return "FALSE"; 00082 else 00083 llvm_unreachable("Invalid constraint; neither true or false."); 00084 } 00085 } 00086 00087 void ExprInspectionChecker::analyzerEval(const CallExpr *CE, 00088 CheckerContext &C) const { 00089 ExplodedNode *N = C.getPredecessor(); 00090 const LocationContext *LC = N->getLocationContext(); 00091 00092 // A specific instantiation of an inlined function may have more constrained 00093 // values than can generally be assumed. Skip the check. 00094 if (LC->getCurrentStackFrame()->getParent() != nullptr) 00095 return; 00096 00097 if (!BT) 00098 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 00099 00100 BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); 00101 C.emitReport(R); 00102 } 00103 00104 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, 00105 CheckerContext &C) const { 00106 ExplodedNode *N = C.getPredecessor(); 00107 00108 if (!BT) 00109 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 00110 00111 BugReport *R = new BugReport(*BT, "REACHABLE", N); 00112 C.emitReport(R); 00113 } 00114 00115 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, 00116 CheckerContext &C) const { 00117 ExplodedNode *N = C.getPredecessor(); 00118 const LocationContext *LC = N->getLocationContext(); 00119 00120 // An inlined function could conceivably also be analyzed as a top-level 00121 // function. We ignore this case and only emit a message (TRUE or FALSE) 00122 // when we are analyzing it as an inlined function. This means that 00123 // clang_analyzer_checkInlined(true) should always print TRUE, but 00124 // clang_analyzer_checkInlined(false) should never actually print anything. 00125 if (LC->getCurrentStackFrame()->getParent() == nullptr) 00126 return; 00127 00128 if (!BT) 00129 BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); 00130 00131 BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N); 00132 C.emitReport(R); 00133 } 00134 00135 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, 00136 CheckerContext &C) const { 00137 LLVM_BUILTIN_TRAP; 00138 } 00139 00140 void ento::registerExprInspectionChecker(CheckerManager &Mgr) { 00141 Mgr.registerChecker<ExprInspectionChecker>(); 00142 } 00143