[cfe-commits] r110210 - in /cfe/trunk: include/clang-c/Index.h include/clang/Frontend/ASTUnit.h include/clang/Sema/CodeCompleteConsumer.h lib/Frontend/ASTUnit.cpp lib/Frontend/CompilerInstance.cpp lib/Sema/CodeCompleteConsumer.cpp lib/Sema/SemaCodeComplete.cpp test/Index/complete-exprs.c tools/c-index-test/c-index-test.c tools/libclang/CIndex.cpp tools/libclang/CIndexCodeCompletion.cpp tools/libclang/libclang.darwin.exports tools/libclang/libclang.exports

Douglas Gregor dgregor at apple.com
Wed Aug 4 09:47:14 PDT 2010


Author: dgregor
Date: Wed Aug  4 11:47:14 2010
New Revision: 110210

URL: http://llvm.org/viewvc/llvm-project?rev=110210&view=rev
Log:
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).


Modified:
    cfe/trunk/include/clang-c/Index.h
    cfe/trunk/include/clang/Frontend/ASTUnit.h
    cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
    cfe/trunk/lib/Frontend/ASTUnit.cpp
    cfe/trunk/lib/Frontend/CompilerInstance.cpp
    cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp
    cfe/trunk/test/Index/complete-exprs.c
    cfe/trunk/tools/c-index-test/c-index-test.c
    cfe/trunk/tools/libclang/CIndex.cpp
    cfe/trunk/tools/libclang/CIndexCodeCompletion.cpp
    cfe/trunk/tools/libclang/libclang.darwin.exports
    cfe/trunk/tools/libclang/libclang.exports

Modified: cfe/trunk/include/clang-c/Index.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang-c/Index.h?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/include/clang-c/Index.h (original)
+++ cfe/trunk/include/clang-c/Index.h Wed Aug  4 11:47:14 2010
@@ -715,7 +715,7 @@
  * \p command_line_args.
  *
  * \param unsaved_files the files that have not yet been saved to disk
- * but may be required for code completion, including the contents of
+ * 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.
@@ -2079,6 +2079,76 @@
                                           unsigned complete_column);
 
 /**
+ * \brief Perform code completion at a given location in a translation unit.
+ *
+ * This function performs code completion at a particular file, line, and
+ * column within source code, providing results that suggest potential
+ * code snippets based on the context of the completion. The basic model
+ * for code completion is that Clang will parse a complete source file,
+ * performing syntax checking up to the location where code-completion has
+ * been requested. At that point, a special code-completion token is passed
+ * to the parser, which recognizes this token and determines, based on the
+ * current location in the C/Objective-C/C++ grammar and the state of
+ * semantic analysis, what completions to provide. These completions are
+ * returned via a new \c CXCodeCompleteResults structure.
+ *
+ * Code completion itself is meant to be triggered by the client when the
+ * user types punctuation characters or whitespace, at which point the
+ * code-completion location will coincide with the cursor. For example, if \c p
+ * is a pointer, code-completion might be triggered after the "-" and then
+ * after the ">" in \c p->. When the code-completion location is afer the ">",
+ * the completion results will provide, e.g., the members of the struct that
+ * "p" points to. The client is responsible for placing the cursor at the
+ * beginning of the token currently being typed, then filtering the results
+ * based on the contents of the token. For example, when code-completing for
+ * the expression \c p->get, the client should provide the location just after
+ * the ">" (e.g., pointing at the "g") to this code-completion hook. Then, the
+ * client can filter the results based on the current token text ("get"), only
+ * showing those results that start with "get". The intent of this interface
+ * is to separate the relatively high-latency acquisition of code-completion
+ * results from the filtering of results on a per-character basis, which must
+ * have a lower latency.
+ *
+ * \param TU The translation unit in which code-completion should
+ * occur. The source files for this translation unit need not be
+ * completely up-to-date (and the contents of those source files may
+ * be overridden via \p unsaved_files). Cursors referring into the
+ * translation unit may be invalidated by this invocation.
+ *
+ * \param complete_filename The name of the source file where code
+ * completion should be performed. This filename may be any file
+ * included in the translation unit.
+ *
+ * \param complete_line The line at which code-completion should occur.
+ *
+ * \param complete_column The column at which code-completion should occur.
+ * Note that the column should point just after the syntactic construct that
+ * initiated code completion, and not in the middle of a lexical token.
+ *
+ * \param unsaved_files the Tiles that have not yet been saved to disk
+ * but may be required for parsing or code completion, 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.
+ *
+ * \param num_unsaved_files The number of unsaved file entries in \p
+ * unsaved_files.
+ *
+ * \returns If successful, a new \c CXCodeCompleteResults structure
+ * containing code-completion results, which should eventually be
+ * freed with \c clang_disposeCodeCompleteResults(). If code
+ * completion fails, returns NULL.
+ */
+CINDEX_LINKAGE
+CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
+                                            const char *complete_filename,
+                                            unsigned complete_line,
+                                            unsigned complete_column,
+                                            struct CXUnsavedFile *unsaved_files,
+                                            unsigned num_unsaved_files);
+
+/**
  * \brief Free the given set of code-completion results.
  */
 CINDEX_LINKAGE

Modified: cfe/trunk/include/clang/Frontend/ASTUnit.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTUnit.h?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/ASTUnit.h (original)
+++ cfe/trunk/include/clang/Frontend/ASTUnit.h Wed Aug  4 11:47:14 2010
@@ -38,6 +38,7 @@
 
 namespace clang {
 class ASTContext;
+class CodeCompleteConsumer;
 class CompilerInvocation;
 class Decl;
 class Diagnostic;
@@ -384,6 +385,24 @@
   /// contain any translation-unit information, false otherwise.  
   bool Reparse(RemappedFile *RemappedFiles = 0,
                unsigned NumRemappedFiles = 0);
+
+  /// \brief Perform code completion at the given file, line, and
+  /// column within this translation unit.
+  ///
+  /// \brief File The file in which code completion will occur.
+  /// \brief Line The line at which code completion will occur.
+  /// \brief Column The column at which code completion will occur.
+  /// \brief Consumer The consumer that will receive code-completion results.
+  ///
+  /// FIXME: The Diag, LangOpts, SourceMgr, FileMgr, and
+  /// StoredDiagnostics parameters are all disgusting hacks. They will
+  /// go away.
+  void 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);
 };
 
 } // namespace clang

Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Wed Aug  4 11:47:14 2010
@@ -274,7 +274,10 @@
   std::string getAsString() const; 
   
   /// \brief Clone this code-completion string.
-  CodeCompletionString *Clone() const;
+  ///
+  /// \param Result If non-NULL, points to an empty code-completion
+  /// result that will be given a cloned copy of
+  CodeCompletionString *Clone(CodeCompletionString *Result = 0) const;
   
   /// \brief Serialize this code-completion string to the given stream.
   void Serialize(llvm::raw_ostream &OS) const;
@@ -410,7 +413,14 @@
     
     /// \brief Create a new code-completion string that describes how to insert
     /// this result into a program.
-    CodeCompletionString *CreateCodeCompletionString(Sema &S);
+    ///
+    /// \param S The semantic analysis that created the result.
+    ///
+    /// \param Result If non-NULL, the already-allocated, empty
+    /// code-completion string that will be populated with the
+    /// appropriate code completion string for this result.
+    CodeCompletionString *CreateCodeCompletionString(Sema &S,
+                                              CodeCompletionString *Result = 0);
     
     void Destroy();
     

Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTUnit.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTUnit.cpp Wed Aug  4 11:47:14 2010
@@ -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();
+}

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Wed Aug  4 11:47:14 2010
@@ -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)

Modified: cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp (original)
+++ cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp Wed Aug  4 11:47:14 2010
@@ -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?");

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Wed Aug  4 11:47:14 2010
@@ -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);

Modified: cfe/trunk/test/Index/complete-exprs.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/complete-exprs.c?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/test/Index/complete-exprs.c (original)
+++ cfe/trunk/test/Index/complete-exprs.c Wed Aug  4 11:47:14 2010
@@ -13,12 +13,14 @@
 const char *str = "Hello, \nWorld";
 
 // RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: macro definition:{TypedText __VERSION__} (70)
 // CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12)
 // CHECK-CC1-NOT: NotImplemented:{TypedText float} (40)
 // CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (2)
 // CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
 // RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
+// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
 // CHECK-CC3: macro definition:{TypedText __VERSION__} (70)
 // CHECK-CC3: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
 // CHECK-CC3-NOT: NotImplemented:{TypedText float} (40)

Modified: cfe/trunk/tools/c-index-test/c-index-test.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/c-index-test/c-index-test.c?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/tools/c-index-test/c-index-test.c (original)
+++ cfe/trunk/tools/c-index-test/c-index-test.c Wed Aug  4 11:47:14 2010
@@ -888,11 +888,21 @@
     return -1;
 
   CIdx = clang_createIndex(0, 1);
-  results = clang_codeComplete(CIdx,
-                               argv[argc - 1], argc - num_unsaved_files - 3,
-                               argv + num_unsaved_files + 2,
-                               num_unsaved_files, unsaved_files,
-                               filename, line, column);
+  if (getenv("CINDEXTEST_EDITING")) {
+    CXTranslationUnit *TU = clang_parseTranslationUnit(CIdx, 0,
+                                  argv + num_unsaved_files + 2,
+                                  argc - num_unsaved_files - 2,
+                                  unsaved_files,
+                                  num_unsaved_files,
+                                  getDefaultParsingOptions());
+    results = clang_codeCompleteAt(TU, filename, line, column,
+                                   unsaved_files, num_unsaved_files);
+  } else
+    results = clang_codeComplete(CIdx,
+                                 argv[argc - 1], argc - num_unsaved_files - 3,
+                                 argv + num_unsaved_files + 2,
+                                 num_unsaved_files, unsaved_files,
+                                 filename, line, column);
 
   if (results) {
     unsigned i, n = results->NumResults;

Modified: cfe/trunk/tools/libclang/CIndex.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CIndex.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CIndex.cpp (original)
+++ cfe/trunk/tools/libclang/CIndex.cpp Wed Aug  4 11:47:14 2010
@@ -1443,7 +1443,7 @@
   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);
+      = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
     RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
                                            Buffer));
   }

Modified: cfe/trunk/tools/libclang/CIndexCodeCompletion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/CIndexCodeCompletion.cpp?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/CIndexCodeCompletion.cpp (original)
+++ cfe/trunk/tools/libclang/CIndexCodeCompletion.cpp Wed Aug  4 11:47:14 2010
@@ -16,6 +16,7 @@
 #include "CIndexDiagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Sema/CodeCompleteConsumer.h"
@@ -231,7 +232,7 @@
   llvm::SmallVector<StoredDiagnostic, 8> Diagnostics;
 
   /// \brief Diag object
-  Diagnostic Diag;
+  llvm::IntrusiveRefCntPtr<Diagnostic> Diag;
   
   /// \brief Language options used to adjust source locations.
   LangOptions LangOpts;
@@ -248,7 +249,8 @@
 };
 
 AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults() 
-  : CXCodeCompleteResults(), Buffer(0), SourceMgr(Diag) { }
+  : CXCodeCompleteResults(), Buffer(0), Diag(new Diagnostic), 
+    SourceMgr(*Diag) { }
   
 AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
   for (unsigned I = 0, N = NumResults; I != N; ++I)
@@ -529,6 +531,165 @@
   return Results;
 }
 
+} // end extern "C"
+
+namespace clang {
+  // FIXME: defined in CodeCompleteConsumer.cpp, but should be a
+  // static function here.
+  CXCursorKind 
+  getCursorKindForCompletionResult(const CodeCompleteConsumer::Result &R);
+}
+
+
+namespace {
+  class CaptureCompletionResults : public CodeCompleteConsumer {
+    AllocatedCXCodeCompleteResults &AllocatedResults;
+
+  public:
+    explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results)
+      : CodeCompleteConsumer(true, false, false), AllocatedResults(Results) { }
+
+    virtual void ProcessCodeCompleteResults(Sema &S, Result *Results,
+                                            unsigned NumResults) {
+      AllocatedResults.Results = new CXCompletionResult [NumResults];
+      AllocatedResults.NumResults = NumResults;
+      for (unsigned I = 0; I != NumResults; ++I) {
+        CXStoredCodeCompletionString *StoredCompletion
+          = new CXStoredCodeCompletionString(Results[I].Priority);
+        (void)Results[I].CreateCodeCompletionString(S, StoredCompletion);
+        AllocatedResults.Results[I].CursorKind 
+          = getCursorKindForCompletionResult(Results[I]);
+        AllocatedResults.Results[I].CompletionString = StoredCompletion;
+      }
+    }
+  };
+}
+
+extern "C" {
+CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
+                                            const char *complete_filename,
+                                            unsigned complete_line,
+                                            unsigned complete_column,
+                                            struct CXUnsavedFile *unsaved_files,
+                                            unsigned num_unsaved_files) {
+#ifdef UDP_CODE_COMPLETION_LOGGER
+#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
+  const llvm::TimeRecord &StartTime =  llvm::TimeRecord::getCurrentTime();
+#endif
+#endif
+
+  bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
+  
+  ASTUnit *AST = static_cast<ASTUnit *>(TU);
+  if (!AST)
+    return 0;
+
+  // Perform the remapping of source files.
+  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));
+  }
+  
+  if (EnableLogging) {
+    // FIXME: Add logging.
+  }
+
+  // Parse the resulting source file to find code-completion results.
+  AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults;
+  Results->Results = 0;
+  Results->NumResults = 0;
+  Results->Buffer = 0;
+
+  // Create a code-completion consumer to capture the results.
+  CaptureCompletionResults Capture(*Results);
+
+  // Perform completion.
+  AST->CodeComplete(complete_filename, complete_line, complete_column,
+                    RemappedFiles.data(), RemappedFiles.size(), Capture,
+                    *Results->Diag, Results->LangOpts, Results->SourceMgr,
+                    Results->FileMgr, Results->Diagnostics);
+
+  
+
+#ifdef UDP_CODE_COMPLETION_LOGGER
+#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
+  const llvm::TimeRecord &EndTime =  llvm::TimeRecord::getCurrentTime();
+  llvm::SmallString<256> LogResult;
+  llvm::raw_svector_ostream os(LogResult);
+
+  // Figure out the language and whether or not it uses PCH.
+  const char *lang = 0;
+  bool usesPCH = false;
+
+  for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
+       I != E; ++I) {
+    if (*I == 0)
+      continue;
+    if (strcmp(*I, "-x") == 0) {
+      if (I + 1 != E) {
+        lang = *(++I);
+        continue;
+      }
+    }
+    else if (strcmp(*I, "-include") == 0) {
+      if (I+1 != E) {
+        const char *arg = *(++I);
+        llvm::SmallString<512> pchName;
+        {
+          llvm::raw_svector_ostream os(pchName);
+          os << arg << ".pth";
+        }
+        pchName.push_back('\0');
+        struct stat stat_results;
+        if (stat(pchName.data(), &stat_results) == 0)
+          usesPCH = true;
+        continue;
+      }
+    }
+  }
+
+  os << "{ ";
+  os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime());
+  os << ", \"numRes\": " << Results->NumResults;
+  os << ", \"diags\": " << Results->Diagnostics.size();
+  os << ", \"pch\": " << (usesPCH ? "true" : "false");
+  os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"';
+  const char *name = getlogin();
+  os << ", \"user\": \"" << (name ? name : "unknown") << '"';
+  os << ", \"clangVer\": \"" << getClangFullVersion() << '"';
+  os << " }";
+
+  llvm::StringRef res = os.str();
+  if (res.size() > 0) {
+    do {
+      // Setup the UDP socket.
+      struct sockaddr_in servaddr;
+      bzero(&servaddr, sizeof(servaddr));
+      servaddr.sin_family = AF_INET;
+      servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT);
+      if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER,
+                    &servaddr.sin_addr) <= 0)
+        break;
+
+      int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+      if (sockfd < 0)
+        break;
+
+      sendto(sockfd, res.data(), res.size(), 0,
+             (struct sockaddr *)&servaddr, sizeof(servaddr));
+      close(sockfd);
+    }
+    while (false);
+  }
+#endif
+#endif
+  return Results;
+}
+
 void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) {
   if (!ResultsIn)
     return;

Modified: cfe/trunk/tools/libclang/libclang.darwin.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/libclang.darwin.exports?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/libclang.darwin.exports (original)
+++ cfe/trunk/tools/libclang/libclang.darwin.exports Wed Aug  4 11:47:14 2010
@@ -1,6 +1,7 @@
 _clang_CXXMethod_isStatic
 _clang_annotateTokens
 _clang_codeComplete
+_clang_codeCompleteAt
 _clang_codeCompleteGetDiagnostic
 _clang_codeCompleteGetNumDiagnostics
 _clang_constructUSR_ObjCCategory

Modified: cfe/trunk/tools/libclang/libclang.exports
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/libclang/libclang.exports?rev=110210&r1=110209&r2=110210&view=diff
==============================================================================
--- cfe/trunk/tools/libclang/libclang.exports (original)
+++ cfe/trunk/tools/libclang/libclang.exports Wed Aug  4 11:47:14 2010
@@ -1,6 +1,7 @@
 clang_CXXMethod_isStatic
 clang_annotateTokens
 clang_codeComplete
+clang_codeCompleteAt
 clang_codeCompleteGetDiagnostic
 clang_codeCompleteGetNumDiagnostics
 clang_constructUSR_ObjCCategory





More information about the cfe-commits mailing list