Index: cfe/trunk/lib/Sema/SemaCodeComplete.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaCodeComplete.cpp +++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp @@ -3612,6 +3612,44 @@ return Container; } +/// \brief Adds a block invocation code completion result for the given block +/// declaration \p BD. +static void AddObjCBlockCall(ASTContext &Context, const PrintingPolicy &Policy, + CodeCompletionBuilder &Builder, + const NamedDecl *BD, + const FunctionTypeLoc &BlockLoc, + const FunctionProtoTypeLoc &BlockProtoLoc) { + Builder.AddResultTypeChunk( + GetCompletionTypeString(BlockLoc.getReturnLoc().getType(), Context, + Policy, Builder.getAllocator())); + + AddTypedNameChunk(Context, Policy, BD, Builder); + Builder.AddChunk(CodeCompletionString::CK_LeftParen); + + if (BlockProtoLoc && BlockProtoLoc.getTypePtr()->isVariadic()) { + Builder.AddPlaceholderChunk("..."); + } else { + for (unsigned I = 0, N = BlockLoc.getNumParams(); I != N; ++I) { + if (I) + Builder.AddChunk(CodeCompletionString::CK_Comma); + + // Format the placeholder string. + std::string PlaceholderStr = + FormatFunctionParameter(Policy, BlockLoc.getParam(I)); + + if (I == N - 1 && BlockProtoLoc && + BlockProtoLoc.getTypePtr()->isVariadic()) + PlaceholderStr += ", ..."; + + // Add the placeholder string. + Builder.AddPlaceholderChunk( + Builder.getAllocator().CopyString(PlaceholderStr)); + } + } + + Builder.AddChunk(CodeCompletionString::CK_RightParen); +} + static void AddObjCProperties(const CodeCompletionContext &CCContext, ObjCContainerDecl *Container, bool AllowCategories, bool AllowNullaryMethods, @@ -3629,42 +3667,61 @@ if (!AddedProperties.insert(P->getIdentifier()).second) continue; - Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), - CurContext); + // FIXME: Provide block invocation completion for non-statement + // expressions. + if (!P->getType().getTypePtr()->isBlockPointerType() || + !IsBaseExprStatement) { + Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), + CurContext); + continue; + } + + // Block setter and invocation completion is provided only when we are able + // to find the FunctionProtoTypeLoc with parameter names for the block. + FunctionTypeLoc BlockLoc; + FunctionProtoTypeLoc BlockProtoLoc; + findTypeLocationForBlockDecl(P->getTypeSourceInfo(), BlockLoc, + BlockProtoLoc); + if (!BlockLoc) { + Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), + CurContext); + continue; + } + + // The default completion result for block properties should be the block + // invocation completion when the base expression is a statement. + CodeCompletionBuilder Builder(Results.getAllocator(), + Results.getCodeCompletionTUInfo()); + AddObjCBlockCall(Container->getASTContext(), + getCompletionPrintingPolicy(Results.getSema()), Builder, P, + BlockLoc, BlockProtoLoc); + Results.MaybeAddResult( + Result(Builder.TakeString(), P, Results.getBasePriority(P)), + CurContext); // Provide additional block setter completion iff the base expression is a - // statement. - if (!P->isReadOnly() && IsBaseExprStatement && - P->getType().getTypePtr()->isBlockPointerType()) { - FunctionTypeLoc BlockLoc; - FunctionProtoTypeLoc BlockProtoLoc; - findTypeLocationForBlockDecl(P->getTypeSourceInfo(), BlockLoc, - BlockProtoLoc); - - // Provide block setter completion only when we are able to find - // the FunctionProtoTypeLoc with parameter names for the block. - if (BlockLoc) { - CodeCompletionBuilder Builder(Results.getAllocator(), - Results.getCodeCompletionTUInfo()); - AddResultTypeChunk(Container->getASTContext(), - getCompletionPrintingPolicy(Results.getSema()), P, - CCContext.getBaseType(), Builder); - Builder.AddTypedTextChunk( - Results.getAllocator().CopyString(P->getName())); - Builder.AddChunk(CodeCompletionString::CK_Equal); + // statement and the block property is mutable. + if (!P->isReadOnly()) { + CodeCompletionBuilder Builder(Results.getAllocator(), + Results.getCodeCompletionTUInfo()); + AddResultTypeChunk(Container->getASTContext(), + getCompletionPrintingPolicy(Results.getSema()), P, + CCContext.getBaseType(), Builder); + Builder.AddTypedTextChunk( + Results.getAllocator().CopyString(P->getName())); + Builder.AddChunk(CodeCompletionString::CK_Equal); - std::string PlaceholderStr = formatBlockPlaceholder( - getCompletionPrintingPolicy(Results.getSema()), P, BlockLoc, - BlockProtoLoc, /*SuppressBlockName=*/true); - // Add the placeholder string. - Builder.AddPlaceholderChunk( - Builder.getAllocator().CopyString(PlaceholderStr)); - - Results.MaybeAddResult( - Result(Builder.TakeString(), P, - Results.getBasePriority(P) + CCD_BlockPropertySetter), - CurContext); - } + std::string PlaceholderStr = formatBlockPlaceholder( + getCompletionPrintingPolicy(Results.getSema()), P, BlockLoc, + BlockProtoLoc, /*SuppressBlockName=*/true); + // Add the placeholder string. + Builder.AddPlaceholderChunk( + Builder.getAllocator().CopyString(PlaceholderStr)); + + Results.MaybeAddResult( + Result(Builder.TakeString(), P, + Results.getBasePriority(P) + CCD_BlockPropertySetter), + CurContext); } } 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 @@ -0,0 +1,53 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test. + +// Block invocations should be presented when completing properties in +// standalone statements. +// rdar://28846196 + +typedef int Foo; +typedef void (^FooBlock)(Foo *someParameter); +typedef int (^BarBlock)(int *); + +@interface Obj + +@property (readwrite, nonatomic, copy) void (^block)(); +@property (readonly, nonatomic, copy) int (^performA)(); +@property (readonly, nonatomic, copy) int (^performB)(int x, int y); +@property (readwrite, nonatomic, copy) Foo (^blocker)(int x, Foo y, FooBlock foo); + +@end + + +@interface Test : Obj + +@property (readonly, nonatomic, copy) FooBlock fooBlock; +@property (readonly, nonatomic, copy) BarBlock barBlock; +@property (readonly, nonatomic, copy) Test * (^getObject)(int index); +@property (readwrite, nonatomic) int foo; + +@end + +@implementation Test + +- (void)test { + self.foo = 2; + int x = self.performA(); self.foo = 2; + self.getObject(0).foo = 2; +} + +// RUN: c-index-test -code-completion-at=%s:34:8 %s | FileCheck -check-prefix=CHECK-CC1 %s +// RUN: c-index-test -code-completion-at=%s:35:33 %s | FileCheck -check-prefix=CHECK-CC1 %s +// RUN: c-index-test -code-completion-at=%s:36:21 %s | FileCheck -check-prefix=CHECK-CC1 %s +//CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText barBlock}{LeftParen (}{Placeholder int *}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText block}{LeftParen (}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void (^)()}{TypedText block}{Equal = }{Placeholder ^(void)} (38) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType Foo}{TypedText blocker}{LeftParen (}{Placeholder int x}{Comma , }{Placeholder Foo y}{Comma , }{Placeholder ^(Foo *someParameter)foo}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType Foo (^)(int, Foo, FooBlock)}{TypedText blocker}{Equal = }{Placeholder ^Foo(int x, Foo y, FooBlock foo)} (38) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText foo} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText fooBlock}{LeftParen (}{Placeholder Foo *someParameter}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType Test *}{TypedText getObject}{LeftParen (}{Placeholder int index}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText performA}{LeftParen (}{RightParen )} (35) +//CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType int}{TypedText performB}{LeftParen (}{Placeholder int x}{Comma , }{Placeholder int y}{RightParen )} (35) + +@end Index: cfe/trunk/test/Index/complete-block-property-assignment.m =================================================================== --- cfe/trunk/test/Index/complete-block-property-assignment.m +++ cfe/trunk/test/Index/complete-block-property-assignment.m @@ -35,11 +35,11 @@ // RUN: c-index-test -code-completion-at=%s:29:9 %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: ObjCPropertyDecl:{ResultType int}{TypedText foo} (35) // CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType Obj *}{TypedText obj} (35) -// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void (^)(Obj *)}{TypedText onAction} (35) +// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText onAction}{LeftParen (}{Placeholder Obj *object}{RightParen )} (35) // CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void (^)(Obj *)}{TypedText onAction}{Equal = }{Placeholder ^(Obj *object)} (38) -// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType FooBlock}{TypedText onEventHandler} (35) +// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText onEventHandler}{LeftParen (}{Placeholder Foo *someParameter}{RightParen )} (35) // CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType FooBlock}{TypedText onEventHandler}{Equal = }{Placeholder ^(Foo *someParameter)} (38) -// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void (^)(int *)}{TypedText onReadonly} (35) +// CHECK-CC1-NEXT: ObjCPropertyDecl:{ResultType void}{TypedText onReadonly}{LeftParen (}{Placeholder int *someParameter}{RightParen )} (35) - (void) takeInt:(int)x { }