clang API Documentation

ExprInspectionChecker.cpp
Go to the documentation of this file.
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