New libclang API to expose container type for code completion, from

Connor Wakamo!

llvm-svn: 135651
This commit is contained in:
Douglas Gregor 2011-07-21 01:05:26 +00:00
parent 858e9f083d
commit 63745d5935
10 changed files with 188 additions and 13 deletions

View 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);
/**
* @}

View 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;

View 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());
}

View 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 )}

View 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

View 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}

View 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);

View File

@ -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<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 = 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<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();
}
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

View 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

View 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