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
<rdar://problem/11121951>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153545 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index 904585f..8d7d859 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3543,6 +3543,26 @@
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 8d35ece..ff2ca87 100644
--- a/include/clang/Sema/CodeCompleteConsumer.h
+++ b/include/clang/Sema/CodeCompleteConsumer.h
@@ -434,17 +434,24 @@
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 @@
/// \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 @@
/// \brief An allocator used specifically for the purpose of code completion.
class CodeCompletionAllocator : public llvm::BumpPtrAllocator {
+ llvm::DenseMap<DeclContext *, StringRef> ParentNames;
+
public:
/// \brief Copy the given string into this allocator.
const char *CopyString(StringRef String);
@@ -500,6 +519,12 @@
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<DeclContext *, StringRef> &getParentNames() {
+ return ParentNames;
+ }
};
} // end namespace clang
@@ -521,7 +546,9 @@
CodeCompletionAllocator &Allocator;
unsigned Priority;
CXAvailabilityKind Availability;
-
+ CXCursorKind ParentKind;
+ StringRef ParentName;
+
/// \brief The chunks stored in this string.
SmallVector<Chunk, 4> Chunks;
@@ -529,12 +556,13 @@
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 @@
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 @@
/// \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 @@
/// \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 @@
/// \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 @@
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 @@
{
}
+ /// \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 3b7113d..e476bfb 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -845,7 +845,7 @@
}
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 caebe3a..dbc9b00 100644
--- a/lib/Sema/CodeCompleteConsumer.cpp
+++ b/lib/Sema/CodeCompleteConsumer.cpp
@@ -192,9 +192,12 @@
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 *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 @@
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<NamedDecl>(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<DeclContext *, 2> Contexts;
+ while (DC && !DC->isFunctionOrMethod()) {
+ if (NamedDecl *ND = dyn_cast<NamedDecl>(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<ObjCCategoryImplDecl>(CurDC))
+ CurDC = CatImpl->getCategoryDecl();
+
+ if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(CurDC)) {
+ ObjCInterfaceDecl *Interface = Cat->getClassInterface();
+ if (!Interface)
+ return;
+
+ OS << Interface->getName() << '(' << Cat->getName() << ')';
+ } else {
+ OS << cast<NamedDecl>(CurDC)->getName();
+ }
+ }
+
+ ParentName = Allocator.CopyString(OS.str());
+ CachedParentName = ParentName;
+ }
+}
+
unsigned CodeCompletionResult::getPriorityFromDecl(NamedDecl *ND) {
if (!ND)
return CCP_Unlikely;
@@ -444,6 +512,13 @@
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 @@
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 b33c169..3fd66ec 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -2454,6 +2454,13 @@
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 @@
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 @@
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 @@
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 @@
// namespace to the list of results.
Results.EnterNewScope();
for (std::map<NamespaceDecl *, NamespaceDecl *>::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 @@
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 @@
}
}
- 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 @@
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 6b7f2f9..de3aac5 100644
--- a/test/Index/complete-exprs.cpp
+++ b/test/Index/complete-exprs.cpp
@@ -34,6 +34,17 @@
}
+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 @@
// 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 2ab1197..e26359c 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 fc25ef8..1835c3e 100644
--- a/test/Index/complete-objc-message.m
+++ b/test/Index/complete-objc-message.m
@@ -190,11 +190,11 @@
}
// 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 @@
// 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 20f5105..f5c032c 100644
--- a/test/Index/complete-qualified.cpp
+++ b/test/Index/complete-qualified.cpp
@@ -14,7 +14,7 @@
Foo::
// RUN: c-index-test -code-completion-at=%s:14:8 %s -o - | FileCheck -check-prefix=CC1 %s
-// CHECK-CC1: FieldDecl:{ResultType C<Foo, class Bar>}{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<Foo, class Bar>}{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 67de850..39bb8eb 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1082,7 +1082,9 @@
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 @@
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 10f2411..1b4a7e4 100644
--- a/tools/libclang/CIndexCodeCompletion.cpp
+++ b/tools/libclang/CIndexCodeCompletion.cpp
@@ -213,7 +213,21 @@
: 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 1900ac8..afdb0c9 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -65,6 +65,7 @@
clang_getCompletionChunkKind
clang_getCompletionChunkText
clang_getCompletionNumAnnotations
+clang_getCompletionParent
clang_getCompletionPriority
clang_getCursor
clang_getCursorAvailability