ExprInspectionChecker.cpp revision ac7cc2d37e82181e73fcc265c1d0a619d18b7605
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
16using namespace clang;
17using namespace ento;
18
19namespace {
20class 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
30public:
31  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
32};
33}
34
35bool 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
53static 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
85void 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
102void 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
122void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
123                                          CheckerContext &C) const {
124  LLVM_BUILTIN_TRAP;
125}
126
127void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
128  Mgr.registerChecker<ExprInspectionChecker>();
129}
130
131