193a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
293a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//
393a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//                     The LLVM Compiler Infrastructure
493a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//
593a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose// This file is distributed under the University of Illinois Open Source
693a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose// License. See LICENSE.TXT for details.
793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//
893a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose//===----------------------------------------------------------------------===//
993a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1093a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose#include "ClangSACheckers.h"
1193a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1255fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/Checker.h"
1393a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
144967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
159852f58f50b4fc20914fbce5b4454135a42343f4Benjamin Kramer#include "llvm/ADT/StringSwitch.h"
1693a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Roseusing namespace clang;
1893a9d828378b5c969344f27aeb275b8c2a19d918Jordy Roseusing namespace ento;
1993a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
2093a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rosenamespace {
2187d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainarclass ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
22651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines  mutable std::unique_ptr<BugType> BT;
23e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
24e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
25e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
26d000b852022bcd4fc14029b48d2fa873f63e4032Jordan Rose  void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
27ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose  void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
2887d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
294967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
304967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
31e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
32e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
33e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose                                                 CheckerContext &C) const;
34e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
354967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  void reportBug(llvm::StringRef Msg, CheckerContext &C) const;
364967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
3793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rosepublic:
3893a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
3987d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
4093a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose};
4193a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose}
4293a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
434967a710c84587c654b56c828382219c3937dacbPirama Arumuga NainarREGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
4487d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar
4593a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rosebool ExprInspectionChecker::evalCall(const CallExpr *CE,
46e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose                                     CheckerContext &C) const {
4793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  // These checks should have no effect on the surrounding environment
48e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // (globals should not be invalidated, etc), hence the use of evalCall.
49e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
50e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
51e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    .Case("clang_analyzer_checkInlined",
52e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose          &ExprInspectionChecker::analyzerCheckInlined)
53ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose    .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
5487d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    .Case("clang_analyzer_warnIfReached",
5587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar          &ExprInspectionChecker::analyzerWarnIfReached)
5687d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    .Case("clang_analyzer_warnOnDeadSymbol",
5787d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar          &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
584967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
594967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
606bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    .Default(nullptr);
61e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
62e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  if (!Handler)
63e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    return false;
64e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
65e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  (this->*Handler)(CE, C);
66e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  return true;
67e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose}
68e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
69e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rosestatic const char *getArgumentValueString(const CallExpr *CE,
70e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose                                          CheckerContext &C) {
71e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  if (CE->getNumArgs() == 0)
72e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    return "Missing assertion argument";
73e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
7493a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  ExplodedNode *N = C.getPredecessor();
7593a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  const LocationContext *LC = N->getLocationContext();
76e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  ProgramStateRef State = N->getState();
77e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
78e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  const Expr *Assertion = CE->getArg(0);
79e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  SVal AssertionVal = State->getSVal(Assertion, LC);
80e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
81e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  if (AssertionVal.isUndef())
82e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    return "UNDEFINED";
83e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
84e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  ProgramStateRef StTrue, StFalse;
85651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines  std::tie(StTrue, StFalse) =
865251abea41b446c26e3239c8dd6c7edea6fc335dDavid Blaikie    State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
87e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
88e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  if (StTrue) {
89e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    if (StFalse)
90e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose      return "UNKNOWN";
91e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    else
92e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose      return "TRUE";
93e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  } else {
94e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    if (StFalse)
95e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose      return "FALSE";
96e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    else
97e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose      llvm_unreachable("Invalid constraint; neither true or false.");
98e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  }
99e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose}
10093a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1014967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainarvoid ExprInspectionChecker::reportBug(llvm::StringRef Msg,
1024967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar                                      CheckerContext &C) const {
1034967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  if (!BT)
1044967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
1054967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1064967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  ExplodedNode *N = C.generateNonFatalErrorNode();
1074967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  if (!N)
1084967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    return;
1094967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1104967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  C.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
1114967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar}
1124967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
113e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rosevoid ExprInspectionChecker::analyzerEval(const CallExpr *CE,
114e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose                                         CheckerContext &C) const {
11587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  const LocationContext *LC = C.getPredecessor()->getLocationContext();
11693a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
11793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  // A specific instantiation of an inlined function may have more constrained
11893a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  // values than can generally be assumed. Skip the check.
1196bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines  if (LC->getCurrentStackFrame()->getParent() != nullptr)
120e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    return;
12193a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1224967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  reportBug(getArgumentValueString(CE, C), C);
123e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose}
124e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose
125d000b852022bcd4fc14029b48d2fa873f63e4032Jordan Rosevoid ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
126d000b852022bcd4fc14029b48d2fa873f63e4032Jordan Rose                                                  CheckerContext &C) const {
1274967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  reportBug("REACHABLE", C);
128d000b852022bcd4fc14029b48d2fa873f63e4032Jordan Rose}
129d000b852022bcd4fc14029b48d2fa873f63e4032Jordan Rose
130e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rosevoid ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
131e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose                                                 CheckerContext &C) const {
13287d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  const LocationContext *LC = C.getPredecessor()->getLocationContext();
13393a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
134e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // An inlined function could conceivably also be analyzed as a top-level
135e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // function. We ignore this case and only emit a message (TRUE or FALSE)
136e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // when we are analyzing it as an inlined function. This means that
137e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // clang_analyzer_checkInlined(true) should always print TRUE, but
138e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose  // clang_analyzer_checkInlined(false) should never actually print anything.
1396bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines  if (LC->getCurrentStackFrame()->getParent() == nullptr)
140e5399f1375f8571bdd821ae08291af1c895adfd3Jordan Rose    return;
14193a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1424967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  reportBug(getArgumentValueString(CE, C), C);
1434967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar}
14493a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
1454967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainarvoid ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
1464967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar                                            CheckerContext &C) const {
1474967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  if (CE->getNumArgs() == 0)
1484967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    reportBug("Missing argument for explaining", C);
1494967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1504967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  SVal V = C.getSVal(CE->getArg(0));
1514967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  SValExplainer Ex(C.getASTContext());
1524967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  reportBug(Ex.Visit(V), C);
1534967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar}
1544967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1554967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainarvoid ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
1564967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar                                              CheckerContext &C) const {
1574967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  if (CE->getNumArgs() == 0)
1584967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    reportBug("Missing region for obtaining extent", C);
1594967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1604967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
1614967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  if (!MR)
1624967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    reportBug("Obtaining extent of a non-region", C);
1634967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar
1644967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  ProgramStateRef State = C.getState();
1654967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  State = State->BindExpr(CE, C.getLocationContext(),
1664967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar                          MR->getExtent(C.getSValBuilder()));
1674967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  C.addTransition(State);
16887d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar}
16987d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar
17087d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainarvoid ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
17187d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar                                                     CheckerContext &C) const {
17287d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  if (CE->getNumArgs() == 0)
17387d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    return;
17487d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  SVal Val = C.getSVal(CE->getArg(0));
17587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  SymbolRef Sym = Val.getAsSymbol();
17687d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  if (!Sym)
17787d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    return;
17887d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar
17987d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  ProgramStateRef State = C.getState();
18087d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  State = State->add<MarkedSymbols>(Sym);
18187d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  C.addTransition(State);
18287d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar}
18387d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar
18487d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainarvoid ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
18587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar                                             CheckerContext &C) const {
18687d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  ProgramStateRef State = C.getState();
18787d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
18887d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
1894967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    SymbolRef Sym = *I;
19087d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar    if (!SymReaper.isDead(Sym))
19187d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar      continue;
19287d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar
1934967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    reportBug("SYMBOL DEAD", C);
1944967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar    State = State->remove<MarkedSymbols>(Sym);
19587d948ecccffea9e9e37d0d053b246e2d6d6c47bPirama Arumuga Nainar  }
1964967a710c84587c654b56c828382219c3937dacbPirama Arumuga Nainar  C.addTransition(State);
19793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose}
19893a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
199ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rosevoid ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
200ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose                                          CheckerContext &C) const {
201ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose  LLVM_BUILTIN_TRAP;
202ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose}
203ac7cc2d37e82181e73fcc265c1d0a619d18b7605Jordan Rose
20493a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rosevoid ento::registerExprInspectionChecker(CheckerManager &Mgr) {
20593a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose  Mgr.registerChecker<ExprInspectionChecker>();
20693a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose}
20793a9d828378b5c969344f27aeb275b8c2a19d918Jordy Rose
208