Add code-completion support directly to ASTUnit, which performs code
completion within the translation unit using the same command-line
arguments for parsing the translation unit. Eventually, we'll reuse
the precompiled preamble to improve code-completion performance, and
this also gives us a place to cache results.

Expose this function via the new libclang function
clang_codeCompleteAt(), which performs the code completion within a
CXTranslationUnit. The completion occurs in-process
(clang_codeCompletion() runs code completion out-of-process).



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110210 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index b553d7c..5bffbac 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -417,8 +417,12 @@
   Clang.setInvocation(Invocation.take());
   OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
     
-  // Set up diagnostics.
+  // Set up diagnostics, capturing any diagnostics that would
+  // otherwise be dropped.
   Clang.setDiagnostics(&getDiagnostics());
+  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
+                                    getDiagnostics(),
+                                    StoredDiagnostics);
   Clang.setDiagnosticClient(getDiagnostics().getClient());
   
   // Create the target instance.
@@ -456,12 +460,7 @@
 
   if (!OverrideMainBuffer)
     StoredDiagnostics.clear();
-  
-  // Capture any diagnostics that would otherwise be dropped.
-  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
-                                    Clang.getDiagnostics(),
-                                    StoredDiagnostics);
-  
+    
   // Create a file manager object to provide access to and cache the filesystem.
   Clang.setFileManager(&getFileManager());
   
@@ -471,11 +470,13 @@
   // If the main file has been overridden due to the use of a preamble,
   // make that override happen and introduce the preamble.
   PreprocessorOptions &PreprocessorOpts = Clang.getPreprocessorOpts();
+  std::string PriorImplicitPCHInclude;
   if (OverrideMainBuffer) {
     PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer);
     PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size();
     PreprocessorOpts.PrecompiledPreambleBytes.second
                                                     = PreambleEndsAtStartOfLine;
+    PriorImplicitPCHInclude = PreprocessorOpts.ImplicitPCHInclude;
     PreprocessorOpts.ImplicitPCHInclude = PreambleFile;
     PreprocessorOpts.DisablePCHValidation = true;
     
@@ -513,10 +514,12 @@
   Act->EndSourceFile();
 
   // Remove the overridden buffer we used for the preamble.
-  if (OverrideMainBuffer)
+  if (OverrideMainBuffer) {
     PreprocessorOpts.eraseRemappedFile(
                                PreprocessorOpts.remapped_file_buffer_end() - 1);
-  
+    PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
+  }
+
   Clang.takeDiagnosticClient();
   
   Invocation.reset(Clang.takeInvocation());
@@ -528,6 +531,7 @@
     PreprocessorOpts.eraseRemappedFile(
                                PreprocessorOpts.remapped_file_buffer_end() - 1);
     PreprocessorOpts.DisablePCHValidation = true;
+    PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude;
   }
   
   Clang.takeSourceManager();
@@ -853,8 +857,11 @@
   Clang.setInvocation(&PreambleInvocation);
   OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
   
-  // Set up diagnostics.
+  // Set up diagnostics, capturing all of the diagnostics produced.
   Clang.setDiagnostics(&getDiagnostics());
+  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
+                                    getDiagnostics(),
+                                    StoredDiagnostics);
   Clang.setDiagnosticClient(getDiagnostics().getClient());
   
   // Create the target instance.
@@ -889,11 +896,6 @@
   StoredDiagnostics.clear();
   TopLevelDecls.clear();
   TopLevelDeclsInPreamble.clear();
-
-  // Capture any diagnostics that would otherwise be dropped.
-  CaptureDroppedDiagnostics Capture(CaptureDiagnostics, 
-                                    getDiagnostics(),
-                                    StoredDiagnostics);
   
   // Create a file manager object to provide access to and cache the filesystem.
   Clang.setFileManager(new FileManager);
@@ -1127,7 +1129,6 @@
   }
   
   // Remap files.
-  // FIXME: Do we want to remove old mappings for these files?
   Invocation->getPreprocessorOpts().clearRemappedFiles();
   for (unsigned I = 0; I != NumRemappedFiles; ++I)
     Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first,
@@ -1149,3 +1150,88 @@
     ReparsingTimer->stopTimer();
   return Result;
 }
+
+void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
+                           RemappedFile *RemappedFiles, 
+                           unsigned NumRemappedFiles,
+                           CodeCompleteConsumer &Consumer,
+                           Diagnostic &Diag, LangOptions &LangOpts,
+                           SourceManager &SourceMgr, FileManager &FileMgr,
+                   llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics) {
+  if (!Invocation.get())
+    return;
+
+  CompilerInvocation CCInvocation(*Invocation);
+  FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts();
+  PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts();
+  
+  FrontendOpts.ShowMacrosInCodeCompletion = 1;
+  FrontendOpts.ShowCodePatternsInCodeCompletion = 1;
+  FrontendOpts.CodeCompletionAt.FileName = File;
+  FrontendOpts.CodeCompletionAt.Line = Line;
+  FrontendOpts.CodeCompletionAt.Column = Column;
+
+  // Set the language options appropriately.
+  LangOpts = CCInvocation.getLangOpts();
+
+  CompilerInstance Clang;
+  Clang.setInvocation(&CCInvocation);
+  OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second;
+    
+  // Set up diagnostics, capturing any diagnostics produced.
+  Clang.setDiagnostics(&Diag);
+  CaptureDroppedDiagnostics Capture(true, 
+                                    Clang.getDiagnostics(),
+                                    StoredDiagnostics);
+  Clang.setDiagnosticClient(Diag.getClient());
+  
+  // Create the target instance.
+  Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(),
+                                               Clang.getTargetOpts()));
+  if (!Clang.hasTarget()) {
+    Clang.takeDiagnosticClient();
+    Clang.takeInvocation();
+  }
+  
+  // 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 &&
+         "FIXME: AST inputs not yet supported here!");
+  assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR &&
+         "IR inputs not support here!");
+
+  
+  // Use the source and file managers that we were given.
+  Clang.setFileManager(&FileMgr);
+  Clang.setSourceManager(&SourceMgr);
+
+  // Remap files.
+  PreprocessorOpts.clearRemappedFiles();
+  for (unsigned I = 0; I != NumRemappedFiles; ++I)
+    PreprocessorOpts.addRemappedFile(RemappedFiles[I].first,
+                                     RemappedFiles[I].second);
+  
+  // Use the code completion consumer we were given.
+  Clang.setCodeCompletionConsumer(&Consumer);
+
+  llvm::OwningPtr<SyntaxOnlyAction> Act;
+  Act.reset(new SyntaxOnlyAction);
+  if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
+                           Clang.getFrontendOpts().Inputs[0].first)) {
+    Act->Execute();
+    Act->EndSourceFile();
+  }
+  
+  // Steal back our resources. 
+  Clang.takeFileManager();
+  Clang.takeSourceManager();
+  Clang.takeInvocation();
+  Clang.takeDiagnosticClient();
+  Clang.takeCodeCompletionConsumer();
+}
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index 84e5183..76773e4 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -296,17 +296,41 @@
 
 // Code Completion
 
+static bool EnableCodeCompletion(Preprocessor &PP, 
+                                 const std::string &Filename,
+                                 unsigned Line,
+                                 unsigned Column) {
+  // Tell the source manager to chop off the given file at a specific
+  // line and column.
+  const FileEntry *Entry = PP.getFileManager().getFile(Filename);
+  if (!Entry) {
+    PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
+      << Filename;
+    return true;
+  }
+
+  // Truncate the named file at the given line/column.
+  PP.SetCodeCompletionPoint(Entry, Line, Column);
+  return false;
+}
+
 void CompilerInstance::createCodeCompletionConsumer() {
   const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt;
-  CompletionConsumer.reset(
-    createCodeCompletionConsumer(getPreprocessor(),
-                                 Loc.FileName, Loc.Line, Loc.Column,
-                                 getFrontendOpts().DebugCodeCompletionPrinter,
-                                 getFrontendOpts().ShowMacrosInCodeCompletion,
+  if (!CompletionConsumer) {
+    CompletionConsumer.reset(
+      createCodeCompletionConsumer(getPreprocessor(),
+                                   Loc.FileName, Loc.Line, Loc.Column,
+                                   getFrontendOpts().DebugCodeCompletionPrinter,
+                                   getFrontendOpts().ShowMacrosInCodeCompletion,
                              getFrontendOpts().ShowCodePatternsInCodeCompletion,
-                                 llvm::outs()));
-  if (!CompletionConsumer)
+                                   llvm::outs()));
+    if (!CompletionConsumer)
+      return;
+  } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName,
+                                  Loc.Line, Loc.Column)) {
+    CompletionConsumer.reset();
     return;
+  }
 
   if (CompletionConsumer->isOutputBinary() &&
       llvm::sys::Program::ChangeStdoutToBinary()) {
@@ -328,17 +352,8 @@
                                                bool ShowMacros,
                                                bool ShowCodePatterns,
                                                llvm::raw_ostream &OS) {
-  // Tell the source manager to chop off the given file at a specific
-  // line and column.
-  const FileEntry *Entry = PP.getFileManager().getFile(Filename);
-  if (!Entry) {
-    PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file)
-      << Filename;
+  if (EnableCodeCompletion(PP, Filename, Line, Column))
     return 0;
-  }
-
-  // Truncate the named file at the given line/column.
-  PP.SetCodeCompletionPoint(Entry, Line, Column);
 
   // Set up the creation routine for code-completion.
   if (UseDebugPrinter)
diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp
index 0310776..62e4abb 100644
--- a/lib/Sema/CodeCompleteConsumer.cpp
+++ b/lib/Sema/CodeCompleteConsumer.cpp
@@ -245,8 +245,10 @@
   return 0;
 }
 
-CodeCompletionString *CodeCompletionString::Clone() const {
-  CodeCompletionString *Result = new CodeCompletionString;
+CodeCompletionString *
+CodeCompletionString::Clone(CodeCompletionString *Result) const {
+  if (!Result)
+    Result = new CodeCompletionString;
   for (iterator C = begin(), CEnd = end(); C != CEnd; ++C)
     Result->AddChunk(C->Clone());
   return Result;
@@ -493,98 +495,81 @@
   }
 }
 
-void 
-CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
-                                                       Result *Results, 
-                                                       unsigned NumResults) {
-  // Print the results.
-  for (unsigned I = 0; I != NumResults; ++I) {
-    CXCursorKind Kind = CXCursor_NotImplemented;
-
-    switch (Results[I].Kind) {
+namespace clang {
+  // FIXME: Used externally by CIndexCodeCompletion.cpp; this code
+  // will move there, eventually, when the CIndexCodeCompleteConsumer
+  // dies.
+  CXCursorKind 
+  getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R) {
+    typedef CodeCompleteConsumer::Result Result;
+    switch (R.Kind) {
     case Result::RK_Declaration:
-      switch (Results[I].Declaration->getKind()) {
+      switch (R.Declaration->getKind()) {
       case Decl::Record:
       case Decl::CXXRecord:
       case Decl::ClassTemplateSpecialization: {
-        RecordDecl *Record = cast<RecordDecl>(Results[I].Declaration);
+        RecordDecl *Record = cast<RecordDecl>(R.Declaration);
         if (Record->isStruct())
-          Kind = CXCursor_StructDecl;
+          return CXCursor_StructDecl;
         else if (Record->isUnion())
-          Kind = CXCursor_UnionDecl;
+          return CXCursor_UnionDecl;
         else
-          Kind = CXCursor_ClassDecl;
-        break;
+          return CXCursor_ClassDecl;
       }
         
       case Decl::ObjCMethod: {
-        ObjCMethodDecl *Method = cast<ObjCMethodDecl>(Results[I].Declaration);
+        ObjCMethodDecl *Method = cast<ObjCMethodDecl>(R.Declaration);
         if (Method->isInstanceMethod())
-            Kind = CXCursor_ObjCInstanceMethodDecl;
+            return CXCursor_ObjCInstanceMethodDecl;
         else
-          Kind = CXCursor_ObjCClassMethodDecl;
-        break;
+          return CXCursor_ObjCClassMethodDecl;
       }
         
       case Decl::Typedef:
-        Kind = CXCursor_TypedefDecl;
-        break;
+        return CXCursor_TypedefDecl;
         
       case Decl::Enum:
-        Kind = CXCursor_EnumDecl;
-        break;
+        return CXCursor_EnumDecl;
         
       case Decl::Field:
-        Kind = CXCursor_FieldDecl;
-        break;
+        return CXCursor_FieldDecl;
         
       case Decl::EnumConstant:
-        Kind = CXCursor_EnumConstantDecl;
-        break;
+        return CXCursor_EnumConstantDecl;
         
       case Decl::Function:
       case Decl::CXXMethod:
       case Decl::CXXConstructor:
       case Decl::CXXDestructor:
       case Decl::CXXConversion:
-        Kind = CXCursor_FunctionDecl;
-        break;
+        return CXCursor_FunctionDecl;
         
       case Decl::Var:
-        Kind = CXCursor_VarDecl;
-        break;
+        return CXCursor_VarDecl;
         
       case Decl::ParmVar:
-        Kind = CXCursor_ParmDecl;
-        break;
+        return CXCursor_ParmDecl;
         
       case Decl::ObjCInterface:
-        Kind = CXCursor_ObjCInterfaceDecl;
-        break;
+        return CXCursor_ObjCInterfaceDecl;
         
       case Decl::ObjCCategory:
-        Kind = CXCursor_ObjCCategoryDecl;
-        break;
+        return CXCursor_ObjCCategoryDecl;
         
       case Decl::ObjCProtocol:
-        Kind = CXCursor_ObjCProtocolDecl;
-        break;
+        return CXCursor_ObjCProtocolDecl;
         
       case Decl::ObjCProperty:
-        Kind = CXCursor_ObjCPropertyDecl;
-        break;
+        return CXCursor_ObjCPropertyDecl;
         
       case Decl::ObjCIvar:
-        Kind = CXCursor_ObjCIvarDecl;
-        break;
+        return CXCursor_ObjCIvarDecl;
         
       case Decl::ObjCImplementation:
-        Kind = CXCursor_ObjCImplementationDecl;
-        break;
+        return CXCursor_ObjCImplementationDecl;
         
       case Decl::ObjCCategoryImpl:
-        Kind = CXCursor_ObjCCategoryImplDecl;
-        break;
+        return CXCursor_ObjCCategoryImplDecl;
         
       default:
         break;
@@ -592,16 +577,23 @@
       break;
 
     case Result::RK_Macro:
-      Kind = CXCursor_MacroDefinition;
-      break;
+      return CXCursor_MacroDefinition;
 
     case Result::RK_Keyword:
     case Result::RK_Pattern:
-      Kind = CXCursor_NotImplemented;
-      break;
+      return CXCursor_NotImplemented;
     }
+    return CXCursor_NotImplemented;
+  }
+}
 
-    WriteUnsigned(OS, Kind);
+void 
+CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
+                                                       Result *Results, 
+                                                       unsigned NumResults) {
+  // Print the results.
+  for (unsigned I = 0; I != NumResults; ++I) {
+    WriteUnsigned(OS, getCursorKindForCompletionResult(Results[I]));
     WriteUnsigned(OS, Results[I].Priority);
     CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
     assert(CCS && "No code-completion string?");
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 476c79c..52ac8dd 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -1811,13 +1811,15 @@
 /// how to use this result, or NULL to indicate that the string or name of the
 /// result is all that is needed.
 CodeCompletionString *
-CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
+CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S,
+                                               CodeCompletionString *Result) {
   typedef CodeCompletionString::Chunk Chunk;
   
   if (Kind == RK_Pattern)
-    return Pattern->Clone();
+    return Pattern->Clone(Result);
   
-  CodeCompletionString *Result = new CodeCompletionString;
+  if (!Result)
+    Result = new CodeCompletionString;
 
   if (Kind == RK_Keyword) {
     Result->AddTypedTextChunk(Keyword);