From ba1030698dbc276db86b11c5329a1edee8a1805e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 27 Mar 2012 23:34:16 +0000 Subject: [PATCH] Introduce a new libclang API to determine the parent context of a code completion item. For example, if the code completion itself represents a declaration in a namespace (say, std::vector), then this API retrieves the cursor kind and name of the namespace (std). Implements . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153545 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 20 +++++ include/clang/Sema/CodeCompleteConsumer.h | 75 +++++++++++++++---- lib/AST/Decl.cpp | 2 +- lib/Sema/CodeCompleteConsumer.cpp | 89 ++++++++++++++++++++--- lib/Sema/SemaCodeComplete.cpp | 56 ++++++-------- test/Index/complete-exprs.cpp | 17 +++++ test/Index/complete-method-decls.m | 14 ++-- test/Index/complete-objc-message.m | 12 +-- test/Index/complete-qualified.cpp | 8 +- tools/c-index-test/c-index-test.c | 17 ++++- tools/libclang/CIndexCodeCompletion.cpp | 16 +++- tools/libclang/libclang.exports | 1 + 12 files changed, 250 insertions(+), 77 deletions(-) diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 904585f108..8d7d8595bd 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -3542,6 +3542,26 @@ CINDEX_LINKAGE CXString clang_getCompletionAnnotation(CXCompletionString completion_string, unsigned annotation_number); +/** + * \brief Retrieve the parent context of the given completion string. + * + * The parent context of a completion string is the semantic parent of + * the declaration (if any) that the code completion represents. For example, + * a code completion for an Objective-C method would have the method's class + * or protocol as its context. + * + * \param completion_string The code completion string whose parent is + * being queried. + * + * \param kind If non-NULL, will be set to the kind of the parent context, + * or CXCursor_NotImplemented if there is no context. + * + * \param Returns the name of the completion parent, e.g., "NSObject" if + * the completion string represents a method in the NSObject class. + */ +CINDEX_LINKAGE CXString +clang_getCompletionParent(CXCompletionString completion_string, + enum CXCursorKind *kind); /** * \brief Retrieve a completion string for an arbitrary declaration or macro * definition cursor. diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 8d35eceb93..ff2ca871f2 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -434,17 +434,24 @@ private: unsigned NumAnnotations : 16; /// \brief The priority of this code-completion string. - unsigned Priority : 30; + unsigned Priority : 16; /// \brief The availability of this code-completion result. unsigned Availability : 2; + /// \brief The kind of the parent context. + unsigned ParentKind : 14; + + /// \brief The name of the parent context. + StringRef ParentName; + CodeCompletionString(const CodeCompletionString &); // DO NOT IMPLEMENT CodeCompletionString &operator=(const CodeCompletionString &); // DITTO CodeCompletionString(const Chunk *Chunks, unsigned NumChunks, unsigned Priority, CXAvailabilityKind Availability, - const char **Annotations, unsigned NumAnnotations); + const char **Annotations, unsigned NumAnnotations, + CXCursorKind ParentKind, StringRef ParentName); ~CodeCompletionString() { } friend class CodeCompletionBuilder; @@ -477,6 +484,16 @@ public: /// \brief Retrieve the annotation string specified by \c AnnotationNr. const char *getAnnotation(unsigned AnnotationNr) const; + /// \brief Retrieve parent context's cursor kind. + CXCursorKind getParentContextKind() const { + return (CXCursorKind)ParentKind; + } + + /// \brief Retrieve the name of the parent context. + StringRef getParentContextName() const { + return ParentName; + } + /// \brief Retrieve a string representation of the code completion string, /// which is mainly useful for debugging. std::string getAsString() const; @@ -484,6 +501,8 @@ public: /// \brief An allocator used specifically for the purpose of code completion. class CodeCompletionAllocator : public llvm::BumpPtrAllocator { + llvm::DenseMap ParentNames; + public: /// \brief Copy the given string into this allocator. const char *CopyString(StringRef String); @@ -500,6 +519,12 @@ public: const char *CopyString(const std::string &String) { return CopyString(StringRef(String)); } + + /// \brief Retrieve the mapping from known parent declaration contexts to + /// the (already copied) strings associated with each context. + llvm::DenseMap &getParentNames() { + return ParentNames; + } }; } // end namespace clang @@ -521,7 +546,9 @@ private: CodeCompletionAllocator &Allocator; unsigned Priority; CXAvailabilityKind Availability; - + CXCursorKind ParentKind; + StringRef ParentName; + /// \brief The chunks stored in this string. SmallVector Chunks; @@ -529,12 +556,13 @@ private: public: CodeCompletionBuilder(CodeCompletionAllocator &Allocator) - : Allocator(Allocator), Priority(0), Availability(CXAvailability_Available){ - } + : Allocator(Allocator), Priority(0), Availability(CXAvailability_Available), + ParentKind(CXCursor_NotImplemented) { } CodeCompletionBuilder(CodeCompletionAllocator &Allocator, unsigned Priority, CXAvailabilityKind Availability) - : Allocator(Allocator), Priority(Priority), Availability(Availability) { } + : Allocator(Allocator), Priority(Priority), Availability(Availability), + ParentKind(CXCursor_NotImplemented) { } /// \brief Retrieve the allocator into which the code completion /// strings should be allocated. @@ -570,6 +598,12 @@ public: void AddChunk(CodeCompletionString::ChunkKind CK, const char *Text = ""); void AddAnnotation(const char *A) { Annotations.push_back(A); } + + /// \brief Add the parent context information to this code completion. + void addParentContext(DeclContext *DC); + + CXCursorKind getParentKind() const { return ParentKind; } + StringRef getParentName() const { return ParentName; } }; /// \brief Captures a result of code completion. @@ -586,11 +620,11 @@ public: /// \brief The kind of result stored here. ResultKind Kind; - union { - /// \brief When Kind == RK_Declaration, the declaration we are referring - /// to. - NamedDecl *Declaration; + /// \brief When Kind == RK_Declaration or RK_Pattern, the declaration we are + /// referring to. In the latter case, the declaration might be NULL. + NamedDecl *Declaration; + union { /// \brief When Kind == RK_Keyword, the string representing the keyword /// or symbol's spelling. const char *Keyword; @@ -655,7 +689,7 @@ public: /// \brief Build a result that refers to a keyword or symbol. CodeCompletionResult(const char *Keyword, unsigned Priority = CCP_Keyword) - : Kind(RK_Keyword), Keyword(Keyword), Priority(Priority), + : Kind(RK_Keyword), Declaration(0), Keyword(Keyword), Priority(Priority), Availability(CXAvailability_Available), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), @@ -665,7 +699,7 @@ public: /// \brief Build a result that refers to a macro. CodeCompletionResult(IdentifierInfo *Macro, unsigned Priority = CCP_Macro) - : Kind(RK_Macro), Macro(Macro), Priority(Priority), + : Kind(RK_Macro), Declaration(0), Macro(Macro), Priority(Priority), Availability(CXAvailability_Available), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), @@ -677,8 +711,9 @@ public: CodeCompletionResult(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern, CXCursorKind CursorKind = CXCursor_NotImplemented, - CXAvailabilityKind Availability = CXAvailability_Available) - : Kind(RK_Pattern), Pattern(Pattern), Priority(Priority), + CXAvailabilityKind Availability = CXAvailability_Available, + NamedDecl *D = 0) + : Kind(RK_Pattern), Declaration(D), Pattern(Pattern), Priority(Priority), CursorKind(CursorKind), Availability(Availability), StartParameter(0), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), @@ -686,6 +721,18 @@ public: { } + /// \brief Build a result that refers to a pattern with an associated + /// declaration. + CodeCompletionResult(CodeCompletionString *Pattern, NamedDecl *D, + unsigned Priority) + : Kind(RK_Pattern), Declaration(D), Pattern(Pattern), Priority(Priority), + Availability(CXAvailability_Available), StartParameter(0), + Hidden(false), QualifierIsInformative(false), + StartsNestedNameSpecifier(false), AllParametersAreInformative(false), + DeclaringEntity(false), Qualifier(0) { + computeCursorKindAndAvailability(); + } + /// \brief Retrieve the declaration stored in this result. NamedDecl *getDeclaration() const { assert(Kind == RK_Declaration && "Not a declaration result"); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 3b7113d354..e476bfb6f1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -845,7 +845,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { } std::string NamedDecl::getQualifiedNameAsString() const { - return getQualifiedNameAsString(getASTContext().getLangOpts()); + return getQualifiedNameAsString(getASTContext().getPrintingPolicy()); } std::string NamedDecl::getQualifiedNameAsString(const PrintingPolicy &P) const { diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index caebe3a221..dbc9b0080a 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -192,9 +192,12 @@ CodeCompletionString::CodeCompletionString(const Chunk *Chunks, unsigned Priority, CXAvailabilityKind Availability, const char **Annotations, - unsigned NumAnnotations) - : NumChunks(NumChunks), NumAnnotations(NumAnnotations) - , Priority(Priority), Availability(Availability) + unsigned NumAnnotations, + CXCursorKind ParentKind, + StringRef ParentName) + : NumChunks(NumChunks), NumAnnotations(NumAnnotations), + Priority(Priority), Availability(Availability), ParentKind(ParentKind), + ParentName(ParentName) { assert(NumChunks <= 0xffff); assert(NumAnnotations <= 0xffff); @@ -272,7 +275,8 @@ CodeCompletionString *CodeCompletionBuilder::TakeString() { CodeCompletionString *Result = new (Mem) CodeCompletionString(Chunks.data(), Chunks.size(), Priority, Availability, - Annotations.data(), Annotations.size()); + Annotations.data(), Annotations.size(), + ParentKind, ParentName); Chunks.clear(); return Result; } @@ -311,6 +315,70 @@ void CodeCompletionBuilder::AddChunk(CodeCompletionString::ChunkKind CK, Chunks.push_back(Chunk(CK, Text)); } +void CodeCompletionBuilder::addParentContext(DeclContext *DC) { + if (DC->isTranslationUnit()) { + ParentKind = CXCursor_TranslationUnit; + return; + } + + if (DC->isFunctionOrMethod()) + return; + + NamedDecl *ND = dyn_cast(DC); + if (!ND) + return; + + ParentKind = getCursorKindForDecl(ND); + + // Check whether we've already cached the parent name. + StringRef &CachedParentName = Allocator.getParentNames()[DC]; + if (!CachedParentName.empty()) { + ParentName = CachedParentName; + return; + } + + // Find the interesting names. + llvm::SmallVector Contexts; + while (DC && !DC->isFunctionOrMethod()) { + if (NamedDecl *ND = dyn_cast(DC)) { + if (ND->getIdentifier()) + Contexts.push_back(DC); + } + + DC = DC->getParent(); + } + + { + llvm::SmallString<128> S; + llvm::raw_svector_ostream OS(S); + bool First = true; + for (unsigned I = Contexts.size(); I != 0; --I) { + if (First) + First = false; + else { + OS << "::"; + } + + DeclContext *CurDC = Contexts[I-1]; + if (ObjCCategoryImplDecl *CatImpl = dyn_cast(CurDC)) + CurDC = CatImpl->getCategoryDecl(); + + if (ObjCCategoryDecl *Cat = dyn_cast(CurDC)) { + ObjCInterfaceDecl *Interface = Cat->getClassInterface(); + if (!Interface) + return; + + OS << Interface->getName() << '(' << Cat->getName() << ')'; + } else { + OS << cast(CurDC)->getName(); + } + } + + ParentName = Allocator.CopyString(OS.str()); + CachedParentName = ParentName; + } +} + unsigned CodeCompletionResult::getPriorityFromDecl(NamedDecl *ND) { if (!ND) return CCP_Unlikely; @@ -444,6 +512,13 @@ static AvailabilityResult getDeclAvailability(Decl *D) { void CodeCompletionResult::computeCursorKindAndAvailability(bool Accessible) { switch (Kind) { + case RK_Pattern: + if (!Declaration) { + // Do nothing: Patterns can come with cursor kinds! + break; + } + // Fall through + case RK_Declaration: { // Set the availability based on attributes. switch (getDeclAvailability(Declaration)) { @@ -488,11 +563,7 @@ void CodeCompletionResult::computeCursorKindAndAvailability(bool Accessible) { case RK_Keyword: Availability = CXAvailability_Available; CursorKind = CXCursor_NotImplemented; - break; - - case RK_Pattern: - // Do nothing: Patterns can come with cursor kinds! - break; + break; } if (!Accessible) diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index b33c1697bf..3fd66ec042 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -2454,6 +2454,13 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx, if (Kind == RK_Pattern) { Pattern->Priority = Priority; Pattern->Availability = Availability; + + if (Declaration) { + Result.addParentContext(Declaration->getDeclContext()); + Pattern->ParentKind = Result.getParentKind(); + Pattern->ParentName = Result.getParentName(); + } + return Pattern; } @@ -2509,7 +2516,8 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx, assert(Kind == RK_Declaration && "Missed a result kind?"); NamedDecl *ND = Declaration; - + Result.addParentContext(ND->getDeclContext()); + if (StartsNestedNameSpecifier) { Result.AddTypedTextChunk( Result.getAllocator().CopyString(ND->getNameAsString())); @@ -3023,7 +3031,9 @@ static void MaybeAddOverrideCalls(Sema &S, DeclContext *InContext, Builder.AddChunk(CodeCompletionString::CK_RightParen); Results.AddResult(CodeCompletionResult(Builder.TakeString(), CCP_SuperCompletion, - CXCursor_CXXMethod)); + CXCursor_CXXMethod, + CXAvailability_Available, + Overridden)); Results.Ignore(Overridden); } } @@ -3342,28 +3352,8 @@ static void AddObjCProperties(ObjCContainerDecl *Container, Builder.AddTypedTextChunk( Results.getAllocator().CopyString(Name->getName())); - CXAvailabilityKind Availability = CXAvailability_Available; - switch (M->getAvailability()) { - case AR_Available: - case AR_NotYetIntroduced: - Availability = CXAvailability_Available; - break; - - case AR_Deprecated: - Availability = CXAvailability_Deprecated; - break; - - case AR_Unavailable: - Availability = CXAvailability_NotAvailable; - break; - } - - Results.MaybeAddResult(Result(Builder.TakeString(), - CCP_MemberDeclaration + CCD_MethodAsProperty, - M->isInstanceMethod() - ? CXCursor_ObjCInstanceMethodDecl - : CXCursor_ObjCClassMethodDecl, - Availability), + Results.MaybeAddResult(Result(Builder.TakeString(), *M, + CCP_MemberDeclaration + CCD_MethodAsProperty), CurContext); } } @@ -4028,7 +4018,8 @@ void Sema::CodeCompleteNamespaceDecl(Scope *S) { // namespace to the list of results. Results.EnterNewScope(); for (std::map::iterator - NS = OrigToLatest.begin(), NSEnd = OrigToLatest.end(); + NS = OrigToLatest.begin(), + NSEnd = OrigToLatest.end(); NS != NSEnd; ++NS) Results.AddResult(CodeCompletionResult(NS->second, 0), CurContext, 0, false); @@ -4188,7 +4179,9 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Results.AddResult(CodeCompletionResult(Builder.TakeString(), SawLastInitializer? CCP_NextInitializer : CCP_MemberDeclaration, - CXCursor_MemberRef)); + CXCursor_MemberRef, + CXAvailability_Available, + *Field)); SawLastInitializer = false; } Results.ExitScope(); @@ -5057,10 +5050,8 @@ static ObjCMethodDecl *AddSuperSendCompletion(Sema &S, bool NeedSuperKeyword, } } - Results.AddResult(CodeCompletionResult(Builder.TakeString(), CCP_SuperCompletion, - SuperMethod->isInstanceMethod() - ? CXCursor_ObjCInstanceMethodDecl - : CXCursor_ObjCClassMethodDecl)); + Results.AddResult(CodeCompletionResult(Builder.TakeString(), SuperMethod, + CCP_SuperCompletion)); return SuperMethod; } @@ -6747,10 +6738,7 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, if (!M->second.second) Priority += CCD_InBaseClass; - Results.AddResult(Result(Builder.TakeString(), Priority, - Method->isInstanceMethod() - ? CXCursor_ObjCInstanceMethodDecl - : CXCursor_ObjCClassMethodDecl)); + Results.AddResult(Result(Builder.TakeString(), Method, Priority)); } // Add Key-Value-Coding and Key-Value-Observing accessor methods for all of diff --git a/test/Index/complete-exprs.cpp b/test/Index/complete-exprs.cpp index 6b7f2f9e91..de3aac52c0 100644 --- a/test/Index/complete-exprs.cpp +++ b/test/Index/complete-exprs.cpp @@ -34,6 +34,17 @@ void X::f() const { } +namespace N { + int x; + class C { + int member; + + int f(int param) { + return member; + } + }; +} + // RUN: c-index-test -code-completion-at=%s:20:2 %s -std=c++0x | FileCheck -check-prefix=CHECK-CC1 %s // RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:20:2 -std=c++0x %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: NotImplemented:{ResultType size_t}{TypedText alignof}{LeftParen (}{Placeholder type}{RightParen )} (40) @@ -65,3 +76,9 @@ void X::f() const { // RUN: c-index-test -code-completion-at=%s:34:1 %s -std=c++0x | FileCheck -check-prefix=CHECK-CC4 %s // CHECK-CC4: NotImplemented:{ResultType const X *}{TypedText this} (40) + +// RUN: c-index-test -code-completion-at=%s:43:14 %s | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5: FieldDecl:{ResultType int}{TypedText member} (8) (parent: ClassDecl 'N::C') +// CHECK-CC5: ParmDecl:{ResultType int}{TypedText param} (8) +// CHECK-CC5: StructDecl:{TypedText X} (50) (parent: TranslationUnit '(null)') +// CHECK-CC5: VarDecl:{ResultType int}{TypedText x} (12) (parent: Namespace 'N') diff --git a/test/Index/complete-method-decls.m b/test/Index/complete-method-decls.m index 2ab1197953..e26359cabb 100644 --- a/test/Index/complete-method-decls.m +++ b/test/Index/complete-method-decls.m @@ -69,11 +69,11 @@ @end // RUN: c-index-test -code-completion-at=%s:17:3 %s | FileCheck -check-prefix=CHECK-CC1 %s -// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText abc} -// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text int}{RightParen )}{TypedText getInt} -// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText getSelf} -// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText initWithInt}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x} -// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText initWithTwoInts}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{TypedText second:}{LeftParen (}{Text int}{RightParen )}{Text y} +// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText abc} (40) (parent: ObjCProtocolDecl 'P1') +// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text int}{RightParen )}{TypedText getInt} (40) (parent: ObjCProtocolDecl 'P1') +// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText getSelf} (40) (parent: ObjCProtocolDecl 'P1') +// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText initWithInt}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x} (40) (parent: ObjCProtocolDecl 'P1') +// CHECK-CC1: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText initWithTwoInts}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{TypedText second:}{LeftParen (}{Text int}{RightParen )}{Text y} (40) (parent: ObjCProtocolDecl 'P1') // RUN: c-index-test -code-completion-at=%s:17:7 %s | FileCheck -check-prefix=CHECK-CC2 %s // CHECK-CC2: ObjCInstanceMethodDecl:{TypedText abc} // CHECK-CC2-NEXT: ObjCInstanceMethodDecl:{TypedText getSelf} @@ -94,8 +94,8 @@ // CHECK-CC4: ObjCInstanceMethodDecl:{LeftParen (}{Text id}{RightParen )}{TypedText initWithTwoInts}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{TypedText second:}{LeftParen (}{Text int}{RightParen )}{Text y}{HorizontalSpace }{LeftBrace {}{VerticalSpace // CHECK-CC4: ObjCInstanceMethodDecl:{LeftParen (}{Text int}{RightParen )}{TypedText setValue}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{LeftBrace {}{VerticalSpace // RUN: env CINDEXTEST_CODE_COMPLETE_PATTERNS=1 c-index-test -code-completion-at=%s:33:8 %s | FileCheck -check-prefix=CHECK-CC5 %s -// CHECK-CC5: ObjCInstanceMethodDecl:{TypedText getInt}{HorizontalSpace }{LeftBrace {}{VerticalSpace -// CHECK-CC5: ObjCInstanceMethodDecl:{TypedText getSecondValue}{HorizontalSpace }{LeftBrace {}{VerticalSpace +// CHECK-CC5: ObjCInstanceMethodDecl:{TypedText getInt}{HorizontalSpace }{LeftBrace {}{VerticalSpace }{Text return}{HorizontalSpace }{Placeholder expression}{SemiColon ;}{VerticalSpace }{RightBrace }} (42) (parent: ObjCProtocolDecl 'P1') +// CHECK-CC5: ObjCInstanceMethodDecl:{TypedText getSecondValue}{HorizontalSpace }{LeftBrace {}{VerticalSpace }{Text return}{HorizontalSpace }{Placeholder expression}{SemiColon ;}{VerticalSpace }{RightBrace }} (40) (parent: ObjCInterfaceDecl 'B') // CHECK-CC5-NOT: {TypedText getSelf}{HorizontalSpace }{LeftBrace {}{VerticalSpace // CHECK-CC5: ObjCInstanceMethodDecl:{TypedText setValue}{TypedText :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{LeftBrace {}{VerticalSpace // RUN: env CINDEXTEST_CODE_COMPLETE_PATTERNS=1 c-index-test -code-completion-at=%s:37:7 %s | FileCheck -check-prefix=CHECK-CC6 %s diff --git a/test/Index/complete-objc-message.m b/test/Index/complete-objc-message.m index fc25ef8bc6..1835c3edc5 100644 --- a/test/Index/complete-objc-message.m +++ b/test/Index/complete-objc-message.m @@ -190,11 +190,11 @@ void test_DO(DO *d, A* a) { } // RUN: c-index-test -code-completion-at=%s:23:19 %s | FileCheck -check-prefix=CHECK-CC1 %s -// CHECK-CC1: {TypedText categoryClassMethod} -// CHECK-CC1: {TypedText classMethod1:}{Placeholder (id)}{HorizontalSpace }{TypedText withKeyword:}{Placeholder (int)} -// CHECK-CC1: {TypedText classMethod2} -// CHECK-CC1: {TypedText new} -// CHECK-CC1: {TypedText protocolClassMethod} +// CHECK-CC1: {TypedText categoryClassMethod} (35) (parent: ObjCCategoryDecl 'Foo(FooTestCategory)') +// CHECK-CC1: {TypedText classMethod1:}{Placeholder (id)}{HorizontalSpace }{TypedText withKeyword:}{Placeholder (int)} (35) (parent: ObjCInterfaceDecl 'Foo') +// CHECK-CC1: {TypedText classMethod2} (35) (parent: ObjCInterfaceDecl 'Foo') +// CHECK-CC1: {TypedText new} (35) (parent: ObjCInterfaceDecl 'Foo') +// CHECK-CC1: {TypedText protocolClassMethod} (37) (parent: ObjCProtocolDecl 'FooTestProtocol') // CHECK-CC1: Completion contexts: // CHECK-CC1-NEXT: Objective-C class method // CHECK-CC1-NEXT: Container Kind: ObjCInterfaceDecl @@ -309,7 +309,7 @@ void test_DO(DO *d, A* a) { // RUN: c-index-test -code-completion-at=%s:170:16 %s | FileCheck -check-prefix=CHECK-CLASS-RESULT %s // CHECK-CLASS-RESULT: ObjCClassMethodDecl:{ResultType void}{TypedText class_method3} (35) -// CHECK-CLASS-RESULT: ObjCClassMethodDecl:{ResultType void}{TypedText class_method4} (35) +// CHECK-CLASS-RESULT: ObjCClassMethodDecl:{ResultType void}{TypedText class_method4} (35) (parent: ObjCCategoryDecl 'A(Cat)') // RUN: c-index-test -code-completion-at=%s:181:4 %s | FileCheck -check-prefix=CHECK-BLOCK-RECEIVER %s // CHECK-BLOCK-RECEIVER: ObjCInterfaceDecl:{TypedText A} (50) diff --git a/test/Index/complete-qualified.cpp b/test/Index/complete-qualified.cpp index 20f5105a8c..f5c032c23d 100644 --- a/test/Index/complete-qualified.cpp +++ b/test/Index/complete-qualified.cpp @@ -14,7 +14,7 @@ void foo() Foo:: // RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CC1 %s -// CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35) -// CHECK-CC1: ClassDecl:{TypedText Foo} (35) -// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )} (35) -// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (35) +// CHECK-CC1: FieldDecl:{ResultType C}{TypedText c} (35) (parent: ClassDecl 'Foo') +// CHECK-CC1: ClassDecl:{TypedText Foo} (35) (parent: ClassDecl 'Foo') +// CHECK-CC1: CXXMethod:{ResultType Foo &}{TypedText operator=}{LeftParen (}{Placeholder const Foo &}{RightParen )} (35) (parent: ClassDecl 'Foo') +// CHECK-CC1: CXXDestructor:{ResultType void}{TypedText ~Foo}{LeftParen (}{RightParen )} (35) (parent: ClassDecl 'Foo') diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 67de8508cd..39bb8ebed0 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -1082,7 +1082,9 @@ void print_completion_result(CXCompletionResult *completion_result, FILE *file = (FILE *)client_data; CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind); unsigned annotationCount; - + enum CXCursorKind ParentKind; + CXString ParentName; + fprintf(file, "%s:", clang_getCString(ks)); clang_disposeString(ks); @@ -1121,6 +1123,19 @@ void print_completion_result(CXCompletionResult *completion_result, fprintf(file, ")"); } + if (!getenv("CINDEXTEST_NO_COMPLETION_PARENTS")) { + ParentName = clang_getCompletionParent(completion_result->CompletionString, + &ParentKind); + if (ParentKind != CXCursor_NotImplemented) { + CXString KindSpelling = clang_getCursorKindSpelling(ParentKind); + fprintf(file, " (parent: %s '%s')", + clang_getCString(KindSpelling), + clang_getCString(ParentName)); + clang_disposeString(KindSpelling); + } + clang_disposeString(ParentName); + } + fprintf(file, "\n"); } diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp index 10f2411320..1b4a7e4800 100644 --- a/tools/libclang/CIndexCodeCompletion.cpp +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -213,7 +213,21 @@ CXString clang_getCompletionAnnotation(CXCompletionString completion_string, : createCXString((const char *) 0); } - +CXString +clang_getCompletionParent(CXCompletionString completion_string, + CXCursorKind *kind) { + if (kind) + *kind = CXCursor_NotImplemented; + + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr) + return createCXString((const char *)0); + + if (kind) + *kind = CCStr->getParentContextKind(); + return createCXString(CCStr->getParentContextName(), /*DupString=*/false); +} + /// \brief The CXCodeCompleteResults structure we allocate internally; /// the client only sees the initial CXCodeCompleteResults structure. struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 1900ac86c0..afdb0c90bc 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -65,6 +65,7 @@ clang_getCompletionChunkCompletionString clang_getCompletionChunkKind clang_getCompletionChunkText clang_getCompletionNumAnnotations +clang_getCompletionParent clang_getCompletionPriority clang_getCursor clang_getCursorAvailability -- 2.40.0