clang  3.8.0
ExprInspectionChecker.cpp
Go to the documentation of this file.
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"
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, check::DeadSymbols> {
21  mutable std::unique_ptr<BugType> BT;
22 
23  void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
24  void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
25  void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
26  void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
27  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
28 
29  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
30  CheckerContext &C) const;
31 
32 public:
33  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
34  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
35 };
36 }
37 
38 REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *)
39 
40 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
41  CheckerContext &C) const {
42  // These checks should have no effect on the surrounding environment
43  // (globals should not be invalidated, etc), hence the use of evalCall.
44  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
45  .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
46  .Case("clang_analyzer_checkInlined",
47  &ExprInspectionChecker::analyzerCheckInlined)
48  .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
49  .Case("clang_analyzer_warnIfReached",
50  &ExprInspectionChecker::analyzerWarnIfReached)
51  .Case("clang_analyzer_warnOnDeadSymbol",
52  &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
53  .Default(nullptr);
54 
55  if (!Handler)
56  return false;
57 
58  (this->*Handler)(CE, C);
59  return true;
60 }
61 
62 static const char *getArgumentValueString(const CallExpr *CE,
63  CheckerContext &C) {
64  if (CE->getNumArgs() == 0)
65  return "Missing assertion argument";
66 
68  const LocationContext *LC = N->getLocationContext();
70 
71  const Expr *Assertion = CE->getArg(0);
72  SVal AssertionVal = State->getSVal(Assertion, LC);
73 
74  if (AssertionVal.isUndef())
75  return "UNDEFINED";
76 
77  ProgramStateRef StTrue, StFalse;
78  std::tie(StTrue, StFalse) =
79  State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
80 
81  if (StTrue) {
82  if (StFalse)
83  return "UNKNOWN";
84  else
85  return "TRUE";
86  } else {
87  if (StFalse)
88  return "FALSE";
89  else
90  llvm_unreachable("Invalid constraint; neither true or false.");
91  }
92 }
93 
94 void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
95  CheckerContext &C) const {
97 
98  // A specific instantiation of an inlined function may have more constrained
99  // values than can generally be assumed. Skip the check.
100  if (LC->getCurrentStackFrame()->getParent() != nullptr)
101  return;
102 
103  if (!BT)
104  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
105 
107  if (!N)
108  return;
109  C.emitReport(
110  llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
111 }
112 
113 void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
114  CheckerContext &C) const {
115 
116  if (!BT)
117  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
118 
120  if (!N)
121  return;
122  C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N));
123 }
124 
125 void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
126  CheckerContext &C) const {
128 
129  // An inlined function could conceivably also be analyzed as a top-level
130  // function. We ignore this case and only emit a message (TRUE or FALSE)
131  // when we are analyzing it as an inlined function. This means that
132  // clang_analyzer_checkInlined(true) should always print TRUE, but
133  // clang_analyzer_checkInlined(false) should never actually print anything.
134  if (LC->getCurrentStackFrame()->getParent() == nullptr)
135  return;
136 
137  if (!BT)
138  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
139 
141  if (!N)
142  return;
143  C.emitReport(
144  llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
145 }
146 
147 void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
148  CheckerContext &C) const {
149  if (CE->getNumArgs() == 0)
150  return;
151  SVal Val = C.getSVal(CE->getArg(0));
152  SymbolRef Sym = Val.getAsSymbol();
153  if (!Sym)
154  return;
155 
157  State = State->add<MarkedSymbols>(Sym);
158  C.addTransition(State);
159 }
160 
161 void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
162  CheckerContext &C) const {
163  ProgramStateRef State = C.getState();
164  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
165  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
166  SymbolRef Sym = static_cast<SymbolRef>(*I);
167  if (!SymReaper.isDead(Sym))
168  continue;
169 
170  if (!BT)
171  BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
172 
174  if (!N)
175  return;
176 
177  C.emitReport(llvm::make_unique<BugReport>(*BT, "SYMBOL DEAD", N));
178  C.addTransition(State->remove<MarkedSymbols>(Sym), N);
179  }
180 }
181 
182 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
183  CheckerContext &C) const {
184  LLVM_BUILTIN_TRAP;
185 }
186 
187 void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
188  Mgr.registerChecker<ExprInspectionChecker>();
189 }
190 
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2199
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Symbolic value.
Definition: SymbolManager.h:42
LineState State
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() const
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
Expr - This represents one expression.
Definition: Expr.h:104
const ProgramStateRef & getState() const
const ProgramStateRef & getState() const
#define bool
Definition: stdbool.h:31
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
CHECKER * registerChecker()
Used to register checkers.
const StackFrameContext * getCurrentStackFrame() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:44
A class responsible for cleaning up unused symbols.
bool isUndef() const
Definition: SVals.h:121
const LocationContext * getParent() const
static const char * getArgumentValueString(const CallExpr *CE, CheckerContext &C)
detail::InMemoryDirectory::const_iterator E
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2187
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:111
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2134
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:75
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.