]> granicus.if.org Git - clang/commitdiff
Implement caching of code-completion results for macro definitions
authorDouglas Gregor <dgregor@apple.com>
Fri, 13 Aug 2010 22:48:40 +0000 (22:48 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 13 Aug 2010 22:48:40 +0000 (22:48 +0000)
when the CXTranslationUnit_CacheCompletionResults option is given to
clang_parseTranslationUnit(). Essentially, we compute code-completion
results for macro definitions after we have parsed the file, then
store an ASTContext-agnostic version of those results (completion
string, cursor kind, priority, and active contexts) in the
ASTUnit. When performing code completion in that ASTUnit, we splice
the macro definition results into the results provided by the actual
code-completion (which has had macros turned off) before libclang gets
those results. We use completion context information to only splice in
those results that make sense for that context.

With a completion involving all of the macros from Cocoa.h and a few other
system libraries (totally ~8500 macro definitions) living in a
precompiled header, we get about a 9% performance improvement from
code completion, since we no longer have to deserialize all of the
macro definitions from the precompiled header.

Note that macro definitions are merely the canary; the cache is
designed to also support other top-level declarations, which should be
a bigger performance win. That optimization will be next.

Note also that there is no mechanism for determining when to throw
away the cache and recompute its contents.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@111051 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Frontend/ASTUnit.h
include/clang/Sema/CodeCompleteConsumer.h
include/clang/Sema/Sema.h
lib/Frontend/ASTUnit.cpp
lib/Sema/CodeCompleteConsumer.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaCodeComplete.cpp
test/Index/complete-macros.c
tools/c-index-test/c-index-test.c
tools/libclang/CIndex.cpp
tools/libclang/CIndexCodeCompletion.cpp

index 07bba5c0aa695daa0e7ebef34160c645d9f1b269..76a86871de3d0173826e2a756a23c915ef871f49 100644 (file)
@@ -20,6 +20,7 @@
 #include "clang/Lex/PreprocessingRecord.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/FileManager.h"
+#include "clang-c/Index.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/SmallVector.h"
@@ -198,6 +199,55 @@ private:
   /// declarations parsed within the precompiled preamble.
   std::vector<pch::DeclID> TopLevelDeclsInPreamble;
 
+  ///
+  /// \defgroup CodeCompleteCaching Code-completion caching
+  ///
+  /// \{
+  ///
+
+  /// \brief Whether we should be caching code-completion results.
+  bool ShouldCacheCodeCompletionResults;
+  
+public:
+  /// \brief A cached code-completion result, which may be introduced in one of
+  /// many different contexts.
+  struct CachedCodeCompletionResult {
+    /// \brief The code-completion string corresponding to this completion
+    /// result.
+    CodeCompletionString *Completion;
+    
+    /// \brief A bitmask that indicates which code-completion contexts should
+    /// contain this completion result.
+    ///
+    /// The bits in the bitmask correspond to the values of 
+    /// CodeCompleteContext::Kind. To map from a completion context kind to a 
+    /// bit, subtract one from the completion context kind and shift 1 by that
+    /// number of bits. Many completions can occur in several different
+    /// contexts.
+    unsigned ShowInContexts;
+    
+    /// \brief The priority given to this code-completion result.
+    unsigned Priority;
+    
+    /// \brief The libclang cursor kind corresponding to this code-completion 
+    /// result.
+    CXCursorKind Kind;
+  };
+  
+private:
+  /// \brief The set of cached code-completion results.
+  std::vector<CachedCodeCompletionResult> CachedCompletionResults;
+  
+  /// \brief Cache any "global" code-completion results, so that we 
+  void CacheCodeCompletionResults();
+  
+  /// \brief Clear out and deallocate 
+  void ClearCachedCompletionResults();
+  
+  /// 
+  /// \}
+  ///
+  
   /// \brief The timers we've created from the various parses, reparses, etc.
   /// involved in this translation unit.
   std::vector<llvm::Timer *> Timers;
@@ -339,6 +389,21 @@ public:
     return StoredDiagnostics; 
   }
 
+  typedef std::vector<CachedCodeCompletionResult>::iterator
+    cached_completion_iterator;
+  
+  cached_completion_iterator cached_completion_begin() {
+    return CachedCompletionResults.begin();
+  }
+
+  cached_completion_iterator cached_completion_end() {
+    return CachedCompletionResults.end();
+  }
+
+  unsigned cached_completion_size() const { 
+    return CachedCompletionResults.size(); 
+  }
+  
   /// \brief Whether this AST represents a complete translation unit.
   ///
   /// If false, this AST is only a partial translation unit, e.g., one
@@ -380,7 +445,8 @@ public:
                                              bool OnlyLocalDecls = false,
                                              bool CaptureDiagnostics = false,
                                              bool PrecompilePreamble = false,
-                                          bool CompleteTranslationUnit = true);
+                                          bool CompleteTranslationUnit = true,
+                                       bool CacheCodeCompletionResults = false);
 
   /// LoadFromCommandLine - Create an ASTUnit from a vector of command line
   /// arguments, which must specify exactly one source file.
@@ -405,7 +471,8 @@ public:
                                       unsigned NumRemappedFiles = 0,
                                       bool CaptureDiagnostics = false,
                                       bool PrecompilePreamble = false,
-                                      bool CompleteTranslationUnit = true);
+                                      bool CompleteTranslationUnit = true,
+                                      bool CacheCodeCompletionResults = false);
   
   /// \brief Reparse the source files using the same command-line options that
   /// were originally used to produce this translation unit.
index 5310b63477a78847f3d12dbf5a37445bf4a56914..48e422383f4e001aa3ce130b4df1a70be20e028b 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/AST/Type.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "clang-c/Index.h"
 #include <memory>
 #include <string>
 
@@ -419,6 +420,9 @@ public:
     /// \brief The priority of this particular code-completion result.
     unsigned Priority;
 
+    /// \brief The cursor kind that describes this result.
+    CXCursorKind CursorKind;
+    
     /// \brief Specifies which parameter (of a function, Objective-C method,
     /// macro, etc.) we should start with when formatting the result.
     unsigned StartParameter;
@@ -455,6 +459,7 @@ public:
         Hidden(false), QualifierIsInformative(QualifierIsInformative),
         StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
         DeclaringEntity(false), Qualifier(Qualifier) { 
+      computeCursorKind();
     }
     
     /// \brief Build a result that refers to a keyword or symbol.
@@ -462,21 +467,29 @@ public:
       : Kind(RK_Keyword), Keyword(Keyword), Priority(Priority), 
         StartParameter(0), Hidden(false), QualifierIsInformative(0), 
         StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
-        DeclaringEntity(false), Qualifier(0) { }
+        DeclaringEntity(false), Qualifier(0) {
+      computeCursorKind();
+    }
     
     /// \brief Build a result that refers to a macro.
     Result(IdentifierInfo *Macro, unsigned Priority = CCP_Macro)
-     : Kind(RK_Macro), Macro(Macro), Priority(Priority), StartParameter(0), 
-       Hidden(false), QualifierIsInformative(0), 
-       StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
-       DeclaringEntity(false), Qualifier(0) { }
+      : Kind(RK_Macro), Macro(Macro), Priority(Priority), StartParameter(0), 
+        Hidden(false), QualifierIsInformative(0), 
+        StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
+        DeclaringEntity(false), Qualifier(0) { 
+      computeCursorKind();
+    }
 
     /// \brief Build a result that refers to a pattern.
-    Result(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern)
+    Result(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern,
+           CXCursorKind CursorKind = CXCursor_NotImplemented)
       : Kind(RK_Pattern), Pattern(Pattern), Priority(Priority), 
-        StartParameter(0), Hidden(false), QualifierIsInformative(0), 
-        StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
-        DeclaringEntity(false), Qualifier(0) { }
+        CursorKind(CursorKind), StartParameter(0), Hidden(false), 
+        QualifierIsInformative(0), StartsNestedNameSpecifier(false), 
+        AllParametersAreInformative(false), DeclaringEntity(false), 
+        Qualifier(0) 
+    { 
+    }
     
     /// \brief Retrieve the declaration stored in this result.
     NamedDecl *getDeclaration() const {
@@ -505,6 +518,9 @@ public:
     
     /// brief Determine a base priority for the given declaration.
     static unsigned getPriorityFromDecl(NamedDecl *ND);
+    
+  private:
+    void computeCursorKind();
   };
     
   class OverloadCandidate {
@@ -571,7 +587,8 @@ public:
                                                 Sema &S) const;    
   };
   
-  CodeCompleteConsumer() : IncludeMacros(false), OutputIsBinary(false) { }
+  CodeCompleteConsumer() : IncludeMacros(false), IncludeCodePatterns(false),
+                           OutputIsBinary(false) { }
   
   CodeCompleteConsumer(bool IncludeMacros, bool IncludeCodePatterns,
                        bool OutputIsBinary)
index d2b89824a79f9af68893461fb810dce6550de9e2..5ab6d827e117e7b511e6bed62389e3b6fbd32fa5 100644 (file)
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_AST_SEMA_H
 
 #include "clang/Sema/IdentifierResolver.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/CXXFieldCollector.h"
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/Template.h"
@@ -46,7 +47,6 @@ namespace llvm {
 namespace clang {
   class ASTContext;
   class ASTConsumer;
-  class CodeCompleteConsumer;
   class Preprocessor;
   class Decl;
   class DeclContext;
@@ -4701,7 +4701,8 @@ public:
                                                   TypeTy *ReturnType,
                                                   IdentifierInfo **SelIdents,
                                                   unsigned NumSelIdents);
-  
+  void GatherGlobalCodeCompletions(
+                  llvm::SmallVectorImpl<CodeCompleteConsumer::Result> &Results);
   //@}
 
   //===--------------------------------------------------------------------===//
index e501260af722c9e7e59f87c872aee4b51e163797..e6bae6b72734b90e9e99961a7c1617d06d90e3e5 100644 (file)
@@ -50,7 +50,8 @@ const unsigned DefaultPreambleRebuildInterval = 5;
 ASTUnit::ASTUnit(bool _MainFileIsAST)
   : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), 
     CompleteTranslationUnit(true), ConcurrencyCheckValue(CheckUnlocked), 
-    PreambleRebuildCounter(0), SavedMainFileBuffer(0) { 
+    PreambleRebuildCounter(0), SavedMainFileBuffer(0),
+    ShouldCacheCodeCompletionResults(false) { 
 }
 
 ASTUnit::~ASTUnit() {
@@ -75,6 +76,8 @@ ASTUnit::~ASTUnit() {
   
   delete SavedMainFileBuffer;
   
+  ClearCachedCompletionResults();
+  
   for (unsigned I = 0, N = Timers.size(); I != N; ++I)
     delete Timers[I];
 }
@@ -85,6 +88,70 @@ void ASTUnit::CleanTemporaryFiles() {
   TemporaryFiles.clear();
 }
 
+void ASTUnit::CacheCodeCompletionResults() {
+  if (!TheSema)
+    return;
+  
+  llvm::Timer *CachingTimer = 0;
+  if (TimerGroup.get()) {
+    CachingTimer = new llvm::Timer("Cache global code completions", 
+                                   *TimerGroup);
+    CachingTimer->startTimer();
+    Timers.push_back(CachingTimer);
+  }
+
+  // Clear out the previous results.
+  ClearCachedCompletionResults();
+  
+  // Gather the set of global code completions.
+  typedef CodeCompleteConsumer::Result Result;
+  llvm::SmallVector<Result, 8> Results;
+  TheSema->GatherGlobalCodeCompletions(Results);
+  
+  // Translate global code completions into cached completions.
+  for (unsigned I = 0, N = Results.size(); I != N; ++I) {
+    switch (Results[I].Kind) {
+    case Result::RK_Declaration:
+      // FIXME: Handle declarations!
+      break;
+      
+    case Result::RK_Keyword:
+    case Result::RK_Pattern:
+      // Ignore keywords and patterns; we don't care, since they are so
+      // easily regenerated.
+      break;
+      
+    case Result::RK_Macro: {
+      CachedCodeCompletionResult CachedResult;
+      CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema);
+      CachedResult.ShowInContexts
+        = (1 << (CodeCompletionContext::CCC_TopLevel - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1))
+        | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1))
+        | (1 << (CodeCompletionContext::CCC_Statement - 1))
+        | (1 << (CodeCompletionContext::CCC_Expression - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1));
+      CachedResult.Priority = Results[I].Priority;
+      CachedResult.Kind = Results[I].CursorKind;
+      CachedCompletionResults.push_back(CachedResult);
+      break;
+    }
+    }
+    Results[I].Destroy();
+  }
+
+  if (CachingTimer)
+    CachingTimer->stopTimer();
+}
+
+void ASTUnit::ClearCachedCompletionResults() {
+  for (unsigned I = 0, N = CachedCompletionResults.size(); I != N; ++I)
+    delete CachedCompletionResults[I].Completion;
+  CachedCompletionResults.clear();
+}
+
 namespace {
 
 /// \brief Gathers information from PCHReader that will be used to initialize
@@ -542,6 +609,12 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) {
   Clang.takeDiagnosticClient();
   
   Invocation.reset(Clang.takeInvocation());
+  
+  // If we were asked to cache code-completion results and don't have any
+  // results yet, do so now.
+  if (ShouldCacheCodeCompletionResults && CachedCompletionResults.empty())
+    CacheCodeCompletionResults();
+  
   return false;
   
 error:
@@ -579,7 +652,6 @@ static std::string GetPreamblePCHPath() {
   if (P.createTemporaryFileOnDisk())
     return std::string();
   
-  fprintf(stderr, "Preamble file: %s\n", P.str().c_str());
   return P.str();
 }
 
@@ -1044,7 +1116,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
                                              bool OnlyLocalDecls,
                                              bool CaptureDiagnostics,
                                              bool PrecompilePreamble,
-                                             bool CompleteTranslationUnit) {
+                                             bool CompleteTranslationUnit,
+                                             bool CacheCodeCompletionResults) {
   if (!Diags.getPtr()) {
     // No diagnostics engine was provided, so create our own diagnostics object
     // with the default options.
@@ -1059,6 +1132,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
   AST->CaptureDiagnostics = CaptureDiagnostics;
   AST->OnlyLocalDecls = OnlyLocalDecls;
   AST->CompleteTranslationUnit = CompleteTranslationUnit;
+  AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults;
   AST->Invocation.reset(CI);
   CI->getPreprocessorOpts().RetainRemappedFileBuffers = true;
   
@@ -1097,7 +1171,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
                                       unsigned NumRemappedFiles,
                                       bool CaptureDiagnostics,
                                       bool PrecompilePreamble,
-                                      bool CompleteTranslationUnit) {
+                                      bool CompleteTranslationUnit,
+                                      bool CacheCodeCompletionResults) {
   if (!Diags.getPtr()) {
     // No diagnostics engine was provided, so create our own diagnostics object
     // with the default options.
@@ -1159,7 +1234,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
   CI->getFrontendOpts().DisableFree = true;
   return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
                                     CaptureDiagnostics, PrecompilePreamble,
-                                    CompleteTranslationUnit);
+                                    CompleteTranslationUnit,
+                                    CacheCodeCompletionResults);
 }
 
 bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
@@ -1196,6 +1272,91 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) {
   return Result;
 }
 
+//----------------------------------------------------------------------------//
+// Code completion
+//----------------------------------------------------------------------------//
+
+namespace {
+  /// \brief Code completion consumer that combines the cached code-completion
+  /// results from an ASTUnit with the code-completion results provided to it,
+  /// then passes the result on to 
+  class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer {
+    unsigned NormalContexts;
+    ASTUnit &AST;
+    CodeCompleteConsumer &Next;
+    
+  public:
+    AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next,
+                                  bool IncludeMacros, bool IncludeCodePatterns)
+      : CodeCompleteConsumer(IncludeMacros, IncludeCodePatterns,
+                             Next.isOutputBinary()), AST(AST), Next(Next) 
+    { 
+      // Compute the set of contexts in which we will look when we don't have
+      // any information about the specific context.
+      NormalContexts 
+        = (1 << (CodeCompletionContext::CCC_TopLevel - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1))
+        | (1 << (CodeCompletionContext::CCC_Statement - 1))
+        | (1 << (CodeCompletionContext::CCC_Expression - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1))
+        | (1 << (CodeCompletionContext::CCC_MemberAccess - 1))
+        | (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1));
+      
+      if (AST.getASTContext().getLangOptions().CPlusPlus)
+        NormalContexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1))
+                    | (1 << (CodeCompletionContext::CCC_UnionTag - 1))
+                    | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1));
+    }
+    
+    virtual void ProcessCodeCompleteResults(Sema &S, 
+                                            CodeCompletionContext Context,
+                                            Result *Results,
+                                            unsigned NumResults) { 
+      // Merge the results we were given with the results we cached.
+      bool AddedResult = false;
+      unsigned InContexts = 
+        (Context.getKind() == CodeCompletionContext::CCC_Other? NormalContexts
+                                              : (1 << (Context.getKind() - 1)));
+      typedef CodeCompleteConsumer::Result Result;
+      llvm::SmallVector<Result, 8> AllResults;
+      for (ASTUnit::cached_completion_iterator 
+               C = AST.cached_completion_begin(),
+            CEnd = AST.cached_completion_end();
+           C != CEnd; ++C) {
+        // If the context we are in matches any of the contexts we are 
+        // interested in, we'll add this result.
+        if ((C->ShowInContexts & InContexts) == 0)
+          continue;
+        
+        // If we haven't added any results previously, do so now.
+        if (!AddedResult) {
+          AllResults.insert(AllResults.end(), Results, Results + NumResults);
+          AddedResult = true;
+        }
+        
+        AllResults.push_back(Result(C->Completion, C->Priority, C->Kind));
+      }
+      
+      // If we did not add any cached completion results, just forward the
+      // results we were given to the next consumer.
+      if (!AddedResult) {
+        Next.ProcessCodeCompleteResults(S, Context, Results, NumResults);
+        return;
+      }
+      
+      Next.ProcessCodeCompleteResults(S, Context, AllResults.data(),
+                                      AllResults.size());
+    }
+    
+    virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
+                                           OverloadCandidate *Candidates,
+                                           unsigned NumCandidates) { 
+      Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates);
+    }
+  };
+}
 void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
                            RemappedFile *RemappedFiles, 
                            unsigned NumRemappedFiles,
@@ -1223,7 +1384,8 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
   FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts();
   PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts();
 
-  FrontendOpts.ShowMacrosInCodeCompletion = IncludeMacros;
+  FrontendOpts.ShowMacrosInCodeCompletion
+    = IncludeMacros && CachedCompletionResults.empty();
   FrontendOpts.ShowCodePatternsInCodeCompletion = IncludeCodePatterns;
   FrontendOpts.CodeCompletionAt.FileName = File;
   FrontendOpts.CodeCompletionAt.Line = Line;
@@ -1281,8 +1443,12 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column,
     PreprocessorOpts.addRemappedFile(RemappedFiles[I].first,
                                      RemappedFiles[I].second);
   
-  // Use the code completion consumer we were given.
-  Clang.setCodeCompletionConsumer(&Consumer);
+  // Use the code completion consumer we were given, but adding any cached
+  // code-completion results.
+  AugmentedCodeCompleteConsumer 
+  AugmentedConsumer(*this, Consumer, FrontendOpts.ShowMacrosInCodeCompletion,
+                    FrontendOpts.ShowCodePatternsInCodeCompletion);
+  Clang.setCodeCompletionConsumer(&AugmentedConsumer);
 
   // If we have a precompiled preamble, try to use it. We only allow
   // the use of the precompiled preamble if we're if the completion
index 6f4e1839e302270896071a683f3250833e7b6ae7..c1b666a792ad279b389844b286952a0d95d98dd1 100644 (file)
@@ -496,95 +496,109 @@ PrintingCodeCompleteConsumer::ProcessOverloadCandidates(Sema &SemaRef,
   }
 }
 
-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 (R.Declaration->getKind()) {
-      case Decl::Record:
-      case Decl::CXXRecord:
-      case Decl::ClassTemplateSpecialization: {
-        RecordDecl *Record = cast<RecordDecl>(R.Declaration);
-        if (Record->isStruct())
-          return CXCursor_StructDecl;
-        else if (Record->isUnion())
-          return CXCursor_UnionDecl;
-        else
-          return CXCursor_ClassDecl;
-      }
-        
-      case Decl::ObjCMethod: {
-        ObjCMethodDecl *Method = cast<ObjCMethodDecl>(R.Declaration);
-        if (Method->isInstanceMethod())
-            return CXCursor_ObjCInstanceMethodDecl;
-        else
-          return CXCursor_ObjCClassMethodDecl;
-      }
-        
-      case Decl::Typedef:
-        return CXCursor_TypedefDecl;
-        
-      case Decl::Enum:
-        return CXCursor_EnumDecl;
+void CodeCompleteConsumer::Result::computeCursorKind() {
+  switch (Kind) {
+  case RK_Declaration:
+    switch (Declaration->getKind()) {
+    case Decl::Record:
+    case Decl::CXXRecord:
+    case Decl::ClassTemplateSpecialization: {
+      RecordDecl *Record = cast<RecordDecl>(Declaration);
+      if (Record->isStruct())
+        CursorKind = CXCursor_StructDecl;
+      else if (Record->isUnion())
+        CursorKind = CXCursor_UnionDecl;
+      else
+        CursorKind = CXCursor_ClassDecl;
+      break;
+    }
+      
+    case Decl::ObjCMethod: {
+      ObjCMethodDecl *Method = cast<ObjCMethodDecl>(Declaration);
+      if (Method->isInstanceMethod())
+          CursorKind = CXCursor_ObjCInstanceMethodDecl;
+      else
+        CursorKind = CXCursor_ObjCClassMethodDecl;
+      break;
+    }
+      
+    case Decl::Typedef:
+      CursorKind = CXCursor_TypedefDecl;
+      break;
         
-      case Decl::Field:
-        return CXCursor_FieldDecl;
+    case Decl::Enum:
+      CursorKind = CXCursor_EnumDecl;
+      break;
         
-      case Decl::EnumConstant:
-        return CXCursor_EnumConstantDecl;
+    case Decl::Field:
+      CursorKind = CXCursor_FieldDecl;
+      break;
         
-      case Decl::Function:
-      case Decl::CXXMethod:
-      case Decl::CXXConstructor:
-      case Decl::CXXDestructor:
-      case Decl::CXXConversion:
-        return CXCursor_FunctionDecl;
+    case Decl::EnumConstant:
+      CursorKind = CXCursor_EnumConstantDecl;
+      break;      
         
-      case Decl::Var:
-        return CXCursor_VarDecl;
+    case Decl::Function:
+    case Decl::CXXMethod:
+    case Decl::CXXConstructor:
+    case Decl::CXXDestructor:
+    case Decl::CXXConversion:
+      CursorKind = CXCursor_FunctionDecl;
+      break;
         
-      case Decl::ParmVar:
-        return CXCursor_ParmDecl;
+    case Decl::Var:
+      CursorKind = CXCursor_VarDecl;
+      break;
         
-      case Decl::ObjCInterface:
-        return CXCursor_ObjCInterfaceDecl;
+    case Decl::ParmVar:
+      CursorKind = CXCursor_ParmDecl;
+      break;
         
-      case Decl::ObjCCategory:
-        return CXCursor_ObjCCategoryDecl;
+    case Decl::ObjCInterface:
+      CursorKind = CXCursor_ObjCInterfaceDecl;
+      break;
         
-      case Decl::ObjCProtocol:
-        return CXCursor_ObjCProtocolDecl;
+    case Decl::ObjCCategory:
+      CursorKind = CXCursor_ObjCCategoryDecl;
+      break;
         
-      case Decl::ObjCProperty:
-        return CXCursor_ObjCPropertyDecl;
+    case Decl::ObjCProtocol:
+      CursorKind = CXCursor_ObjCProtocolDecl;
+      break;
+
+    case Decl::ObjCProperty:
+      CursorKind = CXCursor_ObjCPropertyDecl;
+      break;
         
-      case Decl::ObjCIvar:
-        return CXCursor_ObjCIvarDecl;
+    case Decl::ObjCIvar:
+      CursorKind = CXCursor_ObjCIvarDecl;
+      break;
         
-      case Decl::ObjCImplementation:
-        return CXCursor_ObjCImplementationDecl;
+    case Decl::ObjCImplementation:
+      CursorKind = CXCursor_ObjCImplementationDecl;
+      break;
         
-      case Decl::ObjCCategoryImpl:
-        return CXCursor_ObjCCategoryImplDecl;
+    case Decl::ObjCCategoryImpl:
+      CursorKind = CXCursor_ObjCCategoryImplDecl;
+      break;
         
-      default:
-        break;
-      }
+    default:
+      CursorKind = CXCursor_NotImplemented;
       break;
-
-    case Result::RK_Macro:
-      return CXCursor_MacroDefinition;
-
-    case Result::RK_Keyword:
-    case Result::RK_Pattern:
-      return CXCursor_NotImplemented;
     }
-    return CXCursor_NotImplemented;
+    break;
+
+  case Result::RK_Macro:
+    CursorKind = CXCursor_MacroDefinition;
+    break;
+      
+  case Result::RK_Keyword:
+    CursorKind = CXCursor_NotImplemented;
+    break;
+      
+  case Result::RK_Pattern:
+    // Do nothing: Patterns can come with cursor kinds!
+    break;
   }
 }
 
@@ -595,7 +609,7 @@ CIndexCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
                                                        unsigned NumResults) {
   // Print the results.
   for (unsigned I = 0; I != NumResults; ++I) {
-    WriteUnsigned(OS, getCursorKindForCompletionResult(Results[I]));
+    WriteUnsigned(OS, Results[I].CursorKind);
     WriteUnsigned(OS, Results[I].Priority);
     CodeCompletionString *CCS = Results[I].CreateCodeCompletionString(SemaRef);
     assert(CCS && "No code-completion string?");
index f5c85ad99cd5e80662c5b59e9f002c7771310e10..95f08bdf017e60d74c8d9aa8c4e968ffb28f88ae 100644 (file)
@@ -269,8 +269,10 @@ void Sema::ActOnEndOfTranslationUnit() {
                                           true)), 
                               UnusedFileScopedDecls.end());
 
-  if (!CompleteTranslationUnit)
+  if (!CompleteTranslationUnit) {
+    TUScope = 0;
     return;
+  }
 
   // Check for #pragma weak identifiers that were never declared
   // FIXME: This will cause diagnostics to be emitted in a non-determinstic
@@ -340,6 +342,8 @@ void Sema::ActOnEndOfTranslationUnit() {
       Diag((*I)->getLocation(), diag::warn_unused_variable)
             << cast<VarDecl>(*I)->getDeclName();
   }
+
+  TUScope = 0;
 }
 
 
index b8fb04425ec9b5a98968960a7e039d76aeb9311d..31185683a1a15d73248a4c8f8a87a750d1c2d9ab 100644 (file)
@@ -4336,3 +4336,23 @@ void Sema::CodeCompleteObjCMethodDeclSelector(Scope *S,
                             CodeCompletionContext::CCC_Other,
                             Results.data(),Results.size());
 }
+
+void Sema::GatherGlobalCodeCompletions(
+                 llvm::SmallVectorImpl<CodeCompleteConsumer::Result> &Results) {
+  ResultBuilder Builder(*this);
+
+#if 0
+  // FIXME: We need a name lookup that means "look for everything", 
+  CodeCompletionDeclConsumer Consumer(Builder, 
+                                      Context.getTranslationUnitDecl());
+  LookupVisibleDecls(Context.getTranslationUnitDecl(), LookupOrdinaryName, 
+                     Consumer);
+#endif
+  
+  if (!CodeCompleter || CodeCompleter->includeMacros())
+    AddMacroResults(PP, Builder);
+  
+  Results.clear();
+  Results.insert(Results.end(), 
+                 Builder.data(), Builder.data() + Builder.size());
+}
index 9a898e152a7fe88b35278288e1c23ebb8c681de0..d0974db31fb335be7272ae990cabd9cb754362e3 100644 (file)
@@ -16,6 +16,7 @@ void f2() {
 }
 
 // RUN: c-index-test -code-completion-at=%s:7:1 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:7:1 %s | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: macro definition:{TypedText FOO}{LeftParen (}{Placeholder Arg1}{Comma , }{Placeholder Arg2}{RightParen )}
 // RUN: c-index-test -code-completion-at=%s:13:13 %s | FileCheck -check-prefix=CHECK-CC2 %s
 // RUN: c-index-test -code-completion-at=%s:14:8 %s | FileCheck -check-prefix=CHECK-CC2 %s
index ef6bfb8a79740035ddf5ad7843c11681b231baaa..1b2f036fa413c67a8aedf7805e77096c99563322 100644 (file)
@@ -34,6 +34,8 @@ static unsigned getDefaultParsingOptions() {
 
   if (getenv("CINDEXTEST_EDITING"))
     options |= clang_defaultEditingTranslationUnitOptions();
+  if (getenv("CINDEXTEST_COMPLETION_CACHING"))
+    options |= CXTranslationUnit_CacheCompletionResults;
   
   return options;
 }
index 93fdd96c241634f30186d617faa23efaa9afb9d3..f7dce99c723d9ee9ef4240160b35a831de17b740 100644 (file)
@@ -1220,7 +1220,9 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx,
   bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble;
   bool CompleteTranslationUnit
     = ((options & CXTranslationUnit_Incomplete) == 0);
-
+  bool CacheCodeCompetionResults
+    = options & CXTranslationUnit_CacheCompletionResults;
+  
   // Configure the diagnostics.
   DiagnosticOptions DiagOpts;
   llvm::IntrusiveRefCntPtr<Diagnostic> Diags;
@@ -1276,7 +1278,8 @@ CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx,
                                    RemappedFiles.size(),
                                    /*CaptureDiagnostics=*/true,
                                    PrecompilePreamble,
-                                   CompleteTranslationUnit));
+                                   CompleteTranslationUnit,
+                                   CacheCodeCompetionResults));
 
     if (NumErrors != Diags->getNumErrors()) {
       // Make sure to check that 'Unit' is non-NULL.
index 813f82a4ccc204579e45e301222844166b1d77d2..f0e90212f7fb3227c150812c7c85559be79de131 100644 (file)
@@ -541,14 +541,6 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx,
 
 } // 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;
@@ -567,8 +559,7 @@ namespace {
         CXStoredCodeCompletionString *StoredCompletion
           = new CXStoredCodeCompletionString(Results[I].Priority);
         (void)Results[I].CreateCodeCompletionString(S, StoredCompletion);
-        AllocatedResults.Results[I].CursorKind 
-          = getCursorKindForCompletionResult(Results[I]);
+        AllocatedResults.Results[I].CursorKind = Results[I].CursorKind;
         AllocatedResults.Results[I].CompletionString = StoredCompletion;
       }
     }