CheckerManager.cpp revision dff6ef903ff4fcb43b5ea292ecd772e381393b5d
1//===--- CheckerManager.cpp - Static Analyzer Checker Manager -------------===//
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// Defines the Static Analyzer Checker Manager.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/StaticAnalyzer/Core/CheckerManager.h"
15#include "clang/StaticAnalyzer/Core/Checker.h"
16#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
17#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
18#include "clang/Analysis/ProgramPoint.h"
19#include "clang/AST/DeclBase.h"
20
21using namespace clang;
22using namespace ento;
23
24bool CheckerManager::hasPathSensitiveCheckers() const {
25  return !StmtCheckers.empty()              ||
26         !PreObjCMessageCheckers.empty()    ||
27         !PostObjCMessageCheckers.empty()   ||
28         !LocationCheckers.empty()          ||
29         !BindCheckers.empty()              ||
30         !EndAnalysisCheckers.empty()       ||
31         !EndPathCheckers.empty()           ||
32         !BranchConditionCheckers.empty()   ||
33         !LiveSymbolsCheckers.empty()       ||
34         !DeadSymbolsCheckers.empty()       ||
35         !RegionChangesCheckers.empty()     ||
36         !EvalAssumeCheckers.empty()        ||
37         !EvalCallCheckers.empty()          ||
38         !InlineCallCheckers.empty();
39}
40
41void CheckerManager::finishedCheckerRegistration() {
42#ifndef NDEBUG
43  // Make sure that for every event that has listeners, there is at least
44  // one dispatcher registered for it.
45  for (llvm::DenseMap<EventTag, EventInfo>::iterator
46         I = Events.begin(), E = Events.end(); I != E; ++I)
47    assert(I->second.HasDispatcher && "No dispatcher registered for an event");
48#endif
49}
50
51//===----------------------------------------------------------------------===//
52// Functions for running checkers for AST traversing..
53//===----------------------------------------------------------------------===//
54
55void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
56                                          BugReporter &BR) {
57  assert(D);
58
59  unsigned DeclKind = D->getKind();
60  CachedDeclCheckers *checkers = 0;
61  CachedDeclCheckersMapTy::iterator CCI = CachedDeclCheckersMap.find(DeclKind);
62  if (CCI != CachedDeclCheckersMap.end()) {
63    checkers = &(CCI->second);
64  } else {
65    // Find the checkers that should run for this Decl and cache them.
66    checkers = &CachedDeclCheckersMap[DeclKind];
67    for (unsigned i = 0, e = DeclCheckers.size(); i != e; ++i) {
68      DeclCheckerInfo &info = DeclCheckers[i];
69      if (info.IsForDeclFn(D))
70        checkers->push_back(info.CheckFn);
71    }
72  }
73
74  assert(checkers);
75  for (CachedDeclCheckers::iterator
76         I = checkers->begin(), E = checkers->end(); I != E; ++I)
77    (*I)(D, mgr, BR);
78}
79
80void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
81                                          BugReporter &BR) {
82  assert(D && D->hasBody());
83
84  for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i)
85    BodyCheckers[i](D, mgr, BR);
86}
87
88//===----------------------------------------------------------------------===//
89// Functions for running checkers for path-sensitive checking.
90//===----------------------------------------------------------------------===//
91
92template <typename CHECK_CTX>
93static void expandGraphWithCheckers(CHECK_CTX checkCtx,
94                                    ExplodedNodeSet &Dst,
95                                    const ExplodedNodeSet &Src) {
96
97  typename CHECK_CTX::CheckersTy::const_iterator
98      I = checkCtx.checkers_begin(), E = checkCtx.checkers_end();
99  if (I == E) {
100    Dst.insert(Src);
101    return;
102  }
103
104  ExplodedNodeSet Tmp1, Tmp2;
105  const ExplodedNodeSet *PrevSet = &Src;
106
107  for (; I != E; ++I) {
108    ExplodedNodeSet *CurrSet = 0;
109    if (I+1 == E)
110      CurrSet = &Dst;
111    else {
112      CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1;
113      CurrSet->clear();
114    }
115
116    for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
117         NI != NE; ++NI)
118      checkCtx.runChecker(*I, *CurrSet, *NI);
119
120    // Update which NodeSet is the current one.
121    PrevSet = CurrSet;
122  }
123}
124
125namespace {
126  struct CheckStmtContext {
127    typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy;
128    bool IsPreVisit;
129    const CheckersTy &Checkers;
130    const Stmt *S;
131    ExprEngine &Eng;
132
133    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
134    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
135
136    CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
137                     const Stmt *s, ExprEngine &eng)
138      : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { }
139
140    void runChecker(CheckerManager::CheckStmtFunc checkFn,
141                    ExplodedNodeSet &Dst, ExplodedNode *Pred) {
142      // FIXME: Remove respondsToCallback from CheckerContext;
143      CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
144                       IsPreVisit ? ProgramPoint::PreStmtKind :
145                                    ProgramPoint::PostStmtKind, 0, S);
146      checkFn(S, C);
147    }
148  };
149}
150
151/// \brief Run checkers for visiting Stmts.
152void CheckerManager::runCheckersForStmt(bool isPreVisit,
153                                        ExplodedNodeSet &Dst,
154                                        const ExplodedNodeSet &Src,
155                                        const Stmt *S,
156                                        ExprEngine &Eng) {
157  CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
158                     S, Eng);
159  expandGraphWithCheckers(C, Dst, Src);
160}
161
162namespace {
163  struct CheckObjCMessageContext {
164    typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
165    bool IsPreVisit;
166    const CheckersTy &Checkers;
167    const ObjCMessage &Msg;
168    ExprEngine &Eng;
169
170    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
171    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
172
173    CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers,
174                            const ObjCMessage &msg, ExprEngine &eng)
175      : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { }
176
177    void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
178                    ExplodedNodeSet &Dst, ExplodedNode *Pred) {
179      CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
180                       IsPreVisit ? ProgramPoint::PreStmtKind :
181                                    ProgramPoint::PostStmtKind, 0,
182                       Msg.getOriginExpr());
183      checkFn(Msg, C);
184    }
185  };
186}
187
188/// \brief Run checkers for visiting obj-c messages.
189void CheckerManager::runCheckersForObjCMessage(bool isPreVisit,
190                                               ExplodedNodeSet &Dst,
191                                               const ExplodedNodeSet &Src,
192                                               const ObjCMessage &msg,
193                                               ExprEngine &Eng) {
194  CheckObjCMessageContext C(isPreVisit,
195                            isPreVisit ? PreObjCMessageCheckers
196                                       : PostObjCMessageCheckers,
197                            msg, Eng);
198  expandGraphWithCheckers(C, Dst, Src);
199}
200
201namespace {
202  struct CheckLocationContext {
203    typedef std::vector<CheckerManager::CheckLocationFunc> CheckersTy;
204    const CheckersTy &Checkers;
205    SVal Loc;
206    bool IsLoad;
207    const Stmt *S;
208    ExprEngine &Eng;
209
210    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
211    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
212
213    CheckLocationContext(const CheckersTy &checkers,
214                         SVal loc, bool isLoad, const Stmt *s, ExprEngine &eng)
215      : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), Eng(eng) { }
216
217    void runChecker(CheckerManager::CheckLocationFunc checkFn,
218                    ExplodedNodeSet &Dst, ExplodedNode *Pred) {
219      CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
220                       IsLoad ? ProgramPoint::PreLoadKind :
221                       ProgramPoint::PreStoreKind, 0, S);
222      checkFn(Loc, IsLoad, C);
223    }
224  };
225}
226
227/// \brief Run checkers for load/store of a location.
228void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
229                                            const ExplodedNodeSet &Src,
230                                            SVal location, bool isLoad,
231                                            const Stmt *S, ExprEngine &Eng) {
232  CheckLocationContext C(LocationCheckers, location, isLoad, S, Eng);
233  expandGraphWithCheckers(C, Dst, Src);
234}
235
236namespace {
237  struct CheckBindContext {
238    typedef std::vector<CheckerManager::CheckBindFunc> CheckersTy;
239    const CheckersTy &Checkers;
240    SVal Loc;
241    SVal Val;
242    const Stmt *S;
243    ExprEngine &Eng;
244
245    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
246    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
247
248    CheckBindContext(const CheckersTy &checkers,
249                     SVal loc, SVal val, const Stmt *s, ExprEngine &eng)
250      : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng) { }
251
252    void runChecker(CheckerManager::CheckBindFunc checkFn,
253                    ExplodedNodeSet &Dst, ExplodedNode *Pred) {
254      CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
255                       ProgramPoint::PreStmtKind, 0, S);
256      checkFn(Loc, Val, C);
257    }
258  };
259}
260
261/// \brief Run checkers for binding of a value to a location.
262void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
263                                        const ExplodedNodeSet &Src,
264                                        SVal location, SVal val,
265                                        const Stmt *S, ExprEngine &Eng) {
266  CheckBindContext C(BindCheckers, location, val, S, Eng);
267  expandGraphWithCheckers(C, Dst, Src);
268}
269
270void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
271                                               BugReporter &BR,
272                                               ExprEngine &Eng) {
273  for (unsigned i = 0, e = EndAnalysisCheckers.size(); i != e; ++i)
274    EndAnalysisCheckers[i](G, BR, Eng);
275}
276
277/// \brief Run checkers for end of path.
278void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B,
279                                           ExprEngine &Eng) {
280  for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) {
281    CheckEndPathFunc fn = EndPathCheckers[i];
282    EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker);
283    fn(specialB, Eng);
284  }
285}
286
287/// \brief Run checkers for branch condition.
288void CheckerManager::runCheckersForBranchCondition(const Stmt *condition,
289                                                   BranchNodeBuilder &B,
290                                                   ExprEngine &Eng) {
291  for (unsigned i = 0, e = BranchConditionCheckers.size(); i != e; ++i) {
292    CheckBranchConditionFunc fn = BranchConditionCheckers[i];
293    fn(condition, B, Eng);
294  }
295}
296
297/// \brief Run checkers for live symbols.
298void CheckerManager::runCheckersForLiveSymbols(const ProgramState *state,
299                                               SymbolReaper &SymReaper) {
300  for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i)
301    LiveSymbolsCheckers[i](state, SymReaper);
302}
303
304namespace {
305  struct CheckDeadSymbolsContext {
306    typedef std::vector<CheckerManager::CheckDeadSymbolsFunc> CheckersTy;
307    const CheckersTy &Checkers;
308    SymbolReaper &SR;
309    const Stmt *S;
310    ExprEngine &Eng;
311
312    CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
313    CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
314
315    CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
316                            const Stmt *s, ExprEngine &eng)
317      : Checkers(checkers), SR(sr), S(s), Eng(eng) { }
318
319    void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
320                    ExplodedNodeSet &Dst, ExplodedNode *Pred) {
321      CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker,
322                       ProgramPoint::PostPurgeDeadSymbolsKind, 0, S);
323      checkFn(SR, C);
324    }
325  };
326}
327
328/// \brief Run checkers for dead symbols.
329void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
330                                               const ExplodedNodeSet &Src,
331                                               SymbolReaper &SymReaper,
332                                               const Stmt *S,
333                                               ExprEngine &Eng) {
334  CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng);
335  expandGraphWithCheckers(C, Dst, Src);
336}
337
338/// \brief True if at least one checker wants to check region changes.
339bool CheckerManager::wantsRegionChangeUpdate(const ProgramState *state) {
340  for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i)
341    if (RegionChangesCheckers[i].WantUpdateFn(state))
342      return true;
343
344  return false;
345}
346
347/// \brief Run checkers for region changes.
348const ProgramState *
349CheckerManager::runCheckersForRegionChanges(const ProgramState *state,
350                            const StoreManager::InvalidatedSymbols *invalidated,
351                                    ArrayRef<const MemRegion *> ExplicitRegions,
352                                          ArrayRef<const MemRegion *> Regions) {
353  for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
354    // If any checker declares the state infeasible (or if it starts that way),
355    // bail out.
356    if (!state)
357      return NULL;
358    state = RegionChangesCheckers[i].CheckFn(state, invalidated,
359                                             ExplicitRegions, Regions);
360  }
361  return state;
362}
363
364/// \brief Run checkers for handling assumptions on symbolic values.
365const ProgramState *
366CheckerManager::runCheckersForEvalAssume(const ProgramState *state,
367                                         SVal Cond, bool Assumption) {
368  for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
369    // If any checker declares the state infeasible (or if it starts that way),
370    // bail out.
371    if (!state)
372      return NULL;
373    state = EvalAssumeCheckers[i](state, Cond, Assumption);
374  }
375  return state;
376}
377
378/// \brief Run checkers for evaluating a call.
379/// Only one checker will evaluate the call.
380void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
381                                            const ExplodedNodeSet &Src,
382                                            const CallExpr *CE,
383                                            ExprEngine &Eng,
384                                            GraphExpander *defaultEval) {
385  if (EvalCallCheckers.empty()   &&
386      InlineCallCheckers.empty() &&
387      defaultEval == 0) {
388    Dst.insert(Src);
389    return;
390  }
391
392  for (ExplodedNodeSet::iterator
393         NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) {
394
395    ExplodedNode *Pred = *NI;
396    bool anyEvaluated = false;
397
398    // First, check if any of the InlineCall callbacks can evaluate the call.
399    assert(InlineCallCheckers.size() <= 1 &&
400           "InlineCall is a special hacky callback to allow intrusive"
401           "evaluation of the call (which simulates inlining). It is "
402           "currently only used by OSAtomicChecker and should go away "
403           "at some point.");
404    for (std::vector<InlineCallFunc>::iterator
405           EI = InlineCallCheckers.begin(), EE = InlineCallCheckers.end();
406         EI != EE; ++EI) {
407      ExplodedNodeSet checkDst;
408      bool evaluated = (*EI)(CE, Eng, Pred, checkDst);
409      assert(!(evaluated && anyEvaluated)
410             && "There are more than one checkers evaluating the call");
411      if (evaluated) {
412        anyEvaluated = true;
413        Dst.insert(checkDst);
414#ifdef NDEBUG
415        break; // on release don't check that no other checker also evals.
416#endif
417      }
418    }
419
420#ifdef NDEBUG // on release don't check that no other checker also evals.
421    if (anyEvaluated) {
422      break;
423    }
424#endif
425
426    // Next, check if any of the EvalCall callbacks can evaluate the call.
427    for (std::vector<EvalCallFunc>::iterator
428           EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end();
429         EI != EE; ++EI) {
430      ExplodedNodeSet checkDst;
431      CheckerContext C(checkDst, Eng.getBuilder(), Eng, Pred, EI->Checker,
432                       ProgramPoint::PostStmtKind, 0, CE);
433      bool evaluated = (*EI)(CE, C);
434      assert(!(evaluated && anyEvaluated)
435             && "There are more than one checkers evaluating the call");
436      if (evaluated) {
437        anyEvaluated = true;
438        Dst.insert(checkDst);
439#ifdef NDEBUG
440        break; // on release don't check that no other checker also evals.
441#endif
442      }
443    }
444
445    // If none of the checkers evaluated the call, ask ExprEngine to handle it.
446    if (!anyEvaluated) {
447      if (defaultEval)
448        defaultEval->expandGraph(Dst, Pred);
449      else
450        Dst.insert(Pred);
451    }
452  }
453}
454
455/// \brief Run checkers for the entire Translation Unit.
456void CheckerManager::runCheckersOnEndOfTranslationUnit(
457                                                  const TranslationUnitDecl *TU,
458                                                  AnalysisManager &mgr,
459                                                  BugReporter &BR) {
460  for (unsigned i = 0, e = EndOfTranslationUnitCheckers.size(); i != e; ++i)
461    EndOfTranslationUnitCheckers[i](TU, mgr, BR);
462}
463
464void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
465                                              const ProgramState *State,
466                                              const char *NL, const char *Sep) {
467  for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator
468        I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I)
469    I->second->printState(Out, State, NL, Sep);
470}
471
472//===----------------------------------------------------------------------===//
473// Internal registration functions for AST traversing.
474//===----------------------------------------------------------------------===//
475
476void CheckerManager::_registerForDecl(CheckDeclFunc checkfn,
477                                      HandlesDeclFunc isForDeclFn) {
478  DeclCheckerInfo info = { checkfn, isForDeclFn };
479  DeclCheckers.push_back(info);
480}
481
482void CheckerManager::_registerForBody(CheckDeclFunc checkfn) {
483  BodyCheckers.push_back(checkfn);
484}
485
486//===----------------------------------------------------------------------===//
487// Internal registration functions for path-sensitive checking.
488//===----------------------------------------------------------------------===//
489
490void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn,
491                                         HandlesStmtFunc isForStmtFn) {
492  StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true };
493  StmtCheckers.push_back(info);
494}
495void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn,
496                                          HandlesStmtFunc isForStmtFn) {
497  StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false };
498  StmtCheckers.push_back(info);
499}
500
501void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) {
502  PreObjCMessageCheckers.push_back(checkfn);
503}
504void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
505  PostObjCMessageCheckers.push_back(checkfn);
506}
507
508void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
509  LocationCheckers.push_back(checkfn);
510}
511
512void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
513  BindCheckers.push_back(checkfn);
514}
515
516void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
517  EndAnalysisCheckers.push_back(checkfn);
518}
519
520void CheckerManager::_registerForEndPath(CheckEndPathFunc checkfn) {
521  EndPathCheckers.push_back(checkfn);
522}
523
524void CheckerManager::_registerForBranchCondition(
525                                             CheckBranchConditionFunc checkfn) {
526  BranchConditionCheckers.push_back(checkfn);
527}
528
529void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
530  LiveSymbolsCheckers.push_back(checkfn);
531}
532
533void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) {
534  DeadSymbolsCheckers.push_back(checkfn);
535}
536
537void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn,
538                                     WantsRegionChangeUpdateFunc wantUpdateFn) {
539  RegionChangesCheckerInfo info = {checkfn, wantUpdateFn};
540  RegionChangesCheckers.push_back(info);
541}
542
543void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) {
544  EvalAssumeCheckers.push_back(checkfn);
545}
546
547void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
548  EvalCallCheckers.push_back(checkfn);
549}
550
551void CheckerManager::_registerForInlineCall(InlineCallFunc checkfn) {
552  InlineCallCheckers.push_back(checkfn);
553}
554
555void CheckerManager::_registerForEndOfTranslationUnit(
556                                            CheckEndOfTranslationUnit checkfn) {
557  EndOfTranslationUnitCheckers.push_back(checkfn);
558}
559
560//===----------------------------------------------------------------------===//
561// Implementation details.
562//===----------------------------------------------------------------------===//
563
564CheckerManager::CachedStmtCheckers *
565CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
566  assert(S);
567
568  CachedStmtCheckersKey key(S->getStmtClass(), isPreVisit);
569  CachedStmtCheckers *checkers = 0;
570  CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(key);
571  if (CCI != CachedStmtCheckersMap.end()) {
572    checkers = &(CCI->second);
573  } else {
574    // Find the checkers that should run for this Stmt and cache them.
575    checkers = &CachedStmtCheckersMap[key];
576    for (unsigned i = 0, e = StmtCheckers.size(); i != e; ++i) {
577      StmtCheckerInfo &info = StmtCheckers[i];
578      if (info.IsPreVisit == isPreVisit && info.IsForStmtFn(S))
579        checkers->push_back(info.CheckFn);
580    }
581  }
582
583  assert(checkers);
584  return checkers;
585}
586
587CheckerManager::~CheckerManager() {
588  for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i)
589    CheckerDtors[i]();
590}
591
592// Anchor for the vtable.
593GraphExpander::~GraphExpander() { }
594