diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 727dd7929c0c..0e30a2b0bae8 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -2953,9 +2953,14 @@ void Sema::CodeCompletePostfixExpression(Scope *S, ExprResult E) { CodeCompleteObjCInstanceMessage(S, E.take(), 0, 0, false); } +/// \brief The set of properties that have already been added, referenced by +/// property name. +typedef llvm::SmallPtrSet AddedPropertiesSet; + static void AddObjCProperties(ObjCContainerDecl *Container, bool AllowCategories, DeclContext *CurContext, + AddedPropertiesSet &AddedProperties, ResultBuilder &Results) { typedef CodeCompletionResult Result; @@ -2963,40 +2968,46 @@ static void AddObjCProperties(ObjCContainerDecl *Container, for (ObjCContainerDecl::prop_iterator P = Container->prop_begin(), PEnd = Container->prop_end(); P != PEnd; - ++P) - Results.MaybeAddResult(Result(*P, 0), CurContext); + ++P) { + if (AddedProperties.insert(P->getIdentifier())) + Results.MaybeAddResult(Result(*P, 0), CurContext); + } // Add properties in referenced protocols. if (ObjCProtocolDecl *Protocol = dyn_cast(Container)) { for (ObjCProtocolDecl::protocol_iterator P = Protocol->protocol_begin(), PEnd = Protocol->protocol_end(); P != PEnd; ++P) - AddObjCProperties(*P, AllowCategories, CurContext, Results); + AddObjCProperties(*P, AllowCategories, CurContext, AddedProperties, + Results); } else if (ObjCInterfaceDecl *IFace = dyn_cast(Container)){ if (AllowCategories) { // Look through categories. for (ObjCCategoryDecl *Category = IFace->getCategoryList(); Category; Category = Category->getNextClassCategory()) - AddObjCProperties(Category, AllowCategories, CurContext, Results); + AddObjCProperties(Category, AllowCategories, CurContext, + AddedProperties, Results); } // Look through protocols. for (ObjCInterfaceDecl::all_protocol_iterator I = IFace->all_referenced_protocol_begin(), E = IFace->all_referenced_protocol_end(); I != E; ++I) - AddObjCProperties(*I, AllowCategories, CurContext, Results); + AddObjCProperties(*I, AllowCategories, CurContext, AddedProperties, + Results); // Look in the superclass. if (IFace->getSuperClass()) AddObjCProperties(IFace->getSuperClass(), AllowCategories, CurContext, - Results); + AddedProperties, Results); } else if (const ObjCCategoryDecl *Category = dyn_cast(Container)) { // Look through protocols. for (ObjCCategoryDecl::protocol_iterator P = Category->protocol_begin(), PEnd = Category->protocol_end(); P != PEnd; ++P) - AddObjCProperties(*P, AllowCategories, CurContext, Results); + AddObjCProperties(*P, AllowCategories, CurContext, AddedProperties, + Results); } } @@ -3056,18 +3067,20 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE, } } else if (!IsArrow && BaseType->getAsObjCInterfacePointerType()) { // Objective-C property reference. + AddedPropertiesSet AddedProperties; // Add property results based on our interface. const ObjCObjectPointerType *ObjCPtr = BaseType->getAsObjCInterfacePointerType(); assert(ObjCPtr && "Non-NULL pointer guaranteed above!"); - AddObjCProperties(ObjCPtr->getInterfaceDecl(), true, CurContext, Results); + AddObjCProperties(ObjCPtr->getInterfaceDecl(), true, CurContext, + AddedProperties, Results); // Add properties from the protocols in a qualified interface. for (ObjCObjectPointerType::qual_iterator I = ObjCPtr->qual_begin(), E = ObjCPtr->qual_end(); I != E; ++I) - AddObjCProperties(*I, true, CurContext, Results); + AddObjCProperties(*I, true, CurContext, AddedProperties, Results); } else if ((IsArrow && BaseType->isObjCObjectPointerType()) || (!IsArrow && BaseType->isObjCObjectType())) { // Objective-C instance variable access. @@ -5168,14 +5181,15 @@ void Sema::CodeCompleteObjCPropertyDefinition(Scope *S, Decl *ObjCImpDecl) { Results.Ignore(PropertyImpl->getPropertyDecl()); // Add any properties that we find. + AddedPropertiesSet AddedProperties; Results.EnterNewScope(); if (ObjCImplementationDecl *ClassImpl = dyn_cast(Container)) AddObjCProperties(ClassImpl->getClassInterface(), false, CurContext, - Results); + AddedProperties, Results); else AddObjCProperties(cast(Container)->getCategoryDecl(), - false, CurContext, Results); + false, CurContext, AddedProperties, Results); Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, diff --git a/clang/test/Index/complete-properties.m b/clang/test/Index/complete-properties.m index 80e10e7822c7..0a19f1113345 100644 --- a/clang/test/Index/complete-properties.m +++ b/clang/test/Index/complete-properties.m @@ -21,6 +21,14 @@ @dynamic Prop4; @end +@interface I3 : I2 +@property id Prop3; +@end + +id test(I3 *i3) { + return i3.Prop3; +} + // RUN: c-index-test -code-completion-at=%s:20:13 %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText Prop0} // CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText Prop1} @@ -38,3 +46,11 @@ // RUN: c-index-test -code-completion-at=%s:21:10 %s | FileCheck -check-prefix=CHECK-CC4 %s // CHECK-CC4: ObjCPropertyDecl:{ResultType int}{TypedText Prop0} // CHECK-CC4-NEXT: ObjCPropertyDecl:{ResultType id}{TypedText Prop4} + +// RUN: c-index-test -code-completion-at=%s:29:13 %s | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5: ObjCPropertyDecl:{ResultType int}{TypedText Prop0} (35) +// CHECK-CC5-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText Prop1} (35) +// CHECK-CC5-NEXT: ObjCPropertyDecl:{ResultType float}{TypedText Prop2} (35) +// CHECK-CC5-NEXT: ObjCPropertyDecl:{ResultType id}{TypedText Prop3} (35) +// CHECK-CC5-NEXT: ObjCPropertyDecl:{ResultType id}{TypedText Prop4} (35) +