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