Rework how CIndex handles diagnostics. Rather than using a callback,
we attach diagnostics to translation units and code-completion
results, so they can be queried at any time.

To facilitate this, the new StoredDiagnostic class stores a diagnostic
in a serializable/deserializable form, and ASTUnit knows how to
capture diagnostics in this stored form. CIndex's CXDiagnostic is a
thin wrapper around StoredDiagnostic, providing a C interface to
stored or de-serialized diagnostics.

I've XFAIL'd one test case temporarily, because currently we end up
storing diagnostics in an ASTUnit that's never returned to the user
(because it contains errors). I'll introduce a temporary fix for this
soon; the real fix will be to allow us to return and query invalid ASTs.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96592 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index a0c4889..345b9ba 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -90,8 +90,46 @@
   }
 };
 
+class StoredDiagnosticClient : public DiagnosticClient {
+  llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags;
+  
+public:
+  explicit StoredDiagnosticClient(
+                          llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags)
+    : StoredDiags(StoredDiags) { }
+  
+  virtual void HandleDiagnostic(Diagnostic::Level Level,
+                                const DiagnosticInfo &Info);
+};
+
+/// \brief RAII object that optionally captures diagnostics, if
+/// there is no diagnostic client to capture them already.
+class CaptureDroppedDiagnostics {
+  Diagnostic &Diags;
+  StoredDiagnosticClient Client;
+  DiagnosticClient *PreviousClient;
+
+public:
+  CaptureDroppedDiagnostics(bool RequestCapture, Diagnostic &Diags, 
+                           llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags)
+    : Diags(Diags), Client(StoredDiags), PreviousClient(Diags.getClient()) 
+  {
+    if (RequestCapture || Diags.getClient() == 0)
+      Diags.setClient(&Client);
+  }
+
+  ~CaptureDroppedDiagnostics() {
+    Diags.setClient(PreviousClient);
+  }
+};
+
 } // anonymous namespace
 
+void StoredDiagnosticClient::HandleDiagnostic(Diagnostic::Level Level,
+                                              const DiagnosticInfo &Info) {
+  StoredDiags.push_back(StoredDiagnostic(Level, Info));
+}
+
 const std::string &ASTUnit::getOriginalSourceFileName() {
   return OriginalSourceFile;
 }
@@ -105,11 +143,16 @@
                                   Diagnostic &Diags,
                                   bool OnlyLocalDecls,
                                   RemappedFile *RemappedFiles,
-                                  unsigned NumRemappedFiles) {
+                                  unsigned NumRemappedFiles,
+                                  bool CaptureDiagnostics) {
   llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true));
   AST->OnlyLocalDecls = OnlyLocalDecls;
   AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager()));
 
+  // If requested, capture diagnostics in the ASTUnit.
+  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, Diags, 
+                                    AST->Diagnostics);
+
   for (unsigned I = 0; I != NumRemappedFiles; ++I) {
     // Create the file entry for the file that we're mapping from.
     const FileEntry *FromFile
@@ -231,7 +274,8 @@
 
 ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
                                              Diagnostic &Diags,
-                                             bool OnlyLocalDecls) {
+                                             bool OnlyLocalDecls,
+                                             bool CaptureDiagnostics) {
   // Create the compiler instance to use for building the AST.
   CompilerInstance Clang;
   llvm::OwningPtr<ASTUnit> AST;
@@ -245,8 +289,13 @@
   // Create the target instance.
   Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
                                                Clang.getTargetOpts()));
-  if (!Clang.hasTarget())
-    goto error;
+  if (!Clang.hasTarget()) {
+    Clang.takeSourceManager();
+    Clang.takeFileManager();
+    Clang.takeDiagnosticClient();
+    Clang.takeDiagnostics();
+    return 0;
+  }
 
   // Inform the target of the language options.
   //
@@ -261,10 +310,14 @@
 
   // Create the AST unit.
   AST.reset(new ASTUnit(false));
-
   AST->OnlyLocalDecls = OnlyLocalDecls;
   AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
 
+  // Capture any diagnostics that would otherwise be dropped.
+  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
+                                    Clang.getDiagnostics(),
+                                    AST->Diagnostics);
+
   // Create a file manager object to provide access to and cache the filesystem.
   Clang.setFileManager(&AST->getFileManager());
 
@@ -312,7 +365,8 @@
                                       llvm::StringRef ResourceFilesPath,
                                       bool OnlyLocalDecls,
                                       RemappedFile *RemappedFiles,
-                                      unsigned NumRemappedFiles) {
+                                      unsigned NumRemappedFiles,
+                                      bool CaptureDiagnostics) {
   llvm::SmallVector<const char *, 16> Args;
   Args.push_back("<clang>"); // FIXME: Remove dummy argument.
   Args.insert(Args.end(), ArgBegin, ArgEnd);
@@ -363,5 +417,6 @@
   CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath;
 
   CI->getFrontendOpts().DisableFree = true;
-  return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls);
+  return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
+                                    CaptureDiagnostics);
 }
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index 917cbd7..1831ca5 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -102,7 +102,7 @@
 
 void BinaryDiagnosticSerializer::HandleDiagnostic(Diagnostic::Level DiagLevel,
                                                   const DiagnosticInfo &Info) {
-  Info.Serialize(DiagLevel, OS);
+  StoredDiagnostic(DiagLevel, Info).Serialize(OS);
 }
 
 static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts,