Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -9662,6 +9662,9 @@ bool AtParameterName, ParsedType ReturnType, ArrayRef SelIdents); + void CodeCompleteObjCClassPropertyRefExpr(Scope *S, IdentifierInfo &ClassName, + SourceLocation ClassNameLoc, + bool IsBaseExprStatement); void CodeCompletePreprocessorDirective(bool InConditional); void CodeCompleteInPreprocessorConditionalExclusion(Scope *S); void CodeCompletePreprocessorMacroName(bool IsDefinition); Index: cfe/trunk/lib/Parse/ParseExpr.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseExpr.cpp +++ cfe/trunk/lib/Parse/ParseExpr.cpp @@ -885,7 +885,13 @@ // Allow the base to be 'super' if in an objc-method. (&II == Ident_super && getCurScope()->isInObjcMethodScope()))) { ConsumeToken(); - + + if (Tok.is(tok::code_completion) && &II != Ident_super) { + Actions.CodeCompleteObjCClassPropertyRefExpr( + getCurScope(), II, ILoc, ExprStatementTokLoc == ILoc); + cutOffParsing(); + return ExprError(); + } // Allow either an identifier or the keyword 'class' (in C++). if (Tok.isNot(tok::identifier) && !(getLangOpts().CPlusPlus && Tok.is(tok::kw_class))) { Index: cfe/trunk/lib/Sema/SemaCodeComplete.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaCodeComplete.cpp +++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp @@ -3654,22 +3654,20 @@ Builder.AddChunk(CodeCompletionString::CK_RightParen); } -static void AddObjCProperties(const CodeCompletionContext &CCContext, - ObjCContainerDecl *Container, - bool AllowCategories, bool AllowNullaryMethods, - DeclContext *CurContext, - AddedPropertiesSet &AddedProperties, - ResultBuilder &Results, - bool IsBaseExprStatement = false) { +static void AddObjCProperties( + const CodeCompletionContext &CCContext, ObjCContainerDecl *Container, + bool AllowCategories, bool AllowNullaryMethods, DeclContext *CurContext, + AddedPropertiesSet &AddedProperties, ResultBuilder &Results, + bool IsBaseExprStatement = false, bool IsClassProperty = false) { typedef CodeCompletionResult Result; // Retrieve the definition. Container = getContainerDef(Container); // Add properties in this container. - for (const auto *P : Container->instance_properties()) { + const auto AddProperty = [&](const ObjCPropertyDecl *P) { if (!AddedProperties.insert(P->getIdentifier()).second) - continue; + return; // FIXME: Provide block invocation completion for non-statement // expressions. @@ -3677,7 +3675,7 @@ !IsBaseExprStatement) { Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), CurContext); - continue; + return; } // Block setter and invocation completion is provided only when we are able @@ -3689,7 +3687,7 @@ if (!BlockLoc) { Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), CurContext); - continue; + return; } // The default completion result for block properties should be the block @@ -3727,64 +3725,89 @@ Results.getBasePriority(P) + CCD_BlockPropertySetter), CurContext); } + }; + + if (IsClassProperty) { + for (const auto *P : Container->class_properties()) + AddProperty(P); + } else { + for (const auto *P : Container->instance_properties()) + AddProperty(P); } - // Add nullary methods + // Add nullary methods or implicit class properties if (AllowNullaryMethods) { ASTContext &Context = Container->getASTContext(); PrintingPolicy Policy = getCompletionPrintingPolicy(Results.getSema()); - for (auto *M : Container->methods()) { - if (M->getSelector().isUnarySelector()) - if (IdentifierInfo *Name = M->getSelector().getIdentifierInfoForSlot(0)) - if (AddedProperties.insert(Name).second) { - CodeCompletionBuilder Builder(Results.getAllocator(), - Results.getCodeCompletionTUInfo()); - AddResultTypeChunk(Context, Policy, M, CCContext.getBaseType(), - Builder); - Builder.AddTypedTextChunk( - Results.getAllocator().CopyString(Name->getName())); - - Results.MaybeAddResult(Result(Builder.TakeString(), M, - CCP_MemberDeclaration + CCD_MethodAsProperty), - CurContext); - } + // Adds a method result + const auto AddMethod = [&](const ObjCMethodDecl *M) { + IdentifierInfo *Name = M->getSelector().getIdentifierInfoForSlot(0); + if (!Name) + return; + if (!AddedProperties.insert(Name).second) + return; + CodeCompletionBuilder Builder(Results.getAllocator(), + Results.getCodeCompletionTUInfo()); + AddResultTypeChunk(Context, Policy, M, CCContext.getBaseType(), Builder); + Builder.AddTypedTextChunk( + Results.getAllocator().CopyString(Name->getName())); + Results.MaybeAddResult( + Result(Builder.TakeString(), M, + CCP_MemberDeclaration + CCD_MethodAsProperty), + CurContext); + }; + + if (IsClassProperty) { + for (const auto *M : Container->methods()) { + // Gather the class method that can be used as implicit property + // getters. Methods with arguments or methods that return void aren't + // added to the results as they can't be used as a getter. + if (!M->getSelector().isUnarySelector() || + M->getReturnType()->isVoidType() || M->isInstanceMethod()) + continue; + AddMethod(M); + } + } else { + for (auto *M : Container->methods()) { + if (M->getSelector().isUnarySelector()) + AddMethod(M); + } } } - // Add properties in referenced protocols. if (ObjCProtocolDecl *Protocol = dyn_cast(Container)) { for (auto *P : Protocol->protocols()) AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods, CurContext, AddedProperties, Results, - IsBaseExprStatement); + IsBaseExprStatement, IsClassProperty); } else if (ObjCInterfaceDecl *IFace = dyn_cast(Container)){ if (AllowCategories) { // Look through categories. for (auto *Cat : IFace->known_categories()) AddObjCProperties(CCContext, Cat, AllowCategories, AllowNullaryMethods, CurContext, AddedProperties, Results, - IsBaseExprStatement); + IsBaseExprStatement, IsClassProperty); } // Look through protocols. for (auto *I : IFace->all_referenced_protocols()) AddObjCProperties(CCContext, I, AllowCategories, AllowNullaryMethods, CurContext, AddedProperties, Results, - IsBaseExprStatement); + IsBaseExprStatement, IsClassProperty); // Look in the superclass. if (IFace->getSuperClass()) AddObjCProperties(CCContext, IFace->getSuperClass(), AllowCategories, AllowNullaryMethods, CurContext, AddedProperties, - Results, IsBaseExprStatement); + Results, IsBaseExprStatement, IsClassProperty); } else if (const ObjCCategoryDecl *Category = dyn_cast(Container)) { // Look through protocols. for (auto *P : Category->protocols()) AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods, CurContext, AddedProperties, Results, - IsBaseExprStatement); + IsBaseExprStatement, IsClassProperty); } } @@ -3909,6 +3932,30 @@ Results.data(),Results.size()); } +void Sema::CodeCompleteObjCClassPropertyRefExpr(Scope *S, + IdentifierInfo &ClassName, + SourceLocation ClassNameLoc, + bool IsBaseExprStatement) { + IdentifierInfo *ClassNamePtr = &ClassName; + ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(ClassNamePtr, ClassNameLoc); + if (!IFace) + return; + CodeCompletionContext CCContext( + CodeCompletionContext::CCC_ObjCPropertyAccess); + ResultBuilder Results(*this, CodeCompleter->getAllocator(), + CodeCompleter->getCodeCompletionTUInfo(), CCContext, + &ResultBuilder::IsMember); + Results.EnterNewScope(); + AddedPropertiesSet AddedProperties; + AddObjCProperties(CCContext, IFace, true, + /*AllowNullaryMethods=*/true, CurContext, AddedProperties, + Results, IsBaseExprStatement, + /*IsClassProperty=*/true); + Results.ExitScope(); + HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(), + Results.data(), Results.size()); +} + void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) { if (!CodeCompleter) return; Index: cfe/trunk/test/Index/complete-block-properties.m =================================================================== --- cfe/trunk/test/Index/complete-block-properties.m +++ cfe/trunk/test/Index/complete-block-properties.m @@ -70,3 +70,19 @@ //CHECK-CC2-NEXT: ObjCInstanceMethodDecl:{ResultType BarBlock}{TypedText blockProperty2} (35) //CHECK-CC2-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText setBlockProperty2:}{Placeholder BarBlock blockProperty2} (35) //CHECK-CC2-NEXT: ObjCInstanceMethodDecl:{ResultType void}{TypedText setBlockProperty:}{Placeholder void (^)(void)blockProperty} (35) + +@interface ClassProperties + +@property(class) void (^explicit)(); +@property(class, readonly) void (^explicitReadonly)(); + +@end + +void classBlockProperties() { + ClassProperties.explicit; +} + +// RUN: c-index-test -code-completion-at=%s:82:19 %s | FileCheck -check-prefix=CHECK-CC3 %s +//CHECK-CC3: ObjCPropertyDecl:{ResultType void}{TypedText explicit}{LeftParen (}{RightParen )} (35) +//CHECK-CC3-NEXT: ObjCPropertyDecl:{ResultType void (^)()}{TypedText explicit}{Equal = }{Placeholder ^(void)} (38) +//CHECK-CC3-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText explicitReadonly}{LeftParen (}{RightParen )} (35) Index: cfe/trunk/test/Index/complete-properties.m =================================================================== --- cfe/trunk/test/Index/complete-properties.m +++ cfe/trunk/test/Index/complete-properties.m @@ -94,3 +94,84 @@ // RUN: c-index-test -code-completion-at=%s:57:13 -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC8 %s // CHECK-CC8: ObjCPropertyDecl:{ResultType int}{TypedText Prop5} (35) + +@interface ClassProperties + +@property int instanceProperty; +@property(class) int explicit; +@property(class, readonly) int explicitReadonly; + ++ (int)implicit; ++ (int)setImplicit:(int)x; + ++ (int)implicitReadonly; + ++ (void)noProperty; + +- (int)implicitInstance; + ++ (int)shadowedImplicit; + +@end + +@interface ClassProperties (Category) + ++ (int)implicitInCategory; + +@end + +@protocol ProtocolClassProperties + +@property(class, readonly) int explicitInProtocol; + +@end + +@interface SubClassProperties: ClassProperties + +@property(class) ClassProperties *shadowedImplicit; + +@end + +@implementation SubClassProperties + +-(void) foo { + super.instanceProperty; +} + +@end + +void classProperties() { + (void)ClassProperties.implicit; + (void)SubClassProperties.explicit; +} + +// RUN: c-index-test -code-completion-at=%s:144:25 -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC9 %s +// CHECK-CC9: ObjCPropertyDecl:{ResultType int}{TypedText explicit} (35) +// CHECK-CC9-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText explicitReadonly} (35) +// CHECK-CC9-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicit} (37) +// CHECK-CC9-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicitInCategory} (37) +// CHECK-CC9-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicitReadonly} (37) +// CHECK-CC9-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText shadowedImplicit} (37) +// CHECK-CC9-NOT: implicitInstance +// CHECK-CC9-NOT: noProperty +// CHECK-CC9-NOT: instanceProperty + +// RUN: c-index-test -code-completion-at=%s:145:28 -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC10 %s +// CHECK-CC10: ObjCPropertyDecl:{ResultType int}{TypedText explicit} (35) +// CHECK-CC10-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText explicitInProtocol} (35) +// CHECK-CC10-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText explicitReadonly} (35) +// CHECK-CC10-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicit} (37) +// CHECK-CC10-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicitInCategory} (37) +// CHECK-CC10-NEXT: ObjCClassMethodDecl:{ResultType int}{TypedText implicitReadonly} (37) +// CHECK-CC10-NEXT: ObjCPropertyDecl:{ResultType ClassProperties *}{TypedText shadowedImplicit} (35) +// CHECK-CC10-NOT: implicitInstance +// CHECK-CC10-NOT: noProperty +// CHECK-CC10-NOT: instanceProperty + +// RUN: c-index-test -code-completion-at=%s:138:9 -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC11 %s +// CHECK-CC11-NOT: explicit +// CHECK-CC11-NOT: explicitReadonly +// CHECK-CC11-NOT: implicit +// CHECK-CC11-NOT: implicitReadonly +// CHECK-CC11-NOT: shadowedImplicit +// CHECK-CC11-NOT: implicitInCategory