Introduce a new libclang API, clang_reparseTranslationUnit(), which
reparses an already-parsed translation unit. At the moment it's just a
convenience function, but we hope to use it for performance
optimizations.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@108756 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index b377b6d..08178e6 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -640,6 +640,44 @@
 CINDEX_LINKAGE void clang_disposeTranslationUnit(CXTranslationUnit);
 
 /**
+ * \brief Reparse the source files that produced this translation unit.
+ *
+ * This routine can be used to re-parse the source files that originally
+ * created the given translation unit, for example because those source files
+ * have changed (either on disk or as passed via \p unsaved_files). The
+ * source code will be reparsed with the same command-line options as it
+ * was originally parsed. 
+ *
+ * Reparsing a translation unit invalidates all cursors and source locations
+ * that refer into that translation unit. This makes reparsing a translation
+ * unit semantically equivalent to destroying the translation unit and then
+ * creating a new translation unit with the same command-line arguments.
+ * However, it may be more efficient to reparse a translation 
+ * unit using this routine.
+ *
+ * \param TU The translation unit whose contents will be re-parsed. The
+ * translation unit must originally have been built with 
+ * \c clang_createTranslationUnitFromSourceFile().
+ *
+ * \param num_unsaved_files The number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \param unsaved_files The files that have not yet been saved to disk
+ * but may be required for parsing, including the contents of
+ * those files.  The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ * 
+ * \returns 0 if the sources could be reparsed. A non-zero value will be
+ * returned if reparsing was impossible, such that the translation unit is
+ * invalid. In such cases, the only valid call for \p TU is 
+ * \c clang_disposeTranslationUnit(TU).
+ */
+CINDEX_LINKAGE int clang_reparseTranslationUnit(CXTranslationUnit TU,
+                                                unsigned num_unsaved_files,
+                                          struct CXUnsavedFile *unsaved_files);
+  
+/**
  * @}
  */
 
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index 1fe0d81..b09fd81 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -404,6 +404,10 @@
     ArgToStringCookie = Cookie;
   }
 
+  /// \brief Reset the state of the diagnostic object to its initial 
+  /// configuration.
+  void Reset();
+  
   //===--------------------------------------------------------------------===//
   // Diagnostic classification and reporting interfaces.
   //
diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h
index 9252358..b7a4888 100644
--- a/include/clang/Frontend/ASTUnit.h
+++ b/include/clang/Frontend/ASTUnit.h
@@ -70,6 +70,9 @@
   // FIXME: This is temporary; eventually, CIndex will always do this.
   bool                              OnlyLocalDecls;
 
+  /// \brief Whether to capture any diagnostics produced.
+  bool CaptureDiagnostics;
+  
   /// Track whether the main file was loaded from an AST or not.
   bool MainFileIsAST;
 
@@ -120,6 +123,9 @@
   
   explicit ASTUnit(bool MainFileIsAST);
 
+  void CleanTemporaryFiles();
+  bool Parse();
+  
 public:
   class ConcurrencyCheck {
     volatile ASTUnit &Self;
@@ -259,6 +265,14 @@
                                       RemappedFile *RemappedFiles = 0,
                                       unsigned NumRemappedFiles = 0,
                                       bool CaptureDiagnostics = false);
+  
+  /// \brief Reparse the source files using the same command-line options that
+  /// were originally used to produce this translation unit.
+  ///
+  /// \returns True if a failure occurred that causes the ASTUnit not to
+  /// contain any translation-unit information, false otherwise.  
+  bool Reparse(RemappedFile *RemappedFiles = 0,
+               unsigned NumRemappedFiles = 0);
 };
 
 } // namespace clang
diff --git a/include/clang/Frontend/PreprocessorOptions.h b/include/clang/Frontend/PreprocessorOptions.h
index 891359b..e2c1ca2 100644
--- a/include/clang/Frontend/PreprocessorOptions.h
+++ b/include/clang/Frontend/PreprocessorOptions.h
@@ -95,6 +95,10 @@
   void addRemappedFile(llvm::StringRef From, const llvm::MemoryBuffer * To) {
     RemappedFileBuffers.push_back(std::make_pair(From, To));
   }
+  void clearRemappedFiles() {
+    RemappedFiles.clear();
+    RemappedFileBuffers.clear();
+  }
 };
 
 } // end namespace clang
diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp
index 641d87b..68548da 100644
--- a/lib/Basic/Diagnostic.cpp
+++ b/lib/Basic/Diagnostic.cpp
@@ -244,35 +244,10 @@
 
 
 Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
-  AllExtensionsSilenced = 0;
-  IgnoreAllWarnings = false;
-  WarningsAsErrors = false;
-  ErrorsAsFatal = false;
-  SuppressSystemWarnings = false;
-  SuppressAllDiagnostics = false;
-  ShowOverloads = Ovl_All;
-  ExtBehavior = Ext_Ignore;
-
-  ErrorOccurred = false;
-  FatalErrorOccurred = false;
-  ErrorLimit = 0;
-  TemplateBacktraceLimit = 0;
-
-  NumWarnings = 0;
-  NumErrors = 0;
-  NumErrorsSuppressed = 0;
-  CustomDiagInfo = 0;
-  CurDiagID = ~0U;
-  LastDiagLevel = Ignored;
-
   ArgToStringFn = DummyArgToStringFn;
   ArgToStringCookie = 0;
 
-  DelayedDiagID = 0;
-
-  // Set all mappings to 'unset'.
-  DiagMappings BlankDiags(diag::DIAG_UPPER_LIMIT/2, 0);
-  DiagMappingsStack.push_back(BlankDiags);
+  Reset();
 }
 
 Diagnostic::~Diagnostic() {
@@ -335,6 +310,36 @@
   return true;
 }
 
+void Diagnostic::Reset() {
+  AllExtensionsSilenced = 0;
+  IgnoreAllWarnings = false;
+  WarningsAsErrors = false;
+  ErrorsAsFatal = false;
+  SuppressSystemWarnings = false;
+  SuppressAllDiagnostics = false;
+  ShowOverloads = Ovl_All;
+  ExtBehavior = Ext_Ignore;
+  
+  ErrorOccurred = false;
+  FatalErrorOccurred = false;
+  ErrorLimit = 0;
+  TemplateBacktraceLimit = 0;
+  
+  NumWarnings = 0;
+  NumErrors = 0;
+  NumErrorsSuppressed = 0;
+  CustomDiagInfo = 0;
+  CurDiagID = ~0U;
+  LastDiagLevel = Ignored;
+  DelayedDiagID = 0;
+
+  // Set all mappings to 'unset'.
+  while (!DiagMappingsStack.empty())
+    DiagMappingsStack.pop_back();
+  
+  DiagMappings BlankDiags(diag::DIAG_UPPER_LIMIT/2, 0);
+  DiagMappingsStack.push_back(BlankDiags);
+}
 
 /// getDescription - Given a diagnostic ID, return a description of the
 /// issue.
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index d8e1c51..4bfefd6 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -36,12 +36,18 @@
 using namespace clang;
 
 ASTUnit::ASTUnit(bool _MainFileIsAST)
-  : MainFileIsAST(_MainFileIsAST), ConcurrencyCheckValue(CheckUnlocked) { }
+  : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), 
+    ConcurrencyCheckValue(CheckUnlocked) { }
 
 ASTUnit::~ASTUnit() {
   ConcurrencyCheckValue = CheckLocked;
+  CleanTemporaryFiles();
+}
+
+void ASTUnit::CleanTemporaryFiles() {
   for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I)
     TemporaryFiles[I].eraseFromDisk();
+  TemporaryFiles.clear();
 }
 
 namespace {
@@ -156,7 +162,8 @@
     DiagnosticOptions DiagOpts;
     Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0);
   }
-  
+
+  AST->CaptureDiagnostics = CaptureDiagnostics;
   AST->OnlyLocalDecls = OnlyLocalDecls;
   AST->Diagnostics = Diags;
   AST->FileMgr.reset(new FileManager);
@@ -298,41 +305,38 @@
 
 }
 
-ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
-                                   llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
-                                             bool OnlyLocalDecls,
-                                             bool CaptureDiagnostics) {
+/// Parse the source file into a translation unit using the given compiler
+/// invocation, replacing the current translation unit.
+///
+/// \returns True if a failure occurred that causes the ASTUnit not to
+/// contain any translation-unit information, false otherwise.
+bool ASTUnit::Parse() {
+  if (!Invocation.get())
+    return true;
+  
   // Create the compiler instance to use for building the AST.
   CompilerInstance Clang;
-  llvm::OwningPtr<ASTUnit> AST;
-  llvm::OwningPtr<TopLevelDeclTrackerAction> Act;
-
-  if (!Diags.getPtr()) {
-    // No diagnostics engine was provided, so create our own diagnostics object
-    // with the default options.
-    DiagnosticOptions DiagOpts;
-    Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0);
-  }
+  Clang.setInvocation(Invocation.take());
+  OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
+    
+  // Set up diagnostics.
+  Clang.setDiagnostics(&getDiagnostics());
+  Clang.setDiagnosticClient(getDiagnostics().getClient());
   
-  Clang.setInvocation(CI);
-
-  Clang.setDiagnostics(Diags.getPtr());
-  Clang.setDiagnosticClient(Diags->getClient());
-
   // Create the target instance.
   Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
                                                Clang.getTargetOpts()));
   if (!Clang.hasTarget()) {
     Clang.takeDiagnosticClient();
-    return 0;
+    return true;
   }
-
+  
   // Inform the target of the language options.
   //
   // FIXME: We shouldn't need to do this, the target should be immutable once
   // created. This complexity should be lifted elsewhere.
   Clang.getTarget().setForcedLangOptions(Clang.getLangOpts());
-
+  
   assert(Clang.getFrontendOpts().Inputs.size() == 1 &&
          "Invocation must have exactly one source file!");
   assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST &&
@@ -340,52 +344,84 @@
   assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR &&
          "IR inputs not support here!");
 
-  // Create the AST unit.
-  AST.reset(new ASTUnit(false));
-  AST->Diagnostics = Diags;
-  AST->FileMgr.reset(new FileManager);
-  AST->SourceMgr.reset(new SourceManager(AST->getDiagnostics()));
-  AST->OnlyLocalDecls = OnlyLocalDecls;
-  AST->OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
-
+  // Configure the various subsystems.
+  // FIXME: Should we retain the previous file manager?
+  FileMgr.reset(new FileManager);
+  SourceMgr.reset(new SourceManager(getDiagnostics()));
+  Ctx.reset();
+  PP.reset();
+  
+  // Clear out old caches and data.
+  TopLevelDecls.clear();
+  StoredDiagnostics.clear();
+  CleanTemporaryFiles();
+  PreprocessedEntitiesByFile.clear();
+  
   // Capture any diagnostics that would otherwise be dropped.
   CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
                                     Clang.getDiagnostics(),
-                                    AST->StoredDiagnostics);
-
+                                    StoredDiagnostics);
+  
   // Create a file manager object to provide access to and cache the filesystem.
-  Clang.setFileManager(&AST->getFileManager());
-
+  Clang.setFileManager(&getFileManager());
+  
   // Create the source manager.
-  Clang.setSourceManager(&AST->getSourceManager());
-
-  Act.reset(new TopLevelDeclTrackerAction(*AST));
+  Clang.setSourceManager(&getSourceManager());
+  
+  llvm::OwningPtr<TopLevelDeclTrackerAction> Act;
+  Act.reset(new TopLevelDeclTrackerAction(*this));
   if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
                             Clang.getFrontendOpts().Inputs[0].first))
     goto error;
-
+  
   Act->Execute();
-
+  
   // Steal the created target, context, and preprocessor, and take back the
   // source and file managers.
-  AST->Ctx.reset(Clang.takeASTContext());
-  AST->PP.reset(Clang.takePreprocessor());
+  Ctx.reset(Clang.takeASTContext());
+  PP.reset(Clang.takePreprocessor());
   Clang.takeSourceManager();
   Clang.takeFileManager();
-  AST->Target.reset(Clang.takeTarget());
-
+  Target.reset(Clang.takeTarget());
+  
   Act->EndSourceFile();
-
+  
   Clang.takeDiagnosticClient();
-  Clang.takeInvocation();
-
-  AST->Invocation.reset(Clang.takeInvocation());
-  return AST.take();
-
+  
+  Invocation.reset(Clang.takeInvocation());
+  return false;
+  
 error:
   Clang.takeSourceManager();
   Clang.takeFileManager();
   Clang.takeDiagnosticClient();
+  Invocation.reset(Clang.takeInvocation());
+  return true;
+}
+
+
+ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
+                                   llvm::IntrusiveRefCntPtr<Diagnostic> Diags,
+                                             bool OnlyLocalDecls,
+                                             bool CaptureDiagnostics) {
+  if (!Diags.getPtr()) {
+    // No diagnostics engine was provided, so create our own diagnostics object
+    // with the default options.
+    DiagnosticOptions DiagOpts;
+    Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0);
+  }
+  
+  // Create the AST unit.
+  llvm::OwningPtr<ASTUnit> AST;
+  AST.reset(new ASTUnit(false));
+  AST->Diagnostics = Diags;
+  AST->CaptureDiagnostics = CaptureDiagnostics;
+  AST->OnlyLocalDecls = OnlyLocalDecls;
+  AST->Invocation.reset(CI);
+  
+  if (!AST->Parse())
+    return AST.take();
+  
   return 0;
 }
 
@@ -459,3 +495,19 @@
   return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
                                     CaptureDiagnostics);
 }
+
+bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
+  if (!Invocation.get())
+    return true;
+  
+  // Clear out the diagnostics state.
+  getDiagnostics().Reset();
+  
+  // Remap files.
+  Invocation->getPreprocessorOpts().clearRemappedFiles();
+  for (unsigned I = 0; I != NumRemappedFiles; ++I)
+    Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first,
+                                                       RemappedFiles[I].second);
+  
+  return Parse();
+}
diff --git a/test/Index/cindex-from-source.m b/test/Index/cindex-from-source.m
index 86e794d..f226e45 100644
--- a/test/Index/cindex-from-source.m
+++ b/test/Index/cindex-from-source.m
@@ -7,3 +7,6 @@
 // CHECK: cindex-from-source.m:9:1: TypeRef=t0:1:13 Extent=[9:1 - 9:3]
 struct s0 {};
 t0 g0;
+
+// RUN: c-index-test -test-load-source-reparse 5 local %s -include %t.pfx.h > %t
+// RUN: FileCheck %s < %t
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 4ed24b1..569ef20 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -558,7 +558,7 @@
   struct CXUnsavedFile *unsaved_files = 0;
   int num_unsaved_files = 0;
   int result;
-
+  
   Idx = clang_createIndex(/* excludeDeclsFromPCH */
                           !strcmp(filter, "local") ? 1 : 0,
                           /* displayDiagnosics=*/1);
@@ -578,6 +578,7 @@
                                                  unsaved_files);
   if (!TU) {
     fprintf(stderr, "Unable to load translation unit!\n");
+    free_remapped_files(unsaved_files, num_unsaved_files);
     clang_disposeIndex(Idx);
     return 1;
   }
@@ -588,6 +589,57 @@
   return result;
 }
 
+int perform_test_reparse_source(int argc, const char **argv, int trials,
+                                const char *filter, CXCursorVisitor Visitor,
+                                PostVisitTU PV) {
+  const char *UseExternalASTs =
+  getenv("CINDEXTEST_USE_EXTERNAL_AST_GENERATION");
+  CXIndex Idx;
+  CXTranslationUnit TU;
+  struct CXUnsavedFile *unsaved_files = 0;
+  int num_unsaved_files = 0;
+  int result;
+  int trial;
+  
+  Idx = clang_createIndex(/* excludeDeclsFromPCH */
+                          !strcmp(filter, "local") ? 1 : 0,
+                          /* displayDiagnosics=*/1);
+  
+  if (UseExternalASTs && strlen(UseExternalASTs))
+    clang_setUseExternalASTGeneration(Idx, 1);
+  
+  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
+    clang_disposeIndex(Idx);
+    return -1;
+  }
+  
+  TU = clang_createTranslationUnitFromSourceFile(Idx, 0,
+                                                 argc - num_unsaved_files,
+                                                 argv + num_unsaved_files,
+                                                 num_unsaved_files,
+                                                 unsaved_files);
+  if (!TU) {
+    fprintf(stderr, "Unable to load translation unit!\n");
+    free_remapped_files(unsaved_files, num_unsaved_files);
+    clang_disposeIndex(Idx);
+    return 1;
+  }
+  
+  for (trial = 0; trial < trials; ++trial) {
+    if (clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files)) {
+      clang_disposeTranslationUnit(TU);
+      free_remapped_files(unsaved_files, num_unsaved_files);
+      clang_disposeIndex(Idx);
+      return -1;      
+    }
+  }
+  
+  result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV);
+  free_remapped_files(unsaved_files, num_unsaved_files);
+  clang_disposeIndex(Idx);
+  return result;
+}
+
 /******************************************************************************/
 /* Logic for testing clang_getCursor().                                       */
 /******************************************************************************/
@@ -1219,6 +1271,8 @@
            "[FileCheck prefix]\n"
     "       c-index-test -test-load-source <symbol filter> {<args>}*\n");
   fprintf(stderr,
+    "       c-index-test -test-load-source-reparse <trials> <symbol filter> "
+    "          {<args>}*\n"
     "       c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n"
     "       c-index-test -test-annotate-tokens=<range> {<args>}*\n"
     "       c-index-test -test-inclusion-stack-source {<args>}*\n"
@@ -1252,6 +1306,14 @@
       return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I,
                                   NULL);
   }
+  else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){
+    CXCursorVisitor I = GetVisitor(argv[1] + 25);
+    if (I) {
+      int trials = atoi(argv[2]);
+      return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, 
+                                         NULL);
+    }
+  }
   else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
     CXCursorVisitor I = GetVisitor(argv[1] + 17);
     if (I)
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 7f32a1c..efc61a0 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -1390,6 +1390,25 @@
     delete static_cast<ASTUnit *>(CTUnit);
 }
 
+int clang_reparseTranslationUnit(CXTranslationUnit TU,
+                                 unsigned num_unsaved_files,
+                                 struct CXUnsavedFile *unsaved_files) {
+  if (!TU)
+    return 1;
+  
+  llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
+  for (unsigned I = 0; I != num_unsaved_files; ++I) {
+    llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
+    const llvm::MemoryBuffer *Buffer
+    = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
+    RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
+                                           Buffer));
+  }
+  
+  return static_cast<ASTUnit *>(TU)->Reparse(RemappedFiles.data(),
+                                             RemappedFiles.size())? 1 : 0;
+}
+  
 CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) {
   if (!CTUnit)
     return createCXString("");
diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports
index f21fec6..e09a6a0 100644
--- a/tools/libclang/libclang.darwin.exports
+++ b/tools/libclang/libclang.darwin.exports
@@ -86,6 +86,7 @@
 _clang_isStatement
 _clang_isTranslationUnit
 _clang_isUnexposed
+_clang_reparseTranslationUnit
 _clang_setUseExternalASTGeneration
 _clang_tokenize
 _clang_visitChildren
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index dcb40d4..4b6ddd1 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -86,6 +86,7 @@
 clang_isStatement
 clang_isTranslationUnit
 clang_isUnexposed
+clang_reparseTranslationUnit
 clang_setUseExternalASTGeneration
 clang_tokenize
 clang_visitChildren