Break Frontend's dependency on Rewrite, Checker and CodeGen in shared library configuration

Currently, all AST consumers are located in the Frontend library,
meaning that in a shared library configuration, Frontend has a
dependency on Rewrite, Checker and CodeGen.  This is suboptimal for
clients which only wish to make use of the frontend.  CodeGen in
particular introduces a large number of unwanted dependencies.

This patch breaks the dependency by moving all AST consumers with
dependencies on Rewrite, Checker and/or CodeGen to their respective
libraries.  The patch therefore introduces dependencies in the other
direction (i.e. from Rewrite, Checker and CodeGen to Frontend).

After applying this patch, Clang builds correctly using CMake and
shared libraries ("cmake -DBUILD_SHARED_LIBS=ON").

N.B. This patch includes file renames which are indicated in the
patch body.

Changes in this revision of the patch:
 - Fixed some copy-paste mistakes in the header files
 - Modified certain aspects of the coding to comply with the LLVM
   Coding Standards

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106010 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Checker/AnalysisConsumer.cpp b/lib/Checker/AnalysisConsumer.cpp
new file mode 100644
index 0000000..4cdfd31
--- /dev/null
+++ b/lib/Checker/AnalysisConsumer.cpp
@@ -0,0 +1,589 @@
+//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// "Meta" ASTConsumer for running different source analyses.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/AnalysisConsumer.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/Analyses/UninitializedValues.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Checker/Checkers/LocalCheckers.h"
+#include "clang/Checker/ManagerRegistry.h"
+#include "clang/Checker/BugReporter/PathDiagnostic.h"
+#include "clang/Checker/PathSensitive/AnalysisManager.h"
+#include "clang/Checker/BugReporter/BugReporter.h"
+#include "clang/Checker/PathSensitive/GRExprEngine.h"
+#include "clang/Checker/PathSensitive/GRTransferFuncs.h"
+#include "clang/Checker/PathDiagnosticClients.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/AnalyzerOptions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Path.h"
+#include "llvm/System/Program.h"
+#include "llvm/ADT/OwningPtr.h"
+
+using namespace clang;
+
+static ExplodedNode::Auditor* CreateUbiViz();
+
+//===----------------------------------------------------------------------===//
+// Special PathDiagnosticClients.
+//===----------------------------------------------------------------------===//
+
+static PathDiagnosticClient*
+CreatePlistHTMLDiagnosticClient(const std::string& prefix,
+                                const Preprocessor &PP) {
+  llvm::sys::Path F(prefix);
+  PathDiagnosticClient *PD = CreateHTMLDiagnosticClient(F.getDirname(), PP);
+  return CreatePlistDiagnosticClient(prefix, PP, PD);
+}
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer declaration.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class AnalysisConsumer : public ASTConsumer {
+public:
+  typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D);
+  typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M,
+                           TranslationUnitDecl &TU);
+
+private:
+  typedef std::vector<CodeAction> Actions;
+  typedef std::vector<TUAction> TUActions;
+
+  Actions FunctionActions;
+  Actions ObjCMethodActions;
+  Actions ObjCImplementationActions;
+  Actions CXXMethodActions;
+  TUActions TranslationUnitActions; // Remove this.
+
+public:
+  ASTContext* Ctx;
+  const Preprocessor &PP;
+  const std::string OutDir;
+  AnalyzerOptions Opts;
+  bool declDisplayed;
+
+
+  // PD is owned by AnalysisManager.
+  PathDiagnosticClient *PD;
+
+  StoreManagerCreator CreateStoreMgr;
+  ConstraintManagerCreator CreateConstraintMgr;
+
+  llvm::OwningPtr<AnalysisManager> Mgr;
+
+  AnalysisConsumer(const Preprocessor& pp,
+                   const std::string& outdir,
+                   const AnalyzerOptions& opts)
+    : Ctx(0), PP(pp), OutDir(outdir),
+      Opts(opts), declDisplayed(false), PD(0) {
+    DigestAnalyzerOptions();
+  }
+
+  void DigestAnalyzerOptions() {
+    // Create the PathDiagnosticClient.
+    if (!OutDir.empty()) {
+      switch (Opts.AnalysisDiagOpt) {
+      default:
+#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \
+        case PD_##NAME: PD = CREATEFN(OutDir, PP); break;
+#include "clang/Frontend/Analyses.def"
+      }
+    }
+
+    // Create the analyzer component creators.
+    if (ManagerRegistry::StoreMgrCreator != 0) {
+      CreateStoreMgr = ManagerRegistry::StoreMgrCreator;
+    }
+    else {
+      switch (Opts.AnalysisStoreOpt) {
+      default:
+        assert(0 && "Unknown store manager.");
+#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN)           \
+        case NAME##Model: CreateStoreMgr = CREATEFN; break;
+#include "clang/Frontend/Analyses.def"
+      }
+    }
+
+    if (ManagerRegistry::ConstraintMgrCreator != 0)
+      CreateConstraintMgr = ManagerRegistry::ConstraintMgrCreator;
+    else {
+      switch (Opts.AnalysisConstraintsOpt) {
+      default:
+        assert(0 && "Unknown store manager.");
+#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN)     \
+        case NAME##Model: CreateConstraintMgr = CREATEFN; break;
+#include "clang/Frontend/Analyses.def"
+      }
+    }
+  }
+
+  void DisplayFunction(const Decl *D) {
+    if (!Opts.AnalyzerDisplayProgress || declDisplayed)
+      return;
+
+    declDisplayed = true;
+    SourceManager &SM = Mgr->getASTContext().getSourceManager();
+    PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
+    llvm::errs() << "ANALYZE: " << Loc.getFilename();
+
+    if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
+      const NamedDecl *ND = cast<NamedDecl>(D);
+      llvm::errs() << ' ' << ND << '\n';
+    }
+    else if (isa<BlockDecl>(D)) {
+      llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:"
+                   << Loc.getColumn() << '\n';
+    }
+  }
+
+  void addCodeAction(CodeAction action) {
+    FunctionActions.push_back(action);
+    ObjCMethodActions.push_back(action);
+    CXXMethodActions.push_back(action);
+  }
+
+  void addTranslationUnitAction(TUAction action) {
+    TranslationUnitActions.push_back(action);
+  }
+
+  void addObjCImplementationAction(CodeAction action) {
+    ObjCImplementationActions.push_back(action);
+  }
+
+  virtual void Initialize(ASTContext &Context) {
+    Ctx = &Context;
+    Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(),
+                                  PP.getLangOptions(), PD,
+                                  CreateStoreMgr, CreateConstraintMgr,
+                                  Opts.MaxNodes, Opts.MaxLoop,
+                                  Opts.VisualizeEGDot, Opts.VisualizeEGUbi,
+                                  Opts.PurgeDead, Opts.EagerlyAssume,
+                                  Opts.TrimGraph, Opts.InlineCall));
+  }
+
+  virtual void HandleTranslationUnit(ASTContext &C);
+  void HandleCode(Decl *D, Stmt* Body, Actions& actions);
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer implementation.
+//===----------------------------------------------------------------------===//
+
+void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
+
+  TranslationUnitDecl *TU = C.getTranslationUnitDecl();
+
+  for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end();
+       I != E; ++I) {
+    Decl *D = *I;
+
+    switch (D->getKind()) {
+    case Decl::CXXConstructor:
+    case Decl::CXXDestructor:
+    case Decl::CXXConversion:
+    case Decl::CXXMethod:
+    case Decl::Function: {
+      FunctionDecl* FD = cast<FunctionDecl>(D);
+      
+      if (FD->isThisDeclarationADefinition()) {
+        if (!Opts.AnalyzeSpecificFunction.empty() &&
+            FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction)
+          break;
+        HandleCode(FD, FD->getBody(), FunctionActions);
+      }
+      break;
+    }
+
+    case Decl::ObjCMethod: {
+      ObjCMethodDecl* MD = cast<ObjCMethodDecl>(D);
+      
+      if (MD->isThisDeclarationADefinition()) {
+        if (!Opts.AnalyzeSpecificFunction.empty() &&
+            Opts.AnalyzeSpecificFunction != MD->getSelector().getAsString())
+          break;
+        HandleCode(MD, MD->getBody(), ObjCMethodActions);
+      }
+      break;
+    }
+
+    case Decl::ObjCImplementation: {
+      ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I);
+      HandleCode(ID, 0, ObjCImplementationActions);
+
+      for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(), 
+             ME = ID->meth_end(); MI != ME; ++MI) {
+        if ((*MI)->isThisDeclarationADefinition()) {
+          if (!Opts.AnalyzeSpecificFunction.empty() &&
+             Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString())
+            break;
+          HandleCode(*MI, (*MI)->getBody(), ObjCMethodActions);
+        }
+      }
+      break;
+    }
+
+    default:
+      break;
+    }
+  }
+
+  for (TUActions::iterator I = TranslationUnitActions.begin(),
+                           E = TranslationUnitActions.end(); I != E; ++I) {
+    (*I)(*this, *Mgr, *TU);
+  }
+
+  // Explicitly destroy the PathDiagnosticClient.  This will flush its output.
+  // FIXME: This should be replaced with something that doesn't rely on
+  // side-effects in PathDiagnosticClient's destructor. This is required when
+  // used with option -disable-free.
+  Mgr.reset(NULL);
+}
+
+static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) {
+  if (BlockDecl *BD = dyn_cast<BlockDecl>(D))
+    WL.push_back(BD);
+
+  for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end();
+       I!=E; ++I)
+    if (DeclContext *DC = dyn_cast<DeclContext>(*I))
+      FindBlocks(DC, WL);
+}
+
+void AnalysisConsumer::HandleCode(Decl *D, Stmt* Body, Actions& actions) {
+
+  // Don't run the actions if an error has occured with parsing the file.
+  Diagnostic &Diags = PP.getDiagnostics();
+  if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
+    return;
+
+  // Don't run the actions on declarations in header files unless
+  // otherwise specified.
+  SourceManager &SM = Ctx->getSourceManager();
+  SourceLocation SL = SM.getInstantiationLoc(D->getLocation());
+  if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL))
+    return;
+
+  // Clear the AnalysisManager of old AnalysisContexts.
+  Mgr->ClearContexts();
+
+  // Dispatch on the actions.
+  llvm::SmallVector<Decl*, 10> WL;
+  WL.push_back(D);
+
+  if (Body && Opts.AnalyzeNestedBlocks)
+    FindBlocks(cast<DeclContext>(D), WL);
+
+  for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I)
+    for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end();
+         WI != WE; ++WI)
+      (*I)(*this, *Mgr, *WI);
+}
+
+//===----------------------------------------------------------------------===//
+// Analyses
+//===----------------------------------------------------------------------===//
+
+static void ActionWarnDeadStores(AnalysisConsumer &C, AnalysisManager& mgr,
+                                 Decl *D) {
+  if (LiveVariables *L = mgr.getLiveVariables(D)) {
+    BugReporter BR(mgr);
+    CheckDeadStores(*mgr.getCFG(D), *L, mgr.getParentMap(D), BR);
+  }
+}
+
+static void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr,
+                                 Decl *D) {
+  if (CFG* c = mgr.getCFG(D)) {
+    CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic());
+  }
+}
+
+
+static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr,
+                               Decl *D,
+                               GRTransferFuncs* tf) {
+
+  llvm::OwningPtr<GRTransferFuncs> TF(tf);
+
+  // Construct the analysis engine.  We first query for the LiveVariables
+  // information to see if the CFG is valid.
+  // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
+  if (!mgr.getLiveVariables(D))
+    return;
+  GRExprEngine Eng(mgr, TF.take());
+
+  if (C.Opts.EnableExperimentalInternalChecks)
+    RegisterExperimentalInternalChecks(Eng);
+
+  RegisterAppleChecks(Eng, *D);
+
+  if (C.Opts.EnableExperimentalChecks)
+    RegisterExperimentalChecks(Eng);
+
+  // Set the graph auditor.
+  llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
+  if (mgr.shouldVisualizeUbigraph()) {
+    Auditor.reset(CreateUbiViz());
+    ExplodedNode::SetAuditor(Auditor.get());
+  }
+
+  // Execute the worklist algorithm.
+  Eng.ExecuteWorkList(mgr.getStackFrame(D), mgr.getMaxNodes());
+
+  // Release the auditor (if any) so that it doesn't monitor the graph
+  // created BugReporter.
+  ExplodedNode::SetAuditor(0);
+
+  // Visualize the exploded graph.
+  if (mgr.shouldVisualizeGraphviz())
+    Eng.ViewGraph(mgr.shouldTrimGraph());
+
+  // Display warnings.
+  Eng.getBugReporter().FlushReports();
+}
+
+static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr,
+                                  Decl *D, bool GCEnabled) {
+
+  GRTransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(),
+                                         GCEnabled,
+                                         mgr.getLangOptions());
+
+  ActionGRExprEngine(C, mgr, D, TF);
+}
+
+static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr,
+                               Decl *D) {
+
+ switch (mgr.getLangOptions().getGCMode()) {
+ default:
+   assert (false && "Invalid GC mode.");
+ case LangOptions::NonGC:
+   ActionObjCMemCheckerAux(C, mgr, D, false);
+   break;
+
+ case LangOptions::GCOnly:
+   ActionObjCMemCheckerAux(C, mgr, D, true);
+   break;
+
+ case LangOptions::HybridGC:
+   ActionObjCMemCheckerAux(C, mgr, D, false);
+   ActionObjCMemCheckerAux(C, mgr, D, true);
+   break;
+ }
+}
+
+static void ActionDisplayLiveVariables(AnalysisConsumer &C,
+                                       AnalysisManager& mgr, Decl *D) {
+  if (LiveVariables* L = mgr.getLiveVariables(D)) {
+    L->dumpBlockLiveness(mgr.getSourceManager());
+  }
+}
+
+static void ActionCFGDump(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) {
+  if (CFG *cfg = mgr.getCFG(D)) {
+    cfg->dump(mgr.getLangOptions());
+  }
+}
+
+static void ActionCFGView(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) {
+  if (CFG *cfg = mgr.getCFG(D)) {
+    cfg->viewCFG(mgr.getLangOptions());
+  }
+}
+
+static void ActionSecuritySyntacticChecks(AnalysisConsumer &C,
+                                          AnalysisManager &mgr, Decl *D) {
+  BugReporter BR(mgr);
+  CheckSecuritySyntaxOnly(D, BR);
+}
+
+static void ActionLLVMConventionChecker(AnalysisConsumer &C,
+                                        AnalysisManager &mgr,
+                                        TranslationUnitDecl &TU) {
+  BugReporter BR(mgr);
+  CheckLLVMConventions(TU, BR);
+}
+
+static void ActionWarnObjCDealloc(AnalysisConsumer &C, AnalysisManager& mgr,
+                                  Decl *D) {
+  if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly)
+    return;
+  BugReporter BR(mgr);
+  CheckObjCDealloc(cast<ObjCImplementationDecl>(D), mgr.getLangOptions(), BR);
+}
+
+static void ActionWarnObjCUnusedIvars(AnalysisConsumer &C, AnalysisManager& mgr,
+                                      Decl *D) {
+  BugReporter BR(mgr);
+  CheckObjCUnusedIvar(cast<ObjCImplementationDecl>(D), BR);
+}
+
+static void ActionWarnObjCMethSigs(AnalysisConsumer &C, AnalysisManager& mgr,
+                                   Decl *D) {
+  BugReporter BR(mgr);
+  CheckObjCInstMethSignature(cast<ObjCImplementationDecl>(D), BR);
+}
+
+static void ActionWarnSizeofPointer(AnalysisConsumer &C, AnalysisManager &mgr,
+                                    Decl *D) {
+  BugReporter BR(mgr);
+  CheckSizeofPointer(D, BR);
+}
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer creation.
+//===----------------------------------------------------------------------===//
+
+ASTConsumer* clang::CreateAnalysisConsumer(const Preprocessor& pp,
+                                           const std::string& OutDir,
+                                           const AnalyzerOptions& Opts) {
+  llvm::OwningPtr<AnalysisConsumer> C(new AnalysisConsumer(pp, OutDir, Opts));
+
+  for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i)
+    switch (Opts.AnalysisList[i]) {
+#define ANALYSIS(NAME, CMD, DESC, SCOPE)\
+    case NAME:\
+      C->add ## SCOPE ## Action(&Action ## NAME);\
+      break;
+#include "clang/Frontend/Analyses.def"
+    default: break;
+    }
+
+  // Last, disable the effects of '-Werror' when using the AnalysisConsumer.
+  pp.getDiagnostics().setWarningsAsErrors(false);
+
+  return C.take();
+}
+
+//===----------------------------------------------------------------------===//
+// Ubigraph Visualization.  FIXME: Move to separate file.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class UbigraphViz : public ExplodedNode::Auditor {
+  llvm::OwningPtr<llvm::raw_ostream> Out;
+  llvm::sys::Path Dir, Filename;
+  unsigned Cntr;
+
+  typedef llvm::DenseMap<void*,unsigned> VMap;
+  VMap M;
+
+public:
+  UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
+              llvm::sys::Path& filename);
+
+  ~UbigraphViz();
+
+  virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst);
+};
+
+} // end anonymous namespace
+
+static ExplodedNode::Auditor* CreateUbiViz() {
+  std::string ErrMsg;
+
+  llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg);
+  if (!ErrMsg.empty())
+    return 0;
+
+  llvm::sys::Path Filename = Dir;
+  Filename.appendComponent("llvm_ubi");
+  Filename.makeUnique(true,&ErrMsg);
+
+  if (!ErrMsg.empty())
+    return 0;
+
+  llvm::errs() << "Writing '" << Filename.str() << "'.\n";
+
+  llvm::OwningPtr<llvm::raw_fd_ostream> Stream;
+  Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg));
+
+  if (!ErrMsg.empty())
+    return 0;
+
+  return new UbigraphViz(Stream.take(), Dir, Filename);
+}
+
+void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) {
+
+  assert (Src != Dst && "Self-edges are not allowed.");
+
+  // Lookup the Src.  If it is a new node, it's a root.
+  VMap::iterator SrcI= M.find(Src);
+  unsigned SrcID;
+
+  if (SrcI == M.end()) {
+    M[Src] = SrcID = Cntr++;
+    *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n";
+  }
+  else
+    SrcID = SrcI->second;
+
+  // Lookup the Dst.
+  VMap::iterator DstI= M.find(Dst);
+  unsigned DstID;
+
+  if (DstI == M.end()) {
+    M[Dst] = DstID = Cntr++;
+    *Out << "('vertex', " << DstID << ")\n";
+  }
+  else {
+    // We have hit DstID before.  Change its style to reflect a cache hit.
+    DstID = DstI->second;
+    *Out << "('change_vertex_style', " << DstID << ", 1)\n";
+  }
+
+  // Add the edge.
+  *Out << "('edge', " << SrcID << ", " << DstID
+       << ", ('arrow','true'), ('oriented', 'true'))\n";
+}
+
+UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir,
+                         llvm::sys::Path& filename)
+  : Out(out), Dir(dir), Filename(filename), Cntr(0) {
+
+  *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n";
+  *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66'),"
+          " ('size', '1.5'))\n";
+}
+
+UbigraphViz::~UbigraphViz() {
+  Out.reset(0);
+  llvm::errs() << "Running 'ubiviz' program... ";
+  std::string ErrMsg;
+  llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz");
+  std::vector<const char*> args;
+  args.push_back(Ubiviz.c_str());
+  args.push_back(Filename.c_str());
+  args.push_back(0);
+
+  if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) {
+    llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
+  }
+
+  // Delete the directory.
+  Dir.eraseFromDisk(true);
+}
diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt
index e912024..620dab2 100644
--- a/lib/Checker/CMakeLists.txt
+++ b/lib/Checker/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangChecker
   AdjustedReturnValueChecker.cpp
   AggExprVisitor.cpp
+  AnalysisConsumer.cpp
   ArrayBoundChecker.cpp
   AttrNonNullChecker.cpp
   BasicConstraintManager.cpp
@@ -30,12 +31,14 @@
   ExplodedGraph.cpp
   FixedAddressChecker.cpp
   FlatStore.cpp
+  FrontendActions.cpp
   GRBlockCounter.cpp
   GRCoreEngine.cpp
   GRCXXExprEngine.cpp
   GRExprEngine.cpp
   GRExprEngineExperimentalChecks.cpp
   GRState.cpp
+  HTMLDiagnostics.cpp
   LLVMConventionsChecker.cpp
   MacOSXAPIChecker.cpp
   MallocChecker.cpp
@@ -47,6 +50,7 @@
   ObjCUnusedIVarsChecker.cpp
   OSAtomicChecker.cpp
   PathDiagnostic.cpp
+  PlistDiagnostics.cpp
   PointerArithChecker.cpp
   PointerSubChecker.cpp
   PthreadLockChecker.cpp
diff --git a/lib/Checker/FrontendActions.cpp b/lib/Checker/FrontendActions.cpp
new file mode 100644
index 0000000..d9a54a0
--- /dev/null
+++ b/lib/Checker/FrontendActions.cpp
@@ -0,0 +1,21 @@
+//===--- FrontendActions.cpp ----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/FrontendActions.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Checker/AnalysisConsumer.h"
+using namespace clang;
+
+ASTConsumer *AnalysisAction::CreateASTConsumer(CompilerInstance &CI,
+                                               llvm::StringRef InFile) {
+  return CreateAnalysisConsumer(CI.getPreprocessor(),
+                                CI.getFrontendOpts().OutputFile,
+                                CI.getAnalyzerOpts());
+}
+
diff --git a/lib/Checker/HTMLDiagnostics.cpp b/lib/Checker/HTMLDiagnostics.cpp
new file mode 100644
index 0000000..401f177
--- /dev/null
+++ b/lib/Checker/HTMLDiagnostics.cpp
@@ -0,0 +1,577 @@
+//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the HTMLDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/PathDiagnosticClients.h"
+#include "clang/Checker/BugReporter/PathDiagnostic.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Rewrite/HTMLRewrite.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/System/Path.h"
+
+using namespace clang;
+
+//===----------------------------------------------------------------------===//
+// Boilerplate.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class HTMLDiagnostics : public PathDiagnosticClient {
+  llvm::sys::Path Directory, FilePrefix;
+  bool createdDir, noDir;
+  const Preprocessor &PP;
+  std::vector<const PathDiagnostic*> BatchedDiags;
+public:
+  HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp);
+
+  virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); }
+
+  virtual void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade);
+
+  virtual void HandlePathDiagnostic(const PathDiagnostic* D);
+
+  virtual llvm::StringRef getName() const {
+    return "HTMLDiagnostics";
+  }
+
+  unsigned ProcessMacroPiece(llvm::raw_ostream& os,
+                             const PathDiagnosticMacroPiece& P,
+                             unsigned num);
+
+  void HandlePiece(Rewriter& R, FileID BugFileID,
+                   const PathDiagnosticPiece& P, unsigned num, unsigned max);
+
+  void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+                      const char *HighlightStart = "<span class=\"mrange\">",
+                      const char *HighlightEnd = "</span>");
+
+  void ReportDiag(const PathDiagnostic& D,
+                  llvm::SmallVectorImpl<std::string> *FilesMade);
+};
+
+} // end anonymous namespace
+
+HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix,
+                                 const Preprocessor &pp)
+  : Directory(prefix), FilePrefix(prefix), createdDir(false), noDir(false),
+    PP(pp) {
+  // All html files begin with "report"
+  FilePrefix.appendComponent("report");
+}
+
+PathDiagnosticClient*
+clang::CreateHTMLDiagnosticClient(const std::string& prefix,
+                                  const Preprocessor &PP) {
+  return new HTMLDiagnostics(prefix, PP);
+}
+
+//===----------------------------------------------------------------------===//
+// Report processing.
+//===----------------------------------------------------------------------===//
+
+void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
+  if (!D)
+    return;
+
+  if (D->empty()) {
+    delete D;
+    return;
+  }
+
+  const_cast<PathDiagnostic*>(D)->flattenLocations();
+  BatchedDiags.push_back(D);
+}
+
+void
+HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade)
+{
+  while (!BatchedDiags.empty()) {
+    const PathDiagnostic* D = BatchedDiags.back();
+    BatchedDiags.pop_back();
+    ReportDiag(*D, FilesMade);
+    delete D;
+  }
+
+  BatchedDiags.clear();
+}
+
+void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
+                                 llvm::SmallVectorImpl<std::string> *FilesMade){
+  // Create the HTML directory if it is missing.
+  if (!createdDir) {
+    createdDir = true;
+    std::string ErrorMsg;
+    Directory.createDirectoryOnDisk(true, &ErrorMsg);
+
+    if (!Directory.isDirectory()) {
+      llvm::errs() << "warning: could not create directory '"
+                   << Directory.str() << "'\n"
+                   << "reason: " << ErrorMsg << '\n';
+
+      noDir = true;
+
+      return;
+    }
+  }
+
+  if (noDir)
+    return;
+
+  const SourceManager &SMgr = D.begin()->getLocation().getManager();
+  FileID FID;
+
+  // Verify that the entire path is from the same FileID.
+  for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) {
+    FullSourceLoc L = I->getLocation().asLocation().getInstantiationLoc();
+
+    if (FID.isInvalid()) {
+      FID = SMgr.getFileID(L);
+    } else if (SMgr.getFileID(L) != FID)
+      return; // FIXME: Emit a warning?
+
+    // Check the source ranges.
+    for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
+                                             RE=I->ranges_end(); RI!=RE; ++RI) {
+
+      SourceLocation L = SMgr.getInstantiationLoc(RI->getBegin());
+
+      if (!L.isFileID() || SMgr.getFileID(L) != FID)
+        return; // FIXME: Emit a warning?
+
+      L = SMgr.getInstantiationLoc(RI->getEnd());
+
+      if (!L.isFileID() || SMgr.getFileID(L) != FID)
+        return; // FIXME: Emit a warning?
+    }
+  }
+
+  if (FID.isInvalid())
+    return; // FIXME: Emit a warning?
+
+  // Create a new rewriter to generate HTML.
+  Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOptions());
+
+  // Process the path.
+  unsigned n = D.size();
+  unsigned max = n;
+
+  for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend();
+        I!=E; ++I, --n)
+    HandlePiece(R, FID, *I, n, max);
+
+  // Add line numbers, header, footer, etc.
+
+  // unsigned FID = R.getSourceMgr().getMainFileID();
+  html::EscapeText(R, FID);
+  html::AddLineNumbers(R, FID);
+
+  // If we have a preprocessor, relex the file and syntax highlight.
+  // We might not have a preprocessor if we come from a deserialized AST file,
+  // for example.
+
+  html::SyntaxHighlight(R, FID, PP);
+  html::HighlightMacros(R, FID, PP);
+
+  // Get the full directory name of the analyzed file.
+
+  const FileEntry* Entry = SMgr.getFileEntryForID(FID);
+
+  // This is a cludge; basically we want to append either the full
+  // working directory if we have no directory information.  This is
+  // a work in progress.
+
+  std::string DirName = "";
+
+  if (!llvm::sys::Path(Entry->getName()).isAbsolute()) {
+    llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory();
+    DirName = P.str() + "/";
+  }
+
+  // Add the name of the file as an <h1> tag.
+
+  {
+    std::string s;
+    llvm::raw_string_ostream os(s);
+
+    os << "<!-- REPORTHEADER -->\n"
+      << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
+          "<tr><td class=\"rowname\">File:</td><td>"
+      << html::EscapeText(DirName)
+      << html::EscapeText(Entry->getName())
+      << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>"
+         "<a href=\"#EndPath\">line "
+      << (*D.rbegin()).getLocation().asLocation().getInstantiationLineNumber()
+      << ", column "
+      << (*D.rbegin()).getLocation().asLocation().getInstantiationColumnNumber()
+      << "</a></td></tr>\n"
+         "<tr><td class=\"rowname\">Description:</td><td>"
+      << D.getDescription() << "</td></tr>\n";
+
+    // Output any other meta data.
+
+    for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end();
+         I!=E; ++I) {
+      os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
+    }
+
+    os << "</table>\n<!-- REPORTSUMMARYEXTRA -->\n"
+          "<h3>Annotated Source Code</h3>\n";
+
+    R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
+  }
+
+  // Embed meta-data tags.
+  {
+    std::string s;
+    llvm::raw_string_ostream os(s);
+
+    const std::string& BugDesc = D.getDescription();
+    if (!BugDesc.empty())
+      os << "\n<!-- BUGDESC " << BugDesc << " -->\n";
+
+    const std::string& BugType = D.getBugType();
+    if (!BugType.empty())
+      os << "\n<!-- BUGTYPE " << BugType << " -->\n";
+
+    const std::string& BugCategory = D.getCategory();
+    if (!BugCategory.empty())
+      os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";
+
+    os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
+
+    os << "\n<!-- BUGLINE "
+       << D.back()->getLocation().asLocation().getInstantiationLineNumber()
+       << " -->\n";
+
+    os << "\n<!-- BUGPATHLENGTH " << D.size() << " -->\n";
+
+    // Mark the end of the tags.
+    os << "\n<!-- BUGMETAEND -->\n";
+
+    // Insert the text.
+    R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
+  }
+
+  // Add CSS, header, and footer.
+
+  html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
+
+  // Get the rewrite buffer.
+  const RewriteBuffer *Buf = R.getRewriteBufferFor(FID);
+
+  if (!Buf) {
+    llvm::errs() << "warning: no diagnostics generated for main file.\n";
+    return;
+  }
+
+  // Create a path for the target HTML file.
+  llvm::sys::Path F(FilePrefix);
+  F.makeUnique(false, NULL);
+
+  // Rename the file with an HTML extension.
+  llvm::sys::Path H(F);
+  H.appendSuffix("html");
+  F.renamePathOnDisk(H, NULL);
+
+  std::string ErrorMsg;
+  llvm::raw_fd_ostream os(H.c_str(), ErrorMsg);
+
+  if (!ErrorMsg.empty()) {
+    (llvm::errs() << "warning: could not create file '" << F.str()
+                  << "'\n").flush();
+    return;
+  }
+
+  if (FilesMade)
+    FilesMade->push_back(H.getLast());
+
+  // Emit the HTML to disk.
+  for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I)
+      os << *I;
+}
+
+void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
+                                  const PathDiagnosticPiece& P,
+                                  unsigned num, unsigned max) {
+
+  // For now, just draw a box above the line in question, and emit the
+  // warning.
+  FullSourceLoc Pos = P.getLocation().asLocation();
+
+  if (!Pos.isValid())
+    return;
+
+  SourceManager &SM = R.getSourceMgr();
+  assert(&Pos.getManager() == &SM && "SourceManagers are different!");
+  std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedInstantiationLoc(Pos);
+
+  if (LPosInfo.first != BugFileID)
+    return;
+
+  const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first);
+  const char* FileStart = Buf->getBufferStart();
+
+  // Compute the column number.  Rewind from the current position to the start
+  // of the line.
+  unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
+  const char *TokInstantiationPtr =Pos.getInstantiationLoc().getCharacterData();
+  const char *LineStart = TokInstantiationPtr-ColNo;
+
+  // Compute LineEnd.
+  const char *LineEnd = TokInstantiationPtr;
+  const char* FileEnd = Buf->getBufferEnd();
+  while (*LineEnd != '\n' && LineEnd != FileEnd)
+    ++LineEnd;
+
+  // Compute the margin offset by counting tabs and non-tabs.
+  unsigned PosNo = 0;
+  for (const char* c = LineStart; c != TokInstantiationPtr; ++c)
+    PosNo += *c == '\t' ? 8 : 1;
+
+  // Create the html for the message.
+
+  const char *Kind = 0;
+  switch (P.getKind()) {
+  case PathDiagnosticPiece::Event:  Kind = "Event"; break;
+  case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
+    // Setting Kind to "Control" is intentional.
+  case PathDiagnosticPiece::Macro: Kind = "Control"; break;
+  }
+
+  std::string sbuf;
+  llvm::raw_string_ostream os(sbuf);
+
+  os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
+
+  if (num == max)
+    os << "EndPath";
+  else
+    os << "Path" << num;
+
+  os << "\" class=\"msg";
+  if (Kind)
+    os << " msg" << Kind;
+  os << "\" style=\"margin-left:" << PosNo << "ex";
+
+  // Output a maximum size.
+  if (!isa<PathDiagnosticMacroPiece>(P)) {
+    // Get the string and determining its maximum substring.
+    const std::string& Msg = P.getString();
+    unsigned max_token = 0;
+    unsigned cnt = 0;
+    unsigned len = Msg.size();
+
+    for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I)
+      switch (*I) {
+      default:
+        ++cnt;
+        continue;
+      case ' ':
+      case '\t':
+      case '\n':
+        if (cnt > max_token) max_token = cnt;
+        cnt = 0;
+      }
+
+    if (cnt > max_token)
+      max_token = cnt;
+
+    // Determine the approximate size of the message bubble in em.
+    unsigned em;
+    const unsigned max_line = 120;
+
+    if (max_token >= max_line)
+      em = max_token / 2;
+    else {
+      unsigned characters = max_line;
+      unsigned lines = len / max_line;
+
+      if (lines > 0) {
+        for (; characters > max_token; --characters)
+          if (len / characters > lines) {
+            ++characters;
+            break;
+          }
+      }
+
+      em = characters / 2;
+    }
+
+    if (em < max_line/2)
+      os << "; max-width:" << em << "em";
+  }
+  else
+    os << "; max-width:100em";
+
+  os << "\">";
+
+  if (max > 1) {
+    os << "<table class=\"msgT\"><tr><td valign=\"top\">";
+    os << "<div class=\"PathIndex";
+    if (Kind) os << " PathIndex" << Kind;
+    os << "\">" << num << "</div>";
+    os << "</td><td>";
+  }
+
+  if (const PathDiagnosticMacroPiece *MP =
+        dyn_cast<PathDiagnosticMacroPiece>(&P)) {
+
+    os << "Within the expansion of the macro '";
+
+    // Get the name of the macro by relexing it.
+    {
+      FullSourceLoc L = MP->getLocation().asLocation().getInstantiationLoc();
+      assert(L.isFileID());
+      llvm::StringRef BufferInfo = L.getBufferData();
+      const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data();
+      Lexer rawLexer(L, PP.getLangOptions(), BufferInfo.begin(),
+                     MacroName, BufferInfo.end());
+
+      Token TheTok;
+      rawLexer.LexFromRawLexer(TheTok);
+      for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
+        os << MacroName[i];
+    }
+
+    os << "':\n";
+
+    if (max > 1)
+      os << "</td></tr></table>";
+
+    // Within a macro piece.  Write out each event.
+    ProcessMacroPiece(os, *MP, 0);
+  }
+  else {
+    os << html::EscapeText(P.getString());
+
+    if (max > 1)
+      os << "</td></tr></table>";
+  }
+
+  os << "</div></td></tr>";
+
+  // Insert the new html.
+  unsigned DisplayPos = LineEnd - FileStart;
+  SourceLocation Loc =
+    SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos);
+
+  R.InsertTextBefore(Loc, os.str());
+
+  // Now highlight the ranges.
+  for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end();
+        I != E; ++I)
+    HighlightRange(R, LPosInfo.first, *I);
+
+#if 0
+  // If there is a code insertion hint, insert that code.
+  // FIXME: This code is disabled because it seems to mangle the HTML
+  // output. I'm leaving it here because it's generally the right idea,
+  // but needs some help from someone more familiar with the rewriter.
+  for (const FixItHint *Hint = P.fixit_begin(), *HintEnd = P.fixit_end();
+       Hint != HintEnd; ++Hint) {
+    if (Hint->RemoveRange.isValid()) {
+      HighlightRange(R, LPosInfo.first, Hint->RemoveRange,
+                     "<span class=\"CodeRemovalHint\">", "</span>");
+    }
+    if (Hint->InsertionLoc.isValid()) {
+      std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true);
+      EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode
+        + "</span>";
+      R.InsertTextBefore(Hint->InsertionLoc, EscapedCode);
+    }
+  }
+#endif
+}
+
+static void EmitAlphaCounter(llvm::raw_ostream& os, unsigned n) {
+  unsigned x = n % ('z' - 'a');
+  n /= 'z' - 'a';
+
+  if (n > 0)
+    EmitAlphaCounter(os, n);
+
+  os << char('a' + x);
+}
+
+unsigned HTMLDiagnostics::ProcessMacroPiece(llvm::raw_ostream& os,
+                                            const PathDiagnosticMacroPiece& P,
+                                            unsigned num) {
+
+  for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
+        I!=E; ++I) {
+
+    if (const PathDiagnosticMacroPiece *MP =
+          dyn_cast<PathDiagnosticMacroPiece>(*I)) {
+      num = ProcessMacroPiece(os, *MP, num);
+      continue;
+    }
+
+    if (PathDiagnosticEventPiece *EP = dyn_cast<PathDiagnosticEventPiece>(*I)) {
+      os << "<div class=\"msg msgEvent\" style=\"width:94%; "
+            "margin-left:5px\">"
+            "<table class=\"msgT\"><tr>"
+            "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
+      EmitAlphaCounter(os, num++);
+      os << "</div></td><td valign=\"top\">"
+         << html::EscapeText(EP->getString())
+         << "</td></tr></table></div>\n";
+    }
+  }
+
+  return num;
+}
+
+void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
+                                     SourceRange Range,
+                                     const char *HighlightStart,
+                                     const char *HighlightEnd) {
+  SourceManager &SM = R.getSourceMgr();
+  const LangOptions &LangOpts = R.getLangOpts();
+
+  SourceLocation InstantiationStart = SM.getInstantiationLoc(Range.getBegin());
+  unsigned StartLineNo = SM.getInstantiationLineNumber(InstantiationStart);
+
+  SourceLocation InstantiationEnd = SM.getInstantiationLoc(Range.getEnd());
+  unsigned EndLineNo = SM.getInstantiationLineNumber(InstantiationEnd);
+
+  if (EndLineNo < StartLineNo)
+    return;
+
+  if (SM.getFileID(InstantiationStart) != BugFileID ||
+      SM.getFileID(InstantiationEnd) != BugFileID)
+    return;
+
+  // Compute the column number of the end.
+  unsigned EndColNo = SM.getInstantiationColumnNumber(InstantiationEnd);
+  unsigned OldEndColNo = EndColNo;
+
+  if (EndColNo) {
+    // Add in the length of the token, so that we cover multi-char tokens.
+    EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1;
+  }
+
+  // Highlight the range.  Make the span tag the outermost tag for the
+  // selected range.
+
+  SourceLocation E =
+    InstantiationEnd.getFileLocWithOffset(EndColNo - OldEndColNo);
+
+  html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
+}
diff --git a/lib/Checker/PlistDiagnostics.cpp b/lib/Checker/PlistDiagnostics.cpp
new file mode 100644
index 0000000..13accbb
--- /dev/null
+++ b/lib/Checker/PlistDiagnostics.cpp
@@ -0,0 +1,471 @@
+//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the PlistDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Checker/PathDiagnosticClients.h"
+#include "clang/Checker/BugReporter/PathDiagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+using namespace clang;
+using llvm::cast;
+
+typedef llvm::DenseMap<FileID, unsigned> FIDMap;
+
+namespace clang {
+  class Preprocessor;
+}
+
+namespace {
+struct CompareDiagnostics {
+  // Compare if 'X' is "<" than 'Y'.
+  bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const {
+    // First compare by location
+    const FullSourceLoc &XLoc = X->getLocation().asLocation();
+    const FullSourceLoc &YLoc = Y->getLocation().asLocation();
+    if (XLoc < YLoc)
+      return true;
+    if (XLoc != YLoc)
+      return false;
+    
+    // Next, compare by bug type.
+    llvm::StringRef XBugType = X->getBugType();
+    llvm::StringRef YBugType = Y->getBugType();
+    if (XBugType < YBugType)
+      return true;
+    if (XBugType != YBugType)
+      return false;
+    
+    // Next, compare by bug description.
+    llvm::StringRef XDesc = X->getDescription();
+    llvm::StringRef YDesc = Y->getDescription();
+    if (XDesc < YDesc)
+      return true;
+    if (XDesc != YDesc)
+      return false;
+    
+    // FIXME: Further refine by comparing PathDiagnosticPieces?
+    return false;    
+  }  
+};  
+}
+
+namespace {
+  class PlistDiagnostics : public PathDiagnosticClient {
+    std::vector<const PathDiagnostic*> BatchedDiags;
+    const std::string OutputFile;
+    const LangOptions &LangOpts;
+    llvm::OwningPtr<PathDiagnosticClient> SubPD;
+    bool flushed;
+  public:
+    PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts,
+                     PathDiagnosticClient *subPD);
+
+    ~PlistDiagnostics() { FlushDiagnostics(NULL); }
+
+    void FlushDiagnostics(llvm::SmallVectorImpl<std::string> *FilesMade);
+    
+    void HandlePathDiagnostic(const PathDiagnostic* D);
+    
+    virtual llvm::StringRef getName() const {
+      return "PlistDiagnostics";
+    }
+
+    PathGenerationScheme getGenerationScheme() const;
+    bool supportsLogicalOpControlFlow() const { return true; }
+    bool supportsAllBlockEdges() const { return true; }
+    virtual bool useVerboseDescription() const { return false; }
+  };
+} // end anonymous namespace
+
+PlistDiagnostics::PlistDiagnostics(const std::string& output,
+                                   const LangOptions &LO,
+                                   PathDiagnosticClient *subPD)
+  : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {}
+
+PathDiagnosticClient*
+clang::CreatePlistDiagnosticClient(const std::string& s, const Preprocessor &PP,
+                                   PathDiagnosticClient *subPD) {
+  return new PlistDiagnostics(s, PP.getLangOptions(), subPD);
+}
+
+PathDiagnosticClient::PathGenerationScheme
+PlistDiagnostics::getGenerationScheme() const {
+  if (const PathDiagnosticClient *PD = SubPD.get())
+    return PD->getGenerationScheme();
+
+  return Extensive;
+}
+
+static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl<FileID> &V,
+                   const SourceManager* SM, SourceLocation L) {
+
+  FileID FID = SM->getFileID(SM->getInstantiationLoc(L));
+  FIDMap::iterator I = FIDs.find(FID);
+  if (I != FIDs.end()) return;
+  FIDs[FID] = V.size();
+  V.push_back(FID);
+}
+
+static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM,
+                       SourceLocation L) {
+  FileID FID = SM.getFileID(SM.getInstantiationLoc(L));
+  FIDMap::const_iterator I = FIDs.find(FID);
+  assert(I != FIDs.end());
+  return I->second;
+}
+
+static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) {
+  for (unsigned i = 0; i < indent; ++i) o << ' ';
+  return o;
+}
+
+static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
+                         const LangOptions &LangOpts,
+                         SourceLocation L, const FIDMap &FM,
+                         unsigned indent, bool extend = false) {
+
+  FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast<SourceManager&>(SM));
+
+  // Add in the length of the token, so that we cover multi-char tokens.
+  unsigned offset =
+    extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0;
+
+  Indent(o, indent) << "<dict>\n";
+  Indent(o, indent) << " <key>line</key><integer>"
+                    << Loc.getInstantiationLineNumber() << "</integer>\n";
+  Indent(o, indent) << " <key>col</key><integer>"
+                    << Loc.getInstantiationColumnNumber() + offset << "</integer>\n";
+  Indent(o, indent) << " <key>file</key><integer>"
+                    << GetFID(FM, SM, Loc) << "</integer>\n";
+  Indent(o, indent) << "</dict>\n";
+}
+
+static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM,
+                         const LangOptions &LangOpts,
+                         const PathDiagnosticLocation &L, const FIDMap& FM,
+                         unsigned indent, bool extend = false) {
+  EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend);
+}
+
+static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM,
+                      const LangOptions &LangOpts,
+                      PathDiagnosticRange R, const FIDMap &FM,
+                      unsigned indent) {
+  Indent(o, indent) << "<array>\n";
+  EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1);
+  EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint);
+  Indent(o, indent) << "</array>\n";
+}
+
+static llvm::raw_ostream& EmitString(llvm::raw_ostream& o,
+                                     const std::string& s) {
+  o << "<string>";
+  for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) {
+    char c = *I;
+    switch (c) {
+    default:   o << c; break;
+    case '&':  o << "&amp;"; break;
+    case '<':  o << "&lt;"; break;
+    case '>':  o << "&gt;"; break;
+    case '\'': o << "&apos;"; break;
+    case '\"': o << "&quot;"; break;
+    }
+  }
+  o << "</string>";
+  return o;
+}
+
+static void ReportControlFlow(llvm::raw_ostream& o,
+                              const PathDiagnosticControlFlowPiece& P,
+                              const FIDMap& FM,
+                              const SourceManager &SM,
+                              const LangOptions &LangOpts,
+                              unsigned indent) {
+
+  Indent(o, indent) << "<dict>\n";
+  ++indent;
+
+  Indent(o, indent) << "<key>kind</key><string>control</string>\n";
+
+  // Emit edges.
+  Indent(o, indent) << "<key>edges</key>\n";
+  ++indent;
+  Indent(o, indent) << "<array>\n";
+  ++indent;
+  for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
+       I!=E; ++I) {
+    Indent(o, indent) << "<dict>\n";
+    ++indent;
+    Indent(o, indent) << "<key>start</key>\n";
+    EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1);
+    Indent(o, indent) << "<key>end</key>\n";
+    EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1);
+    --indent;
+    Indent(o, indent) << "</dict>\n";
+  }
+  --indent;
+  Indent(o, indent) << "</array>\n";
+  --indent;
+
+  // Output any helper text.
+  const std::string& s = P.getString();
+  if (!s.empty()) {
+    Indent(o, indent) << "<key>alternate</key>";
+    EmitString(o, s) << '\n';
+  }
+
+  --indent;
+  Indent(o, indent) << "</dict>\n";
+}
+
+static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
+                        const FIDMap& FM,
+                        const SourceManager &SM,
+                        const LangOptions &LangOpts,
+                        unsigned indent) {
+
+  Indent(o, indent) << "<dict>\n";
+  ++indent;
+
+  Indent(o, indent) << "<key>kind</key><string>event</string>\n";
+
+  // Output the location.
+  FullSourceLoc L = P.getLocation().asLocation();
+
+  Indent(o, indent) << "<key>location</key>\n";
+  EmitLocation(o, SM, LangOpts, L, FM, indent);
+
+  // Output the ranges (if any).
+  PathDiagnosticPiece::range_iterator RI = P.ranges_begin(),
+  RE = P.ranges_end();
+
+  if (RI != RE) {
+    Indent(o, indent) << "<key>ranges</key>\n";
+    Indent(o, indent) << "<array>\n";
+    ++indent;
+    for (; RI != RE; ++RI)
+      EmitRange(o, SM, LangOpts, *RI, FM, indent+1);
+    --indent;
+    Indent(o, indent) << "</array>\n";
+  }
+
+  // Output the text.
+  assert(!P.getString().empty());
+  Indent(o, indent) << "<key>extended_message</key>\n";
+  Indent(o, indent);
+  EmitString(o, P.getString()) << '\n';
+
+  // Output the short text.
+  // FIXME: Really use a short string.
+  Indent(o, indent) << "<key>message</key>\n";
+  EmitString(o, P.getString()) << '\n';
+
+  // Finish up.
+  --indent;
+  Indent(o, indent); o << "</dict>\n";
+}
+
+static void ReportMacro(llvm::raw_ostream& o,
+                        const PathDiagnosticMacroPiece& P,
+                        const FIDMap& FM, const SourceManager &SM,
+                        const LangOptions &LangOpts,
+                        unsigned indent) {
+
+  for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end();
+       I!=E; ++I) {
+
+    switch ((*I)->getKind()) {
+    default:
+      break;
+    case PathDiagnosticPiece::Event:
+      ReportEvent(o, cast<PathDiagnosticEventPiece>(**I), FM, SM, LangOpts,
+                  indent);
+      break;
+    case PathDiagnosticPiece::Macro:
+      ReportMacro(o, cast<PathDiagnosticMacroPiece>(**I), FM, SM, LangOpts,
+                  indent);
+      break;
+    }
+  }
+}
+
+static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P,
+                       const FIDMap& FM, const SourceManager &SM,
+                       const LangOptions &LangOpts) {
+
+  unsigned indent = 4;
+
+  switch (P.getKind()) {
+  case PathDiagnosticPiece::ControlFlow:
+    ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), FM, SM,
+                      LangOpts, indent);
+    break;
+  case PathDiagnosticPiece::Event:
+    ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
+                indent);
+    break;
+  case PathDiagnosticPiece::Macro:
+    ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts,
+                indent);
+    break;
+  }
+}
+
+void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) {
+  if (!D)
+    return;
+
+  if (D->empty()) {
+    delete D;
+    return;
+  }
+
+  // We need to flatten the locations (convert Stmt* to locations) because
+  // the referenced statements may be freed by the time the diagnostics
+  // are emitted.
+  const_cast<PathDiagnostic*>(D)->flattenLocations();
+  BatchedDiags.push_back(D);
+}
+
+void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl<std::string>
+                                        *FilesMade) {
+  
+  if (flushed)
+    return;
+  
+  flushed = true;
+  
+  // Sort the diagnostics so that they are always emitted in a deterministic
+  // order.
+  if (!BatchedDiags.empty())
+    std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics()); 
+
+  // Build up a set of FIDs that we use by scanning the locations and
+  // ranges of the diagnostics.
+  FIDMap FM;
+  llvm::SmallVector<FileID, 10> Fids;
+  const SourceManager* SM = 0;
+
+  if (!BatchedDiags.empty())
+    SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager();
+
+  for (std::vector<const PathDiagnostic*>::iterator DI = BatchedDiags.begin(),
+       DE = BatchedDiags.end(); DI != DE; ++DI) {
+
+    const PathDiagnostic *D = *DI;
+
+    for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) {
+      AddFID(FM, Fids, SM, I->getLocation().asLocation());
+
+      for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(),
+           RE=I->ranges_end(); RI!=RE; ++RI) {
+        AddFID(FM, Fids, SM, RI->getBegin());
+        AddFID(FM, Fids, SM, RI->getEnd());
+      }
+    }
+  }
+
+  // Open the file.
+  std::string ErrMsg;
+  llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg);
+  if (!ErrMsg.empty()) {
+    llvm::errs() << "warning: could not creat file: " << OutputFile << '\n';
+    return;
+  }
+
+  // Write the plist header.
+  o << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+  "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
+  "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+  "<plist version=\"1.0\">\n";
+
+  // Write the root object: a <dict> containing...
+  //  - "files", an <array> mapping from FIDs to file names
+  //  - "diagnostics", an <array> containing the path diagnostics
+  o << "<dict>\n"
+       " <key>files</key>\n"
+       " <array>\n";
+
+  for (llvm::SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end();
+       I!=E; ++I) {
+    o << "  ";
+    EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n';
+  }
+
+  o << " </array>\n"
+       " <key>diagnostics</key>\n"
+       " <array>\n";
+
+  for (std::vector<const PathDiagnostic*>::iterator DI=BatchedDiags.begin(),
+       DE = BatchedDiags.end(); DI!=DE; ++DI) {
+
+    o << "  <dict>\n"
+         "   <key>path</key>\n";
+
+    const PathDiagnostic *D = *DI;
+    // Create an owning smart pointer for 'D' just so that we auto-free it
+    // when we exit this method.
+    llvm::OwningPtr<PathDiagnostic> OwnedD(const_cast<PathDiagnostic*>(D));
+
+    o << "   <array>\n";
+
+    for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I)
+      ReportDiag(o, *I, FM, *SM, LangOpts);
+
+    o << "   </array>\n";
+
+    // Output the bug type and bug category.
+    o << "   <key>description</key>";
+    EmitString(o, D->getDescription()) << '\n';
+    o << "   <key>category</key>";
+    EmitString(o, D->getCategory()) << '\n';
+    o << "   <key>type</key>";
+    EmitString(o, D->getBugType()) << '\n';
+
+    // Output the location of the bug.
+    o << "  <key>location</key>\n";
+    EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2);
+
+    // Output the diagnostic to the sub-diagnostic client, if any.
+    if (SubPD) {
+      SubPD->HandlePathDiagnostic(OwnedD.take());
+      llvm::SmallVector<std::string, 1> SubFilesMade;
+      SubPD->FlushDiagnostics(SubFilesMade);
+
+      if (!SubFilesMade.empty()) {
+        o << "  <key>" << SubPD->getName() << "_files</key>\n";
+        o << "  <array>\n";
+        for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i)
+          o << "   <string>" << SubFilesMade[i] << "</string>\n";
+        o << "  </array>\n";
+      }
+    }
+
+    // Close up the entry.
+    o << "  </dict>\n";
+  }
+
+  o << " </array>\n";
+
+  // Finish.
+  o << "</dict>\n</plist>";
+  
+  if (FilesMade)
+    FilesMade->push_back(OutputFile);
+  
+  BatchedDiags.clear();
+}