]> granicus.if.org Git - clang/commitdiff
When caching code completions for global declarations, keep track of
authorDouglas Gregor <dgregor@apple.com>
Mon, 16 Aug 2010 18:08:11 +0000 (18:08 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 16 Aug 2010 18:08:11 +0000 (18:08 +0000)
the usage type of each declaration result, then compare those types to
the preferred type of the completion. This provides parity in the
priority calculation between the code-completion results produced
directly from Sema and those cached by ASTUnit.

For the standard Cocoa.h (+ others) example, there's a penalty of 3-4
hundredeths of a second when caching the global results (for ~31,000
results), because we need an ASTContext-agnostic representation of
types for the comparison, and therefore we use... strings. Eventually,
we'd like to implement a more efficient ASTContext-agnostic encoding
of types.

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

include/clang/AST/CanonicalType.h
include/clang/AST/TypeOrdering.h
include/clang/Frontend/ASTUnit.h
lib/Frontend/ASTUnit.cpp
test/Index/complete-exprs.c

index a1023a2810daa8374477dc46cc240edf297262e0..dad4dfc926e169cef15be6f15baa3770331acdd0 100644 (file)
@@ -703,9 +703,9 @@ inline CanQual<Type> CanQual<T>::getNonReferenceType() const {
 template<typename T>
 CanQual<T> CanQual<T>::getFromOpaquePtr(void *Ptr) {
   CanQual<T> Result;
-  Result.Stored.setFromOpaqueValue(Ptr);
-  assert((!Result || Result.Stored.isCanonical())
-         && "Type is not canonical!");
+  Result.Stored = QualType::getFromOpaquePtr(Ptr);
+  assert((!Result || Result.Stored.getAsOpaquePtr() == (void*)-1 ||
+          Result.Stored.isCanonical()) && "Type is not canonical!");
   return Result;
 }
 
index 1a050d29c8602fef1a270d69f01e63c80b3863e0..7cf0d5e999b6ab9726753321eb2e190e18faf5c1 100644 (file)
@@ -17,6 +17,7 @@
 #define LLVM_CLANG_TYPE_ORDERING_H
 
 #include "clang/AST/Type.h"
+#include "clang/AST/CanonicalType.h"
 #include <functional>
 
 namespace clang {
@@ -51,6 +52,26 @@ namespace llvm {
       return LHS == RHS;
     }
   };
+
+  template<> struct DenseMapInfo<clang::CanQualType> {
+    static inline clang::CanQualType getEmptyKey() { 
+      return clang::CanQualType(); 
+    }
+    
+    static inline clang::CanQualType getTombstoneKey() {
+      using clang::CanQualType;
+      return CanQualType::getFromOpaquePtr(reinterpret_cast<clang::Type *>(-1));
+    }
+    
+    static unsigned getHashValue(clang::CanQualType Val) {
+      return (unsigned)((uintptr_t)Val.getAsOpaquePtr()) ^
+      ((unsigned)((uintptr_t)Val.getAsOpaquePtr() >> 9));
+    }
+    
+    static bool isEqual(clang::CanQualType LHS, clang::CanQualType RHS) {
+      return LHS == RHS;
+    }
+  };
 }
 
 #endif
index f8859425ff998a7ea59243023a93bdac82416920..77a641a2c5d036ab0b9a96bbead4fe69c7f604bf 100644 (file)
@@ -235,12 +235,30 @@ public:
     
     /// \brief The simplified type class for a non-macro completion result.
     SimplifiedTypeClass TypeClass;
+    
+    /// \brief The type of a non-macro completion result, stored as a unique
+    /// integer used by the string map of cached completion types.
+    ///
+    /// This value will be zero if the type is not known, or a unique value
+    /// determined by the formatted type string. Se \c CachedCompletionTypes
+    /// for more information.
+    unsigned Type;
   };
   
+  /// \brief Retrieve the mapping from formatted type names to unique type
+  /// identifiers.
+  llvm::StringMap<unsigned> &getCachedCompletionTypes() { 
+    return CachedCompletionTypes; 
+  }
+  
 private:
   /// \brief The set of cached code-completion results.
   std::vector<CachedCodeCompletionResult> CachedCompletionResults;
   
+  /// \brief A mapping from the formatted type name to a unique number for that
+  /// type, which is used for type equality comparisons.
+  llvm::StringMap<unsigned> CachedCompletionTypes;
+  
   /// \brief Cache any "global" code-completion results, so that we can avoid
   /// recomputing them with each completion.
   void CacheCodeCompletionResults();
index b54162f8d7d89b210dc037eee659b195cfb16cbc..86588c93752f234c120e72bd1e0a9c52399efb1d 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/DeclVisitor.h"
+#include "clang/AST/TypeOrdering.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
@@ -182,6 +183,8 @@ void ASTUnit::CacheCodeCompletionResults() {
   TheSema->GatherGlobalCodeCompletions(Results);
   
   // Translate global code completions into cached completions.
+  llvm::DenseMap<CanQualType, unsigned> CompletionTypes;
+  
   for (unsigned I = 0, N = Results.size(); I != N; ++I) {
     switch (Results[I].Kind) {
     case Result::RK_Declaration: {
@@ -192,13 +195,30 @@ void ASTUnit::CacheCodeCompletionResults() {
       CachedResult.Priority = Results[I].Priority;
       CachedResult.Kind = Results[I].CursorKind;
 
+      // Keep track of the type of this completion in an ASTContext-agnostic 
+      // way.
       QualType UsageType = getDeclUsageType(*Ctx, Results[I].Declaration);
-      if (UsageType.isNull())
+      if (UsageType.isNull()) {
         CachedResult.TypeClass = STC_Void;
-      else {
-        CachedResult.TypeClass
-          = getSimplifiedTypeClass(Ctx->getCanonicalType(UsageType));
+        CachedResult.Type = 0;
+      } else {
+        CanQualType CanUsageType
+          = Ctx->getCanonicalType(UsageType.getUnqualifiedType());
+        CachedResult.TypeClass = getSimplifiedTypeClass(CanUsageType);
+
+        // Determine whether we have already seen this type. If so, we save
+        // ourselves the work of formatting the type string by using the 
+        // temporary, CanQualType-based hash table to find the associated value.
+        unsigned &TypeValue = CompletionTypes[CanUsageType];
+        if (TypeValue == 0) {
+          TypeValue = CompletionTypes.size();
+          CachedCompletionTypes[QualType(CanUsageType).getAsString()]
+            = TypeValue;
+        }
+        
+        CachedResult.Type = TypeValue;
       }
+      
       CachedCompletionResults.push_back(CachedResult);
       break;
     }
@@ -224,6 +244,7 @@ void ASTUnit::CacheCodeCompletionResults() {
       CachedResult.Priority = Results[I].Priority;
       CachedResult.Kind = Results[I].CursorKind;
       CachedResult.TypeClass = STC_Void;
+      CachedResult.Type = 0;
       CachedCompletionResults.push_back(CachedResult);
       break;
     }
@@ -239,6 +260,7 @@ void ASTUnit::ClearCachedCompletionResults() {
   for (unsigned I = 0, N = CachedCompletionResults.size(); I != N; ++I)
     delete CachedCompletionResults[I].Completion;
   CachedCompletionResults.clear();
+  CachedCompletionTypes.clear();
 }
 
 namespace {
@@ -1432,13 +1454,21 @@ namespace {
           if (C->Kind == CXCursor_MacroDefinition) {
             Priority = getMacroUsagePriority(C->Completion->getTypedText(),
                                Context.getPreferredType()->isAnyPointerType());
-          } else {
+          } else if (C->Type) {
             CanQualType Expected
-              = S.Context.getCanonicalType(Context.getPreferredType());
+              = S.Context.getCanonicalType(
+                               Context.getPreferredType().getUnqualifiedType());
             SimplifiedTypeClass ExpectedSTC = getSimplifiedTypeClass(Expected);
             if (ExpectedSTC == C->TypeClass) {
-              // FIXME: How can we check for an exact match?
-              Priority /= CCF_SimilarTypeMatch;
+              // We know this type is similar; check for an exact match.
+              llvm::StringMap<unsigned> &CachedCompletionTypes
+                = AST.getCachedCompletionTypes();
+              llvm::StringMap<unsigned>::iterator Pos
+                = CachedCompletionTypes.find(QualType(Expected).getAsString());
+              if (Pos != CachedCompletionTypes.end() && Pos->second == C->Type)
+                Priority /= CCF_ExactTypeMatch;
+              else
+                Priority /= CCF_SimilarTypeMatch;
             }
           }
         }
index 3605420956e449f7d04a47753a5f8ef3486ec075..5eead7e1fde1532bac65ca06590569e47db8af89 100644 (file)
@@ -20,10 +20,9 @@ const char *str = "Hello, \nWorld";
 // CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (2)
 // CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
 // RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1a %s
-// FIXME: Priorities aren't right
 // CHECK-CC1a: ParmDecl:{ResultType int}{TypedText j} (2)
 // CHECK-CC1a: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
-// CHECK-CC1a: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (25)
+// CHECK-CC1a: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12)
 // CHECK-CC1a: macro definition:{TypedText __VERSION__} (70)
 // 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