AnalysisConsumer.cpp revision 58f2e7c3c3860e410fa3d8252862ef10be7cdc70
1//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===//
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// "Meta" ASTConsumer for running different source analyses.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AnalysisConsumer.h"
15#include "clang/AST/ASTConsumer.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/ParentMap.h"
20#include "clang/Analysis/Analyses/LiveVariables.h"
21#include "clang/Analysis/Analyses/UninitializedValues.h"
22#include "clang/Analysis/CFG.h"
23#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
24#include "clang/StaticAnalyzer/Core/CheckerManager.h"
25#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
26#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
27#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
28#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
29#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
30#include "clang/StaticAnalyzer/Core/PathSensitive/TransferFuncs.h"
31#include "clang/StaticAnalyzer/Core/PathDiagnosticClients.h"
32
33// FIXME: Restructure checker registration.
34#include "../Checkers/InternalChecks.h"
35#include "../Checkers/BasicObjCFoundationChecks.h"
36
37#include "clang/Basic/FileManager.h"
38#include "clang/Basic/SourceManager.h"
39#include "clang/Frontend/AnalyzerOptions.h"
40#include "clang/Lex/Preprocessor.h"
41#include "llvm/Support/raw_ostream.h"
42#include "llvm/Support/Path.h"
43#include "llvm/Support/Program.h"
44#include "llvm/ADT/OwningPtr.h"
45
46using namespace clang;
47using namespace ento;
48
49static ExplodedNode::Auditor* CreateUbiViz();
50
51//===----------------------------------------------------------------------===//
52// Special PathDiagnosticClients.
53//===----------------------------------------------------------------------===//
54
55static PathDiagnosticClient*
56createPlistHTMLDiagnosticClient(const std::string& prefix,
57                                const Preprocessor &PP) {
58  PathDiagnosticClient *PD =
59    createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP);
60  return createPlistDiagnosticClient(prefix, PP, PD);
61}
62
63//===----------------------------------------------------------------------===//
64// AnalysisConsumer declaration.
65//===----------------------------------------------------------------------===//
66
67namespace {
68
69class AnalysisConsumer : public ASTConsumer {
70public:
71  typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D);
72  typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M,
73                           TranslationUnitDecl &TU);
74
75private:
76  typedef std::vector<CodeAction> Actions;
77  typedef std::vector<TUAction> TUActions;
78
79  Actions FunctionActions;
80  Actions ObjCMethodActions;
81  Actions ObjCImplementationActions;
82  Actions CXXMethodActions;
83
84public:
85  ASTContext* Ctx;
86  const Preprocessor &PP;
87  const std::string OutDir;
88  AnalyzerOptions Opts;
89
90  // PD is owned by AnalysisManager.
91  PathDiagnosticClient *PD;
92
93  StoreManagerCreator CreateStoreMgr;
94  ConstraintManagerCreator CreateConstraintMgr;
95
96  llvm::OwningPtr<CheckerManager> checkerMgr;
97  llvm::OwningPtr<AnalysisManager> Mgr;
98
99  AnalysisConsumer(const Preprocessor& pp,
100                   const std::string& outdir,
101                   const AnalyzerOptions& opts)
102    : Ctx(0), PP(pp), OutDir(outdir),
103      Opts(opts), PD(0) {
104    DigestAnalyzerOptions();
105  }
106
107  void DigestAnalyzerOptions() {
108    // Create the PathDiagnosticClient.
109    if (!OutDir.empty()) {
110      switch (Opts.AnalysisDiagOpt) {
111      default:
112#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
113        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
114#include "clang/Frontend/Analyses.def"
115      }
116    } else if (Opts.AnalysisDiagOpt == PD_TEXT) {
117      // Create the text client even without a specified output file since
118      // it just uses diagnostic notes.
119      PD = createTextPathDiagnosticClient("", PP);
120    }
121
122    // Create the analyzer component creators.
123    switch (Opts.AnalysisStoreOpt) {
124    default:
125      assert(0 && "Unknown store manager.");
126#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
127      case NAME##Model: CreateStoreMgr = CREATEFN; break;
128#include "clang/Frontend/Analyses.def"
129    }
130
131    switch (Opts.AnalysisConstraintsOpt) {
132    default:
133      assert(0 && "Unknown store manager.");
134#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
135      case NAME##Model: CreateConstraintMgr = CREATEFN; break;
136#include "clang/Frontend/Analyses.def"
137    }
138  }
139
140  void DisplayFunction(const Decl *D) {
141    if (!Opts.AnalyzerDisplayProgress)
142      return;
143
144    SourceManager &SM = Mgr->getASTContext().getSourceManager();
145    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
146    if (Loc.isValid()) {
147      llvm::errs() << "ANALYZE: " << Loc.getFilename();
148
149      if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
150        const NamedDecl *ND = cast<NamedDecl>(D);
151        llvm::errs() << ' ' << ND << '\n';
152      }
153      else if (isa<BlockDecl>(D)) {
154        llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
155                     << Loc.getColumn() << '\n';
156      }
157      else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
158        Selector S = MD->getSelector();
159        llvm::errs() << ' ' << S.getAsString();
160      }
161    }
162  }
163
164  void addCodeAction(CodeAction action) {
165    FunctionActions.push_back(action);
166    ObjCMethodActions.push_back(action);
167    CXXMethodActions.push_back(action);
168  }
169
170  void addObjCImplementationAction(CodeAction action) {
171    ObjCImplementationActions.push_back(action);
172  }
173
174  virtual void Initialize(ASTContext &Context) {
175    Ctx = &Context;
176    checkerMgr.reset(registerCheckers(Opts, PP.getLangOptions(),
177                                      PP.getDiagnostics()));
178    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
179                                  PP.getLangOptions(), PD,
180                                  CreateStoreMgr, CreateConstraintMgr,
181                                  checkerMgr.get(),
182                                  /* Indexer */ 0,
183                                  Opts.MaxNodes, Opts.MaxLoop,
184                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
185                                  Opts.PurgeDead, Opts.EagerlyAssume,
186                                  Opts.TrimGraph, Opts.InlineCall,
187                                  Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
188                                  Opts.CFGAddInitializers,
189                                  Opts.EagerlyTrimEGraph));
190  }
191
192  virtual void HandleTranslationUnit(ASTContext &C);
193  void HandleDeclContext(ASTContext &C, DeclContext *dc);
194
195  void HandleCode(Decl *D, Actions& actions);
196};
197} // end anonymous namespace
198
199//===----------------------------------------------------------------------===//
200// AnalysisConsumer implementation.
201//===----------------------------------------------------------------------===//
202
203void AnalysisConsumer::HandleDeclContext(ASTContext &C, DeclContext *dc) {
204  BugReporter BR(*Mgr);
205  for (DeclContext::decl_iterator I = dc->decls_begin(), E = dc->decls_end();
206       I != E; ++I) {
207    Decl *D = *I;
208    checkerMgr->runCheckersOnASTDecl(D, *Mgr, BR);
209
210    switch (D->getKind()) {
211      case Decl::Namespace: {
212        HandleDeclContext(C, cast<NamespaceDecl>(D));
213        break;
214      }
215      case Decl::CXXConstructor:
216      case Decl::CXXDestructor:
217      case Decl::CXXConversion:
218      case Decl::CXXMethod:
219      case Decl::Function: {
220        FunctionDecl* FD = cast<FunctionDecl>(D);
221        // We skip function template definitions, as their semantics is
222        // only determined when they are instantiated.
223        if (FD->isThisDeclarationADefinition() &&
224            !FD->isDependentContext()) {
225          if (!Opts.AnalyzeSpecificFunction.empty() &&
226              FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
227            break;
228          DisplayFunction(FD);
229          HandleCode(FD, FunctionActions);
230        }
231        break;
232      }
233
234      case Decl::ObjCImplementation: {
235        ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I);
236        HandleCode(ID, ObjCImplementationActions);
237
238        for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(),
239             ME = ID->meth_end(); MI != ME; ++MI) {
240          if ((*MI)->isThisDeclarationADefinition()) {
241            if (!Opts.AnalyzeSpecificFunction.empty() &&
242                Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString())
243              break;
244            DisplayFunction(*MI);
245            HandleCode(*MI, ObjCMethodActions);
246          }
247        }
248        break;
249      }
250
251      default:
252        break;
253    }
254  }
255}
256
257void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
258  BugReporter BR(*Mgr);
259  TranslationUnitDecl *TU = C.getTranslationUnitDecl();
260  checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
261  HandleDeclContext(C, TU);
262
263  // Explicitly destroy the PathDiagnosticClient.  This will flush its output.
264  // FIXME: This should be replaced with something that doesn't rely on
265  // side-effects in PathDiagnosticClient's destructor. This is required when
266  // used with option -disable-free.
267  Mgr.reset(NULL);
268}
269
270static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
271  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
272    WL.push_back(BD);
273
274  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
275       I!=E; ++I)
276    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
277      FindBlocks(DC, WL);
278}
279
280void AnalysisConsumer::HandleCode(Decl *D, Actions& actions) {
281
282  // Don't run the actions if an error has occured with parsing the file.
283  Diagnostic &Diags = PP.getDiagnostics();
284  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
285    return;
286
287  // Don't run the actions on declarations in header files unless
288  // otherwise specified.
289  SourceManager &SM = Ctx->getSourceManager();
290  SourceLocation SL = SM.getInstantiationLoc(D->getLocation());
291  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
292    return;
293
294  // Clear the AnalysisManager of old AnalysisContexts.
295  Mgr->ClearContexts();
296
297  // Dispatch on the actions.
298  llvm::SmallVector<Decl*, 10> WL;
299  WL.push_back(D);
300
301  if (D->hasBody() && Opts.AnalyzeNestedBlocks)
302    FindBlocks(cast<DeclContext>(D), WL);
303
304  BugReporter BR(*Mgr);
305  for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
306       WI != WE; ++WI)
307    if ((*WI)->hasBody())
308      checkerMgr->runCheckersOnASTBody(*WI, *Mgr, BR);
309
310  for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I)
311    for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
312         WI != WE; ++WI)
313      (*I)(*this, *Mgr, *WI);
314}
315
316//===----------------------------------------------------------------------===//
317// Analyses
318//===----------------------------------------------------------------------===//
319
320static void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr,
321                                 Decl *D) {
322  if (CFG* c = mgr.getCFG(D)) {
323    CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic());
324  }
325}
326
327
328static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
329                               Decl *D,
330                               TransferFuncs* tf) {
331
332  llvm::OwningPtr<TransferFuncs> TF(tf);
333
334  // Construct the analysis engine.  We first query for the LiveVariables
335  // information to see if the CFG is valid.
336  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
337  if (!mgr.getLiveVariables(D))
338    return;
339  ExprEngine Eng(mgr, TF.take());
340
341  RegisterNSErrorChecks(Eng.getBugReporter(), Eng, *D);
342
343  if (C.Opts.BufferOverflows)
344    RegisterArrayBoundCheckerV2(Eng);
345
346  // Set the graph auditor.
347  llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
348  if (mgr.shouldVisualizeUbigraph()) {
349    Auditor.reset(CreateUbiViz());
350    ExplodedNode::SetAuditor(Auditor.get());
351  }
352
353  // Execute the worklist algorithm.
354  Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes());
355
356  // Release the auditor (if any) so that it doesn't monitor the graph
357  // created BugReporter.
358  ExplodedNode::SetAuditor(0);
359
360  // Visualize the exploded graph.
361  if (mgr.shouldVisualizeGraphviz())
362    Eng.ViewGraph(mgr.shouldTrimGraph());
363
364  // Display warnings.
365  Eng.getBugReporter().FlushReports();
366}
367
368static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr,
369                                  Decl *D, bool GCEnabled) {
370
371  TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(),
372                                         GCEnabled,
373                                         mgr.getLangOptions());
374
375  ActionExprEngine(C, mgr, D, TF);
376}
377
378static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
379                               Decl *D) {
380
381 switch (mgr.getLangOptions().getGCMode()) {
382 default:
383   assert (false && "Invalid GC mode.");
384 case LangOptions::NonGC:
385   ActionObjCMemCheckerAux(C, mgr, D, false);
386   break;
387
388 case LangOptions::GCOnly:
389   ActionObjCMemCheckerAux(C, mgr, D, true);
390   break;
391
392 case LangOptions::HybridGC:
393   ActionObjCMemCheckerAux(C, mgr, D, false);
394   ActionObjCMemCheckerAux(C, mgr, D, true);
395   break;
396 }
397}
398
399//===----------------------------------------------------------------------===//
400// AnalysisConsumer creation.
401//===----------------------------------------------------------------------===//
402
403ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp,
404                                           const std::string& OutDir,
405                                           const AnalyzerOptions& Opts) {
406  llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts));
407
408  for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i)
409    switch (Opts.AnalysisList[i]) {
410#define ANALYSIS(NAME, CMD, DESC, SCOPE)\
411    case NAME:\
412      C->add ## SCOPE ## Action(&Action ## NAME);\
413      break;
414#include "clang/Frontend/Analyses.def"
415    default: break;
416    }
417
418  // Last, disable the effects of '-Werror' when using the AnalysisConsumer.
419  pp.getDiagnostics().setWarningsAsErrors(false);
420
421  return C.take();
422}
423
424//===----------------------------------------------------------------------===//
425// Ubigraph Visualization.  FIXME: Move to separate file.
426//===----------------------------------------------------------------------===//
427
428namespace {
429
430class UbigraphViz : public ExplodedNode::Auditor {
431  llvm::OwningPtr<llvm::raw_ostream> Out;
432  llvm::sys::Path Dir, Filename;
433  unsigned Cntr;
434
435  typedef llvm::DenseMap<void*,unsigned> VMap;
436  VMap M;
437
438public:
439  UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
440              llvm::sys::Path& filename);
441
442  ~UbigraphViz();
443
444  virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst);
445};
446
447} // end anonymous namespace
448
449static ExplodedNode::Auditor* CreateUbiViz() {
450  std::string ErrMsg;
451
452  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
453  if (!ErrMsg.empty())
454    return 0;
455
456  llvm::sys::Path Filename = Dir;
457  Filename.appendComponent("llvm_ubi");
458  Filename.makeUnique(true,&ErrMsg);
459
460  if (!ErrMsg.empty())
461    return 0;
462
463  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
464
465  llvm::OwningPtr<llvm::raw_fd_ostream> Stream;
466  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
467
468  if (!ErrMsg.empty())
469    return 0;
470
471  return new UbigraphViz(Stream.take(), Dir, Filename);
472}
473
474void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
475
476  assert (Src != Dst && "Self-edges are not allowed.");
477
478  // Lookup the Src.  If it is a new node, it's a root.
479  VMap::iterator SrcI= M.find(Src);
480  unsigned SrcID;
481
482  if (SrcI == M.end()) {
483    M[Src] = SrcID = Cntr++;
484    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
485  }
486  else
487    SrcID = SrcI->second;
488
489  // Lookup the Dst.
490  VMap::iterator DstI= M.find(Dst);
491  unsigned DstID;
492
493  if (DstI == M.end()) {
494    M[Dst] = DstID = Cntr++;
495    *Out << "('vertex', " << DstID << ")\n";
496  }
497  else {
498    // We have hit DstID before.  Change its style to reflect a cache hit.
499    DstID = DstI->second;
500    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
501  }
502
503  // Add the edge.
504  *Out << "('edge', " << SrcID << ", " << DstID
505       << ", ('arrow','true'), ('oriented', 'true'))\n";
506}
507
508UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
509                         llvm::sys::Path& filename)
510  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
511
512  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
513  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
514          " ('size', '1.5'))\n";
515}
516
517UbigraphViz::~UbigraphViz() {
518  Out.reset(0);
519  llvm::errs() << "Running 'ubiviz' program... ";
520  std::string ErrMsg;
521  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
522  std::vector<const char*> args;
523  args.push_back(Ubiviz.c_str());
524  args.push_back(Filename.c_str());
525  args.push_back(0);
526
527  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
528    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
529  }
530
531  // Delete the directory.
532  Dir.eraseFromDisk(true);
533}
534