Various improvements to Clang's code-completion infrastructure:
  - Introduce more code-completion string "chunk" kinds that describe
  symbols, the actual text that the user is expected to type, etc.
  - Make the generation of macro results optional, since it can be
  slow
  - Make code-completion accessible through the C API, marshalling the
  code-completion results through a temporary file (ick) to maintain
  process separation.

The last doesn't have tests yet.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@86306 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp
index 4798e28..9389139 100644
--- a/tools/CIndex/CIndex.cpp
+++ b/tools/CIndex/CIndex.cpp
@@ -16,12 +16,15 @@
 #include "clang/Index/Indexer.h"
 #include "clang/Index/ASTLocation.h"
 #include "clang/Index/Utils.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/AST/DeclVisitor.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Decl.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Frontend/ASTUnit.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Config/config.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -31,6 +34,7 @@
 
 #include <cstdio>
 #include <vector>
+#include <sstream>
 
 #ifdef LLVM_ON_WIN32
 #define WIN32_LEAN_AND_MEAN
@@ -993,5 +997,316 @@
   *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc());
 }
 
+enum CXCompletionChunkKind 
+clang_getCompletionChunkKind(CXCompletionString completion_string,
+                             unsigned chunk_number) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+  if (!CCStr || chunk_number >= CCStr->size())
+    return CXCompletionChunk_Text;
+  
+  switch ((*CCStr)[chunk_number].Kind) {
+  case CodeCompletionString::CK_TypedText:
+    return CXCompletionChunk_TypedText;
+  case CodeCompletionString::CK_Text:
+    return CXCompletionChunk_Text;
+  case CodeCompletionString::CK_Optional:
+    return CXCompletionChunk_Optional; 
+  case CodeCompletionString::CK_Placeholder:
+    return CXCompletionChunk_Placeholder;
+  case CodeCompletionString::CK_Informative:
+    return CXCompletionChunk_Informative;
+  case CodeCompletionString::CK_CurrentParameter:
+    return CXCompletionChunk_CurrentParameter;
+  case CodeCompletionString::CK_LeftParen:
+    return CXCompletionChunk_LeftParen;
+  case CodeCompletionString::CK_RightParen:
+    return CXCompletionChunk_RightParen;
+  case CodeCompletionString::CK_LeftBracket:
+    return CXCompletionChunk_LeftBracket;
+  case CodeCompletionString::CK_RightBracket:
+    return CXCompletionChunk_RightBracket;
+  case CodeCompletionString::CK_LeftBrace:
+    return CXCompletionChunk_LeftBrace;
+  case CodeCompletionString::CK_RightBrace:
+    return CXCompletionChunk_RightBrace;
+  case CodeCompletionString::CK_LeftAngle:
+    return CXCompletionChunk_LeftAngle;
+  case CodeCompletionString::CK_RightAngle:
+    return CXCompletionChunk_RightAngle;
+  case CodeCompletionString::CK_Comma:
+    return CXCompletionChunk_Comma;
+  }
+  
+  // Should be unreachable, but let's be careful.
+  return CXCompletionChunk_Text;
+}
+  
+const char *clang_getCompletionChunkText(CXCompletionString completion_string,
+                                         unsigned chunk_number) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+  if (!CCStr || chunk_number >= CCStr->size())
+    return 0;
+  
+  switch ((*CCStr)[chunk_number].Kind) {
+  case CodeCompletionString::CK_TypedText:
+  case CodeCompletionString::CK_Text:
+  case CodeCompletionString::CK_Placeholder:
+  case CodeCompletionString::CK_CurrentParameter:
+  case CodeCompletionString::CK_Informative:
+  case CodeCompletionString::CK_LeftParen:
+  case CodeCompletionString::CK_RightParen:
+  case CodeCompletionString::CK_LeftBracket:
+  case CodeCompletionString::CK_RightBracket:
+  case CodeCompletionString::CK_LeftBrace:
+  case CodeCompletionString::CK_RightBrace:
+  case CodeCompletionString::CK_LeftAngle:
+  case CodeCompletionString::CK_RightAngle:
+  case CodeCompletionString::CK_Comma:
+    return (*CCStr)[chunk_number].Text;
+      
+  case CodeCompletionString::CK_Optional:
+    // Note: treated as an empty text block.
+    return 0;
+  }
+  
+  // Should be unreachable, but let's be careful.
+  return 0;
+}
+  
+CXCompletionString
+clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
+                                         unsigned chunk_number) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+  if (!CCStr || chunk_number >= CCStr->size())
+    return 0;
+  
+  switch ((*CCStr)[chunk_number].Kind) {
+  case CodeCompletionString::CK_TypedText:
+  case CodeCompletionString::CK_Text:
+  case CodeCompletionString::CK_Placeholder:
+  case CodeCompletionString::CK_CurrentParameter:
+  case CodeCompletionString::CK_Informative:
+  case CodeCompletionString::CK_LeftParen:
+  case CodeCompletionString::CK_RightParen:
+  case CodeCompletionString::CK_LeftBracket:
+  case CodeCompletionString::CK_RightBracket:
+  case CodeCompletionString::CK_LeftBrace:
+  case CodeCompletionString::CK_RightBrace:
+  case CodeCompletionString::CK_LeftAngle:
+  case CodeCompletionString::CK_RightAngle:
+  case CodeCompletionString::CK_Comma:
+    return 0;
+    
+  case CodeCompletionString::CK_Optional:
+    // Note: treated as an empty text block.
+    return (*CCStr)[chunk_number].Optional;
+  }
+  
+  // Should be unreachable, but let's be careful.
+  return 0;
+}
+  
+unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+  return CCStr? CCStr->size() : 0;
+}
 
+static CXCursorKind parseResultKind(llvm::StringRef Str) {
+  return llvm::StringSwitch<CXCursorKind>(Str)
+    .Case("Typedef", CXCursor_TypedefDecl)
+    .Case("Struct", CXCursor_StructDecl)
+    .Case("Union", CXCursor_UnionDecl)
+    .Case("Class", CXCursor_ClassDecl)
+    .Case("Field", CXCursor_FieldDecl)
+    .Case("EnumConstant", CXCursor_EnumConstantDecl)
+    .Case("Function", CXCursor_FunctionDecl)
+    // FIXME: Hacks here to make C++ member functions look like C functions    
+    .Case("CXXMethod", CXCursor_FunctionDecl)
+    .Case("CXXConstructor", CXCursor_FunctionDecl)
+    .Case("CXXDestructor", CXCursor_FunctionDecl)
+    .Case("CXXConversion", CXCursor_FunctionDecl)
+    .Case("Var", CXCursor_VarDecl)
+    .Case("ParmVar", CXCursor_ParmDecl)
+    .Case("ObjCInterface", CXCursor_ObjCInterfaceDecl)
+    .Case("ObjCCategory", CXCursor_ObjCCategoryDecl)
+    .Case("ObjCProtocol", CXCursor_ObjCProtocolDecl)
+    .Case("ObjCProperty", CXCursor_ObjCPropertyDecl)
+    .Case("ObjCIvar", CXCursor_ObjCIvarDecl)
+    .Case("ObjCInstanceMethod", CXCursor_ObjCInstanceMethodDecl)
+    .Case("ObjCClassMethod", CXCursor_ObjCClassMethodDecl)
+    .Default(CXCursor_NotImplemented);
+}
+  
+void clang_codeComplete(CXIndex CIdx, 
+                        const char *source_filename,
+                        int num_command_line_args, 
+                        const char **command_line_args,
+                        const char *complete_filename,
+                        unsigned complete_line,
+                        unsigned complete_column,
+                        CXCompletionIterator completion_iterator,
+                        CXClientData client_data)  {
+  // The indexer, which is mainly used to determine where diagnostics go.
+  CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx);
+  
+  // Build up the arguments for invoking 'clang'.
+  std::vector<const char *> argv;
+  
+  // First add the complete path to the 'clang' executable.
+  llvm::sys::Path ClangPath = CXXIdx->getClangPath();
+  argv.push_back(ClangPath.c_str());
+  
+  // Add the '-fsyntax-only' argument so that we only perform a basic 
+  // syntax check of the code.
+  argv.push_back("-fsyntax-only");
+
+  // Add the appropriate '-code-completion-at=file:line:column' argument
+  // to perform code completion, with an "-Xclang" preceding it.
+  std::string code_complete_at;
+  code_complete_at += "-code-completion-at=";
+  code_complete_at += complete_filename;
+  code_complete_at += ":";
+  code_complete_at += llvm::utostr(complete_line);
+  code_complete_at += ":";
+  code_complete_at += llvm::utostr(complete_column);
+  argv.push_back("-Xclang");
+  argv.push_back(code_complete_at.c_str());
+  argv.push_back("-Xclang");
+  argv.push_back("-code-completion-printer=cindex");
+  
+  // Add the source file name (FIXME: later, we'll want to build temporary
+  // file from the buffer, or just feed the source text via standard input).
+  argv.push_back(source_filename);  
+  
+  // Process the compiler options, stripping off '-o', '-c', '-fsyntax-only'.
+  for (int i = 0; i < num_command_line_args; ++i)
+    if (const char *arg = command_line_args[i]) {
+      if (strcmp(arg, "-o") == 0) {
+        ++i; // Also skip the matching argument.
+        continue;
+      }
+      if (strcmp(arg, "-emit-ast") == 0 ||
+          strcmp(arg, "-c") == 0 ||
+          strcmp(arg, "-fsyntax-only") == 0) {
+        continue;
+      }
+      
+      // Keep the argument.
+      argv.push_back(arg);
+    }
+  
+  // Add the null terminator.
+  argv.push_back(NULL);
+  
+  // Generate a temporary name for the AST file.
+  char tmpFile[L_tmpnam];
+  char *tmpFileName = tmpnam(tmpFile);
+  llvm::sys::Path ResultsFile(tmpFileName);
+  
+  // Invoke 'clang'.
+  llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null
+                           // on Unix or NUL (Windows).
+  std::string ErrMsg;
+  const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, &DevNull, 0 };
+  llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL,
+                                     /* redirects */ &Redirects[0],
+                                     /* secondsToWait */ 0, 
+                                     /* memoryLimits */ 0, &ErrMsg);
+  
+  if (!ErrMsg.empty()) {
+    llvm::errs() << "clang_codeComplete: " << ErrMsg 
+    << '\n' << "Arguments: \n";
+    for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
+         I!=E; ++I) {
+      if (*I)
+        llvm::errs() << ' ' << *I << '\n';
+    }
+    llvm::errs() << '\n';
+  }
+
+  // Parse the resulting source file to find code-completion results.
+  using llvm::MemoryBuffer;
+  using llvm::StringRef;
+  if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) {
+    StringRef Buffer = F->getBuffer();
+    do {
+      StringRef::size_type CompletionIdx = Buffer.find("COMPLETION:");
+      StringRef::size_type OverloadIdx = Buffer.find("OVERLOAD:");
+      if (CompletionIdx == StringRef::npos && OverloadIdx == StringRef::npos)
+        break;
+      
+      if (OverloadIdx < CompletionIdx) {
+        // Parse an overload result.
+        Buffer = Buffer.substr(OverloadIdx);
+        
+        // Skip past the OVERLOAD:
+        Buffer = Buffer.substr(Buffer.find(':') + 1);
+        
+        // Find the entire completion string.
+        StringRef::size_type EOL = Buffer.find_first_of("\n\r");
+        if (EOL == StringRef::npos)
+          continue;
+        
+        StringRef Line = Buffer.substr(0, EOL);
+        Buffer = Buffer.substr(EOL + 1);
+        CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
+        if (!CCStr || CCStr->empty())
+          continue;
+        
+        // Vend the code-completion result to the caller.
+        CXCompletionResult Result;
+        Result.CursorKind = CXCursor_NotImplemented;
+        Result.CompletionString = CCStr;
+        if (completion_iterator)
+          completion_iterator(&Result, client_data);
+        delete CCStr;
+        
+        continue;
+      }
+      
+      // Parse a completion result.
+      Buffer = Buffer.substr(CompletionIdx);
+      
+      // Skip past the COMPLETION:
+      Buffer = Buffer.substr(Buffer.find(':') + 1);
+      
+      // Get the rank
+      unsigned Rank = 0;
+      StringRef::size_type AfterRank = Buffer.find(':');
+      Buffer.substr(0, AfterRank).getAsInteger(10, Rank);
+      Buffer = Buffer.substr(AfterRank + 1);
+      
+      // Get the kind of result.
+      StringRef::size_type AfterKind = Buffer.find(':');
+      StringRef Kind = Buffer.substr(0, AfterKind);
+      Buffer = Buffer.substr(AfterKind + 1);
+      
+      // Skip over any whitespace.
+      Buffer = Buffer.substr(Buffer.find_first_not_of(" \t"));
+      
+      // Find the entire completion string.
+      StringRef::size_type EOL = Buffer.find_first_of("\n\r");
+      if (EOL == StringRef::npos)
+        continue;
+      
+      StringRef Line = Buffer.substr(0, EOL);
+      Buffer = Buffer.substr(EOL + 1);
+      CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line);
+      if (!CCStr || CCStr->empty())
+        continue;
+          
+      // Vend the code-completion result to the caller.
+      CXCompletionResult Result;
+      Result.CursorKind = parseResultKind(Kind);
+      Result.CompletionString = CCStr;
+      if (completion_iterator)
+        completion_iterator(&Result, client_data);
+      delete CCStr;
+    } while (true);
+    delete F;
+  }  
+  
+  ResultsFile.eraseFromDisk();
+}
+  
 } // end extern "C"
diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports
index 5f461d8..ba977c1 100644
--- a/tools/CIndex/CIndex.exports
+++ b/tools/CIndex/CIndex.exports
@@ -1,8 +1,11 @@
+_clang_codeComplete
 _clang_createIndex
 _clang_createTranslationUnit
 _clang_createTranslationUnitFromSourceFile
 _clang_disposeIndex
 _clang_disposeTranslationUnit
+_clang_getCompletionChunkKind
+_clang_getCompletionChunkText
 _clang_getCursor
 _clang_getCursorColumn
 _clang_getCursorDecl
@@ -24,6 +27,7 @@
 _clang_getEntityFromDecl
 _clang_getFileName
 _clang_getFileTime
+_clang_getNumCompletionChunks
 _clang_getTranslationUnitSpelling
 _clang_getURI
 _clang_isDeclaration
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 8791ee2..429c5e5 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1,6 +1,7 @@
 /* c-index-test.c */
 
 #include "clang-c/Index.h"
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -94,10 +95,115 @@
   }
 }
 
+/* Parse file:line:column from the input string. Returns 0 on success, non-zero
+   on failure. If successful, the pointer *filename will contain newly-allocated
+   memory (that will be owned by the caller) to store the file name. */
+int parse_file_line_column(const char *input, char **filename, unsigned *line, 
+                           unsigned *column) {
+  const char *colon = strchr(input, ':');
+  char *endptr = 0;
+  if (!colon) {
+    fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
+    return 1;
+  }
+
+  /* Copy the file name. */
+  *filename = (char*)malloc(colon - input);
+  strncpy(*filename, input, colon - input);
+  (*filename)[colon - input] = 0;
+  input = colon + 1;
+  
+  /* Parse the line number. */
+  *line = strtol(input, &endptr, 10);
+  if (*endptr != ':') {
+    fprintf(stderr, "could not parse line:column in '%s'\n", input);
+    free(filename);
+    *filename = 0;
+    return 1;
+  }
+  input = endptr + 1;
+  
+  /* Parse the column number. */
+  *column = strtol(input, &endptr, 10);
+  if (*endptr != 0) {
+    fprintf(stderr, "could not parse column in '%s'\n", input);
+    free(filename);
+    *filename = 0;
+    return 1;
+  }
+  
+  return 0;
+}
+
+const char *
+clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
+  switch (Kind) {
+  case CXCompletionChunk_Optional: return "Optional";
+  case CXCompletionChunk_TypedText: return "TypedText";
+  case CXCompletionChunk_Text: return "Text";
+  case CXCompletionChunk_Placeholder: return "Placeholder";
+  case CXCompletionChunk_Informative: return "Informative";
+  case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
+  case CXCompletionChunk_LeftParen: return "LeftParen";
+  case CXCompletionChunk_RightParen: return "RightParen";
+  case CXCompletionChunk_LeftBracket: return "LeftBracket";
+  case CXCompletionChunk_RightBracket: return "RightBracket";
+  case CXCompletionChunk_LeftBrace: return "LeftBrace";
+  case CXCompletionChunk_RightBrace: return "RightBrace";
+  case CXCompletionChunk_LeftAngle: return "LeftAngle";
+  case CXCompletionChunk_RightAngle: return "RightAngle";
+  case CXCompletionChunk_Comma: return "Comma";
+  }
+  
+  return "Unknown";
+}
+
+void print_completion_result(CXCompletionResult *completion_result,
+                             CXClientData client_data) {
+  FILE *file = (FILE *)client_data;
+
+  fprintf(file, "%s:", 
+          clang_getCursorKindSpelling(completion_result->CursorKind));
+  int I, N = clang_getNumCompletionChunks(completion_result->CompletionString);
+  for (I = 0; I != N; ++I) {
+    const char *text 
+      = clang_getCompletionChunkText(completion_result->CompletionString, I);
+    
+    enum CXCompletionChunkKind Kind
+      = clang_getCompletionChunkKind(completion_result->CompletionString, I);
+    fprintf(file, "{%s %s}", 
+            clang_getCompletionChunkKindSpelling(Kind),
+            text? text : "");
+  }
+  fprintf(file, "\n");
+}
+
+void perform_code_completion(int argc, const char **argv) {
+  const char *input = argv[1];
+  char *filename = 0;
+  unsigned line;
+  unsigned column;
+  input += strlen("-code-completion-at=");
+  if (parse_file_line_column(input, &filename, &line, &column))
+    return;
+
+  CXIndex CIdx = clang_createIndex(0, 0);
+  clang_codeComplete(CIdx, argv[argc - 1], argc - 3, argv + 2, 
+                     filename, line, column, &print_completion_result, stdout);
+  clang_disposeIndex(CIdx);
+  free(filename);
+}
+
 /*
  * First sign of life:-)
  */
 int main(int argc, char **argv) {
+  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) {
+    perform_code_completion(argc, (const char **)argv);
+    return 0;
+  }
+  
+  
   if (argc != 3) {
     printf("Incorrect usage of c-index-test (requires 3 arguments)\n");
     return 0;
diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp
index 97dc3fd..cfef4bf 100644
--- a/tools/clang-cc/clang-cc.cpp
+++ b/tools/clang-cc/clang-cc.cpp
@@ -215,15 +215,45 @@
  llvm::cl::desc("Specify output file"));
 
 
+enum CodeCompletionPrinter {
+  CCP_Debug,
+  CCP_CIndex
+};
+
 static llvm::cl::opt<ParsedSourceLocation>
 CodeCompletionAt("code-completion-at",
                  llvm::cl::value_desc("file:line:column"),
               llvm::cl::desc("Dump code-completion information at a location"));
 
+static llvm::cl::opt<CodeCompletionPrinter>
+CodeCompletionPrinter("code-completion-printer",
+                      llvm::cl::desc("Choose output type:"),
+                      llvm::cl::init(CCP_Debug),
+                      llvm::cl::values(
+                        clEnumValN(CCP_Debug, "debug",
+                          "Debug code-completion results"),
+                        clEnumValN(CCP_CIndex, "cindex",
+                          "Code-completion results for the CIndex library"),
+                        clEnumValEnd));
+
+static llvm::cl::opt<bool>
+CodeCompletionWantsMacros("code-completion-macros",
+                 llvm::cl::desc("Include macros in code-completion results"));
+
 /// \brief Buld a new code-completion consumer that prints the results of
 /// code completion to standard output.
 static CodeCompleteConsumer *BuildPrintingCodeCompleter(Sema &S, void *) {
-  return new PrintingCodeCompleteConsumer(S, llvm::outs());
+  switch (CodeCompletionPrinter.getValue()) {
+  case CCP_Debug:
+    return new PrintingCodeCompleteConsumer(S, CodeCompletionWantsMacros, 
+                                            llvm::outs());
+      
+  case CCP_CIndex:
+    return new CIndexCodeCompleteConsumer(S, CodeCompletionWantsMacros,
+                                          llvm::outs());
+  };
+  
+  return 0;
 }
 
 //===----------------------------------------------------------------------===//