]> granicus.if.org Git - clang/commitdiff
New libclang API to expose container type for code completion, from
authorDouglas Gregor <dgregor@apple.com>
Thu, 21 Jul 2011 01:05:26 +0000 (01:05 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 21 Jul 2011 01:05:26 +0000 (01:05 +0000)
Connor Wakamo!

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

include/clang-c/Index.h
include/clang/Sema/CodeCompleteConsumer.h
lib/Sema/SemaCodeComplete.cpp
test/Index/code-completion.cpp
test/Index/complete-member-access.m
test/Index/complete-objc-message.m
tools/c-index-test/c-index-test.c
tools/libclang/CIndexCodeCompletion.cpp
tools/libclang/libclang.darwin.exports
tools/libclang/libclang.exports

index 4852ded7f8834b6f601991e39d40b69cc435f4af..236594ab7c3bf4c361f793db5b177c218b58b6f4 100644 (file)
@@ -3144,6 +3144,39 @@ CXDiagnostic clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *Results,
 CINDEX_LINKAGE
 unsigned long long clang_codeCompleteGetContexts(
                                                 CXCodeCompleteResults *Results);
+
+/**
+ * \brief Returns the cursor kind for the container for the current code
+ * completion context. The container is only guaranteed to be set for
+ * contexts where a container exists (i.e. member accesses or Objective-C
+ * message sends); if there is not a container, this function will return
+ * CXCursor_InvalidCode.
+ *
+ * \param Results the code completion results to query
+ *
+ * \param IsIncomplete on return, this value will be false if Clang has complete
+ * information about the container. If Clang does not have complete
+ * information, this value will be true.
+ *
+ * \returns the container kind, or CXCursor_InvalidCode if there is not a
+ * container
+ */
+CINDEX_LINKAGE
+enum CXCursorKind clang_codeCompleteGetContainerKind(
+                                                 CXCodeCompleteResults *Results,
+                                                     unsigned *IsIncomplete);
+
+/**
+ * \brief Returns the USR for the container for the current code completion
+ * context. If there is not a container for the current context, this
+ * function will return the empty string.
+ *
+ * \param Results the code completion results to query
+ *
+ * \returns the USR for the container
+ */
+CINDEX_LINKAGE
+CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *Results);
   
 /**
  * @}
index e14c593eaadf878c76314e38cfbceeb1d91c28ed..9b5564853c24ce21c62dbe4eda7f26cb85064af9 100644 (file)
@@ -275,7 +275,8 @@ public:
   /// \brief Construct a new code-completion context of the given kind.
   CodeCompletionContext(enum Kind Kind, QualType T) : Kind(Kind) { 
     if (Kind == CCC_DotMemberAccess || Kind == CCC_ArrowMemberAccess ||
-        Kind == CCC_ObjCPropertyAccess)
+        Kind == CCC_ObjCPropertyAccess || Kind == CCC_ObjCClassMessage ||
+        Kind == CCC_ObjCInstanceMessage)
       BaseType = T;
     else
       PreferredType = T;
index b555c8a9aa1f247cc24efe77c58671c76371e480..a65026e4b0fb927d1b83fccdfb36c4b054b98a4e 100644 (file)
@@ -4955,8 +4955,13 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver,
                                         unsigned NumSelIdents,
                                         bool AtArgumentExpression,
                                         bool IsSuper) {
+  
+  QualType T = this->GetTypeFromParser(Receiver);
+  
   ResultBuilder Results(*this, CodeCompleter->getAllocator(),
-                        CodeCompletionContext::CCC_ObjCClassMessage);
+              CodeCompletionContext(CodeCompletionContext::CCC_ObjCClassMessage,
+                                    T));
+    
   AddClassMessageCompletions(*this, S, Receiver, SelIdents, NumSelIdents, 
                              AtArgumentExpression, IsSuper, Results);
   
@@ -4967,7 +4972,7 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver,
   // our preferred type, improving completion results.
   if (AtArgumentExpression) {
     QualType PreferredType = getPreferredArgumentTypeForMessageSend(Results, 
-                                                                    NumSelIdents);
+                                                                  NumSelIdents);
     if (PreferredType.isNull())
       CodeCompleteOrdinaryName(S, PCC_Expression);
     else
@@ -4976,7 +4981,7 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver,
   }
 
   HandleCodeCompleteResults(this, CodeCompleter, 
-                            CodeCompletionContext::CCC_ObjCClassMessage,
+                            Results.getCompletionContext(),
                             Results.data(), Results.size());
 }
 
@@ -5019,7 +5024,9 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver,
 
   // Build the set of methods we can see.
   ResultBuilder Results(*this, CodeCompleter->getAllocator(),
-                        CodeCompletionContext::CCC_ObjCInstanceMessage);
+           CodeCompletionContext(CodeCompletionContext::CCC_ObjCInstanceMessage,
+                                 ReceiverType));
+  
   Results.EnterNewScope();
 
   // If this is a send-to-super, try to add the special "super" send 
@@ -5132,7 +5139,7 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver,
   }
   
   HandleCodeCompleteResults(this, CodeCompleter, 
-                            CodeCompletionContext::CCC_ObjCInstanceMessage,
+                            Results.getCompletionContext(),
                             Results.data(),Results.size());
 }
 
index f75d61f1b04b8413dbe858c746c1d496eecf3ad3..5789d3b9fa07e3a7e251d1fc75ebc8ae557469ae 100644 (file)
@@ -54,6 +54,9 @@ Z::operator int() const {
 // CHECK-MEMBER: CXXDestructor:{ResultType void}{TypedText ~Z}{LeftParen (}{RightParen )}
 // CHECK-MEMBER: Completion contexts:
 // CHECK-MEMBER-NEXT: Dot member access
+// CHECK-MEMBER-NEXT: Container Kind: StructDecl
+// CHECK-MEMBER-NEXT: Container is complete
+// CHECK-MEMBER-NEXT: Container USR: c:@S@Z
 
 // CHECK-OVERLOAD: NotImplemented:{ResultType int &}{Text overloaded}{LeftParen (}{Text Z z}{Comma , }{CurrentParameter int second}{RightParen )}
 // CHECK-OVERLOAD: NotImplemented:{ResultType float &}{Text overloaded}{LeftParen (}{Text int i}{Comma , }{CurrentParameter long second}{RightParen )}
index bd68deb5d115309bbda7635375dcf606452a8a25..48156d93ffba59f0d5b8834db8bdd4b6cd639fb0 100644 (file)
@@ -37,11 +37,26 @@ int test_more_props(Sub *s) {
 // RUN: c-index-test -code-completion-at=%s:21:7 %s | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText prop1}
 // CHECK-CC1: ObjCPropertyDecl:{ResultType float}{TypedText ProtoProp}
+// CHECK-CC1: Completion contexts:
+// CHECK-CC1-NEXT: Objective-C property access
+// CHECK-CC1-NEXT: Container Kind: ObjCInterfaceDecl
+// CHECK-CC1-NEXT: Container is complete
+// CHECK-CC1-NEXT: Container USR: c:objc(cs)Int
 // RUN: c-index-test -code-completion-at=%s:22:8 %s | FileCheck -check-prefix=CHECK-CC2 %s
 // CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText IVar} (35)
 // CHECK-CC2: ObjCIvarDecl:{ResultType int}{TypedText SuperIVar} (37)
+// CHECK-CC2: Completion contexts:
+// CHECK-CC2-NEXT: Arrow member access
+// CHECK-CC2-NEXT: Container Kind: ObjCInterfaceDecl
+// CHECK-CC2-NEXT: Container is complete
+// CHECK-CC2-NEXT: Container USR: c:objc(cs)Int
 // RUN: c-index-test -code-completion-at=%s:34:12 %s | FileCheck -check-prefix=CHECK-CC3 %s
 // CHECK-CC3: ObjCInstanceMethodDecl:{ResultType int}{TypedText myOtherPropLikeThing} (37)
 // CHECK-CC3: ObjCPropertyDecl:{ResultType int}{TypedText myProp} (35)
 // CHECK-CC3: ObjCPropertyDecl:{ResultType int}{TypedText prop1} (35)
 // CHECK-CC3: ObjCPropertyDecl:{ResultType float}{TypedText ProtoProp} (35)
+// CHECK-CC3: Completion contexts:
+// CHECK-CC3-NEXT: Objective-C property access
+// CHECK-CC3-NEXT: Container Kind: ObjCInterfaceDecl
+// CHECK-CC3-NEXT: Container is complete
+// CHECK-CC3-NEXT: Container USR: c:objc(cs)Sub
\ No newline at end of file
index 0ea33850560ec2c3d404be98bc56d22497f46375..e80243a3005729a070f3a2a8c1f5ebd1c55e66f1 100644 (file)
@@ -187,10 +187,20 @@ void test_block_invoke(A *(^block1)(int),
 // CHECK-CC1: {TypedText classMethod2}
 // CHECK-CC1: {TypedText new}
 // CHECK-CC1: {TypedText protocolClassMethod}
+// CHECK-CC1: Completion contexts:
+// CHECK-CC1-NEXT: Objective-C class method
+// CHECK-CC1-NEXT: Container Kind: ObjCInterfaceDecl
+// CHECK-CC1-NEXT: Container is complete
+// CHECK-CC1-NEXT: Container USR: c:objc(cs)Foo
 // RUN: c-index-test -code-completion-at=%s:24:8 %s | FileCheck -check-prefix=CHECK-CC2 %s
 // CHECK-CC2: {TypedText categoryInstanceMethod}
 // CHECK-CC2: {TypedText instanceMethod1}
 // CHECK-CC2: {TypedText protocolInstanceMethod:}{Placeholder (int)}
+// CHECK-CC2: Completion contexts:
+// CHECK-CC2-NEXT: Objective-C instance method
+// CHECK-CC2-NEXT: Container Kind: ObjCInterfaceDecl
+// CHECK-CC2-NEXT: Container is complete
+// CHECK-CC2-NEXT: Container USR: c:objc(cs)Foo
 // RUN: c-index-test -code-completion-at=%s:61:16 %s | FileCheck -check-prefix=CHECK-CC3 %s
 // CHECK-CC3: ObjCClassMethodDecl:{ResultType int}{TypedText MyClassMethod:}{Placeholder (id)}
 // CHECK-CC3: ObjCClassMethodDecl:{ResultType int}{TypedText MyPrivateMethod}
index 6e0aaac73cc37fb9f003df2069205c84463c501b..63031ce178d684f3a4212916868eab8c468b7e1e 100644 (file)
@@ -1171,8 +1171,9 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
   }
 
   if (results) {
-    unsigned i, n = results->NumResults;
+    unsigned i, n = results->NumResults, containerIsIncomplete = 0;
     unsigned long long contexts;
+    enum CXCursorKind containerKind;
     if (!timing_only) {      
       /* Sort the code-completion results based on the typed text. */
       clang_sortCodeCompletionResults(results->Results, results->NumResults);
@@ -1190,6 +1191,27 @@ int perform_code_completion(int argc, const char **argv, int timing_only) {
     contexts = clang_codeCompleteGetContexts(results);
     print_completion_contexts(contexts, stdout);
     
+    containerKind = clang_codeCompleteGetContainerKind(results, &containerIsIncomplete);
+    
+    if (containerKind != CXCursor_InvalidCode) {
+      /* We have found a container */
+      CXString containerUSR, containerKindSpelling;
+      containerKindSpelling = clang_getCursorKindSpelling(containerKind);
+      printf("Container Kind: %s\n", clang_getCString(containerKindSpelling));
+      clang_disposeString(containerKindSpelling);
+      
+      if (containerIsIncomplete) {
+        printf("Container is incomplete\n");
+      }
+      else {
+        printf("Container is complete\n");
+      }
+      
+      containerUSR = clang_codeCompleteGetContainerUSR(results);
+      printf("Container USR: %s\n", clang_getCString(containerUSR));
+      clang_disposeString(containerUSR);
+    }
+    
     clang_disposeCodeCompleteResults(results);
   }
   clang_disposeTranslationUnit(TU);
index 832e2f2f7147f10e048a306ae9f8528c2d21d800..8695cb44b3fde2abf9beefd4265a98e948d72eaf 100644 (file)
 #include "CIndexer.h"
 #include "CXTranslationUnit.h"
 #include "CXString.h"
+#include "CXCursor.h"
 #include "CIndexDiagnostic.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Frontend/ASTUnit.h"
@@ -242,6 +246,11 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
   /// \brief A bitfield representing the acceptable completions for the
   /// current context.
   unsigned long long Contexts;
+  
+  enum CXCursorKind ContainerKind;
+  CXString ContainerUSR;
+  
+  unsigned ContainerIsIncomplete;
 };
 
 /// \brief Tracks the number of code-completion result objects that are 
@@ -267,6 +276,8 @@ AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults(
 AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
   delete [] Results;
   
+  clang_disposeString(ContainerUSR);
+  
   for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I)
     TemporaryFiles[I].eraseFromDisk();
   for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I)
@@ -455,10 +466,12 @@ namespace {
   class CaptureCompletionResults : public CodeCompleteConsumer {
     AllocatedCXCodeCompleteResults &AllocatedResults;
     llvm::SmallVector<CXCompletionResult, 16> StoredResults;
+    CXTranslationUnit *TU;
   public:
-    CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results)
+    CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results,
+                             CXTranslationUnit *TranslationUnit)
       : CodeCompleteConsumer(true, false, true, false), 
-        AllocatedResults(Results) { }
+        AllocatedResults(Results), TU(TranslationUnit) { }
     ~CaptureCompletionResults() { Finish(); }
     
     virtual void ProcessCodeCompleteResults(Sema &S, 
@@ -477,10 +490,53 @@ namespace {
         StoredResults.push_back(R);
       }
       
-      enum CodeCompletionContext::Kind kind = Context.getKind();
+      enum CodeCompletionContext::Kind contextKind = Context.getKind();
+      
+      AllocatedResults.ContextKind = contextKind;
+      AllocatedResults.Contexts = getContextsForContextKind(contextKind, S);
+      
+      QualType baseType = Context.getBaseType();
+      NamedDecl *D = NULL;
+      
+      if (!baseType.isNull()) {
+        // Get the declaration for a class/struct/union/enum type
+        if (const TagType *Tag = baseType->getAs<TagType>())
+          D = Tag->getDecl();
+        // Get the @interface declaration for a (possibly-qualified) Objective-C
+        // object pointer type, e.g., NSString*
+        else if (const ObjCObjectPointerType *ObjPtr = 
+                 baseType->getAs<ObjCObjectPointerType>())
+          D = ObjPtr->getInterfaceDecl();
+        // Get the @interface declaration for an Objective-C object type
+        else if (const ObjCObjectType *Obj = baseType->getAs<ObjCObjectType>())
+          D = Obj->getInterface();
+        // Get the class for a C++ injected-class-name
+        else if (const InjectedClassNameType *Injected =
+                 baseType->getAs<InjectedClassNameType>())
+          D = Injected->getDecl();
+      }
       
-      AllocatedResults.ContextKind = kind;
-      AllocatedResults.Contexts = getContextsForContextKind(kind, S);
+      if (D != NULL) {
+        CXCursor cursor = cxcursor::MakeCXCursor(D, *TU);
+        
+        CXCursorKind cursorKind = clang_getCursorKind(cursor);
+        CXString cursorUSR = clang_getCursorUSR(cursor);
+        
+        AllocatedResults.ContainerKind = cursorKind;
+        AllocatedResults.ContainerUSR = cursorUSR;
+        const Type *type = baseType.getTypePtrOrNull();
+        if (type != NULL) {
+          AllocatedResults.ContainerIsIncomplete = type->isIncompleteType();
+        }
+        else {
+          AllocatedResults.ContainerIsIncomplete = 1;
+        }
+      }
+      else {
+        AllocatedResults.ContainerKind = CXCursor_InvalidCode;
+        AllocatedResults.ContainerUSR = createCXString("");
+        AllocatedResults.ContainerIsIncomplete = 1;
+      }
     }
     
     virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
@@ -571,7 +627,7 @@ void clang_codeCompleteAt_Impl(void *UserData) {
   Results->NumResults = 0;
   
   // Create a code-completion consumer to capture the results.
-  CaptureCompletionResults Capture(*Results);
+  CaptureCompletionResults Capture(*Results, &TU);
 
   // Perform completion.
   AST->CodeComplete(complete_filename, complete_line, complete_column,
@@ -731,6 +787,30 @@ clang_codeCompleteGetContexts(CXCodeCompleteResults *ResultsIn) {
   return Results->Contexts;
 }
 
+enum CXCursorKind clang_codeCompleteGetContainerKind(
+                                               CXCodeCompleteResults *ResultsIn,
+                                                     unsigned *IsIncomplete) {
+  AllocatedCXCodeCompleteResults *Results =
+    static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn);
+  if (!Results)
+    return CXCursor_InvalidCode;
+  
+  if (IsIncomplete != NULL) {
+    *IsIncomplete = Results->ContainerIsIncomplete;
+  }
+  
+  return Results->ContainerKind;
+}
+  
+CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *ResultsIn) {
+  AllocatedCXCodeCompleteResults *Results =
+    static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn);
+  if (!Results)
+    return createCXString("");
+  
+  return createCXString(clang_getCString(Results->ContainerUSR));
+}
+  
 } // end extern "C"
 
 /// \brief Simple utility function that appends a \p New string to the given
index bfc5be9c078b92bc388b0a725dbb34d6ad8fe0a0..87c52566acdb257b4754ef46b67c9af39eee2825 100644 (file)
@@ -6,6 +6,8 @@ _clang_annotateTokens
 _clang_codeCompleteAt
 _clang_codeCompleteGetDiagnostic
 _clang_codeCompleteGetNumDiagnostics
+_clang_codeCompleteGetContainerKind
+_clang_codeCompleteGetContainerUSR
 _clang_codeCompleteGetContexts
 _clang_constructUSR_ObjCCategory
 _clang_constructUSR_ObjCClass
index 47b703011c766f8393b3da012980db506f0ddf81..f82c190afeee734ad21b93160b5aa6a7de7c7cb3 100644 (file)
@@ -6,6 +6,8 @@ clang_annotateTokens
 clang_codeCompleteAt
 clang_codeCompleteGetDiagnostic
 clang_codeCompleteGetNumDiagnostics
+clang_codeCompleteGetContainerKind
+clang_codeCompleteGetContainerUSR
 clang_codeCompleteGetContexts
 clang_constructUSR_ObjCCategory
 clang_constructUSR_ObjCClass