1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
using namespace ento;
namespace {
class ExprInspectionChecker : public Checker< eval::Call > {
mutable OwningPtr<BugType> BT;
void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
void analyzerCrash(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 {
// These checks should have no effect on the surrounding environment
// (globals should not be invalidated, etc), hence the use of evalCall.
FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
.Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
.Case("clang_analyzer_checkInlined",
&ExprInspectionChecker::analyzerCheckInlined)
.Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
.Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached)
.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(AssertionVal.castAs<DefinedOrUnknownSVal>());
if (StTrue) {
if (StFalse)
return "UNKNOWN";
else
return "TRUE";
} else {
if (StFalse)
return "FALSE";
else
llvm_unreachable("Invalid constraint; neither true or 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->getCurrentStackFrame()->getParent() != 0)
return;
if (!BT)
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
BugReport *R = new BugReport(*BT, getArgumentValueString(CE, C), N);
C.emitReport(R);
}
void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
CheckerContext &C) const {
ExplodedNode *N = C.getPredecessor();
if (!BT)
BT.reset(new BugType("Checking analyzer assumptions", "debug"));
BugReport *R = new BugReport(*BT, "REACHABLE", N);
C.emitReport(R);
}
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
CheckerContext &C) const {
ExplodedNode *N = C.getPredecessor();
const LocationContext *LC = N->getLocationContext();
// 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, getArgumentValueString(CE, C), N);
C.emitReport(R);
}
void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
CheckerContext &C) const {
LLVM_BUILTIN_TRAP;
}
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ExprInspectionChecker>();
}
|