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,
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);
-
- 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);
- }
+ // 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);
}
}
--- /dev/null
+// 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