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