From 63745d5935db0e1e974dbc87459a9a7df34dbdad Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 21 Jul 2011 01:05:26 +0000 Subject: [PATCH] New libclang API to expose container type for code completion, from Connor Wakamo! llvm-svn: 135651 --- clang/include/clang-c/Index.h | 33 +++++++ .../include/clang/Sema/CodeCompleteConsumer.h | 3 +- clang/lib/Sema/SemaCodeComplete.cpp | 17 +++- clang/test/Index/code-completion.cpp | 3 + clang/test/Index/complete-member-access.m | 15 +++ clang/test/Index/complete-objc-message.m | 10 ++ clang/tools/c-index-test/c-index-test.c | 24 ++++- clang/tools/libclang/CIndexCodeCompletion.cpp | 92 +++++++++++++++++-- clang/tools/libclang/libclang.darwin.exports | 2 + clang/tools/libclang/libclang.exports | 2 + 10 files changed, 188 insertions(+), 13 deletions(-) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 4852ded7f883..236594ab7c3b 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -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); /** * @} diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h index e14c593eaadf..9b5564853c24 100644 --- a/clang/include/clang/Sema/CodeCompleteConsumer.h +++ b/clang/include/clang/Sema/CodeCompleteConsumer.h @@ -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; diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index b555c8a9aa1f..a65026e4b0fb 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -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()); } diff --git a/clang/test/Index/code-completion.cpp b/clang/test/Index/code-completion.cpp index f75d61f1b04b..5789d3b9fa07 100644 --- a/clang/test/Index/code-completion.cpp +++ b/clang/test/Index/code-completion.cpp @@ -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 )} diff --git a/clang/test/Index/complete-member-access.m b/clang/test/Index/complete-member-access.m index bd68deb5d115..48156d93ffba 100644 --- a/clang/test/Index/complete-member-access.m +++ b/clang/test/Index/complete-member-access.m @@ -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 diff --git a/clang/test/Index/complete-objc-message.m b/clang/test/Index/complete-objc-message.m index 0ea33850560e..e80243a30057 100644 --- a/clang/test/Index/complete-objc-message.m +++ b/clang/test/Index/complete-objc-message.m @@ -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} diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index 6e0aaac73cc3..63031ce178d6 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -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); diff --git a/clang/tools/libclang/CIndexCodeCompletion.cpp b/clang/tools/libclang/CIndexCodeCompletion.cpp index 832e2f2f7147..8695cb44b3fd 100644 --- a/clang/tools/libclang/CIndexCodeCompletion.cpp +++ b/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -15,7 +15,11 @@ #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 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 = kind; - AllocatedResults.Contexts = getContextsForContextKind(kind, S); + 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()) + 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()) + D = ObjPtr->getInterfaceDecl(); + // Get the @interface declaration for an Objective-C object type + else if (const ObjCObjectType *Obj = baseType->getAs()) + D = Obj->getInterface(); + // Get the class for a C++ injected-class-name + else if (const InjectedClassNameType *Injected = + baseType->getAs()) + D = Injected->getDecl(); + } + + 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(ResultsIn); + if (!Results) + return CXCursor_InvalidCode; + + if (IsIncomplete != NULL) { + *IsIncomplete = Results->ContainerIsIncomplete; + } + + return Results->ContainerKind; +} + +CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results = + static_cast(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 diff --git a/clang/tools/libclang/libclang.darwin.exports b/clang/tools/libclang/libclang.darwin.exports index bfc5be9c078b..87c52566acdb 100644 --- a/clang/tools/libclang/libclang.darwin.exports +++ b/clang/tools/libclang/libclang.darwin.exports @@ -6,6 +6,8 @@ _clang_annotateTokens _clang_codeCompleteAt _clang_codeCompleteGetDiagnostic _clang_codeCompleteGetNumDiagnostics +_clang_codeCompleteGetContainerKind +_clang_codeCompleteGetContainerUSR _clang_codeCompleteGetContexts _clang_constructUSR_ObjCCategory _clang_constructUSR_ObjCClass diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports index 47b703011c76..f82c190afeee 100644 --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -6,6 +6,8 @@ clang_annotateTokens clang_codeCompleteAt clang_codeCompleteGetDiagnostic clang_codeCompleteGetNumDiagnostics +clang_codeCompleteGetContainerKind +clang_codeCompleteGetContainerUSR clang_codeCompleteGetContexts clang_constructUSR_ObjCCategory clang_constructUSR_ObjCClass