[CodeCompletion] Add a block property setter completion result
This commit changes code completion results for Objective-C block properties:
clang now suggests an additional completion result that displays the block
property together with '=' and the block literal placeholder for the appropriate
readwrite block properties.
This commit uses a simple heuristic to determine when it's appropriate to
suggest a setter completion for block properties: the additional block setter
completion is provided iff the member access that's being completed is a
standalone statement.
rdar://28481726
Differential Revision: https://reviews.llvm.org/D25520
llvm-svn: 284472
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 6000fc6..a0e79f6 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -2212,6 +2212,7 @@
static std::string
formatBlockPlaceholder(const PrintingPolicy &Policy, const NamedDecl *BlockDecl,
FunctionTypeLoc &Block, FunctionProtoTypeLoc &BlockProto,
+ bool SuppressBlockName = false,
bool SuppressBlock = false,
Optional<ArrayRef<QualType>> ObjCSubsts = None);
@@ -2277,7 +2278,8 @@
// We have the function prototype behind the block pointer type, as it was
// written in the source.
- return formatBlockPlaceholder(Policy, Param, Block, BlockProto, SuppressBlock,
+ return formatBlockPlaceholder(Policy, Param, Block, BlockProto,
+ /*SuppressBlockName=*/false, SuppressBlock,
ObjCSubsts);
}
@@ -2293,7 +2295,7 @@
static std::string
formatBlockPlaceholder(const PrintingPolicy &Policy, const NamedDecl *BlockDecl,
FunctionTypeLoc &Block, FunctionProtoTypeLoc &BlockProto,
- bool SuppressBlock,
+ bool SuppressBlockName, bool SuppressBlock,
Optional<ArrayRef<QualType>> ObjCSubsts) {
std::string Result;
QualType ResultType = Block.getTypePtr()->getReturnType();
@@ -2329,7 +2331,7 @@
if (SuppressBlock) {
// Format as a parameter.
Result = Result + " (^";
- if (BlockDecl->getIdentifier())
+ if (!SuppressBlockName && BlockDecl->getIdentifier())
Result += BlockDecl->getIdentifier()->getName();
Result += ")";
Result += Params;
@@ -2338,7 +2340,7 @@
Result = '^' + Result;
Result += Params;
- if (BlockDecl->getIdentifier())
+ if (!SuppressBlockName && BlockDecl->getIdentifier())
Result += BlockDecl->getIdentifier()->getName();
}
@@ -3611,21 +3613,59 @@
static void AddObjCProperties(const CodeCompletionContext &CCContext,
ObjCContainerDecl *Container,
- bool AllowCategories,
- bool AllowNullaryMethods,
+ bool AllowCategories, bool AllowNullaryMethods,
DeclContext *CurContext,
AddedPropertiesSet &AddedProperties,
- ResultBuilder &Results) {
+ ResultBuilder &Results,
+ bool IsBaseExprStatement = false) {
typedef CodeCompletionResult Result;
// Retrieve the definition.
Container = getContainerDef(Container);
// Add properties in this container.
- for (const auto *P : Container->instance_properties())
- if (AddedProperties.insert(P->getIdentifier()).second)
- Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr),
- CurContext);
+ for (const auto *P : Container->instance_properties()) {
+ if (!AddedProperties.insert(P->getIdentifier()).second)
+ continue;
+
+ Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr),
+ 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);
+ }
+ }
+ }
// Add nullary methods
if (AllowNullaryMethods) {
@@ -3654,37 +3694,41 @@
if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) {
for (auto *P : Protocol->protocols())
AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods,
- CurContext, AddedProperties, Results);
+ CurContext, AddedProperties, Results,
+ IsBaseExprStatement);
} else if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container)){
if (AllowCategories) {
// Look through categories.
for (auto *Cat : IFace->known_categories())
AddObjCProperties(CCContext, Cat, AllowCategories, AllowNullaryMethods,
- CurContext, AddedProperties, Results);
+ CurContext, AddedProperties, Results,
+ IsBaseExprStatement);
}
// Look through protocols.
for (auto *I : IFace->all_referenced_protocols())
AddObjCProperties(CCContext, I, AllowCategories, AllowNullaryMethods,
- CurContext, AddedProperties, Results);
-
+ CurContext, AddedProperties, Results,
+ IsBaseExprStatement);
+
// Look in the superclass.
if (IFace->getSuperClass())
AddObjCProperties(CCContext, IFace->getSuperClass(), AllowCategories,
- AllowNullaryMethods, CurContext,
- AddedProperties, Results);
+ AllowNullaryMethods, CurContext, AddedProperties,
+ Results, IsBaseExprStatement);
} else if (const ObjCCategoryDecl *Category
= dyn_cast<ObjCCategoryDecl>(Container)) {
// Look through protocols.
for (auto *P : Category->protocols())
AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods,
- CurContext, AddedProperties, Results);
+ CurContext, AddedProperties, Results,
+ IsBaseExprStatement);
}
}
void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
- SourceLocation OpLoc,
- bool IsArrow) {
+ SourceLocation OpLoc, bool IsArrow,
+ bool IsBaseExprStatement) {
if (!Base || !CodeCompleter)
return;
@@ -3766,13 +3810,14 @@
assert(ObjCPtr && "Non-NULL pointer guaranteed above!");
AddObjCProperties(CCContext, ObjCPtr->getInterfaceDecl(), true,
/*AllowNullaryMethods=*/true, CurContext,
- AddedProperties, Results);
+ AddedProperties, Results, IsBaseExprStatement);
}
// Add properties from the protocols in a qualified interface.
for (auto *I : BaseType->getAs<ObjCObjectPointerType>()->quals())
AddObjCProperties(CCContext, I, true, /*AllowNullaryMethods=*/true,
- CurContext, AddedProperties, Results);
+ CurContext, AddedProperties, Results,
+ IsBaseExprStatement);
} else if ((IsArrow && BaseType->isObjCObjectPointerType()) ||
(!IsArrow && BaseType->isObjCObjectType())) {
// Objective-C instance variable access.