[CodeCompletion] Provide Objective-C class property completion results
authorAlex Lorenz <arphaman@gmail.com>
Thu, 8 Dec 2016 15:09:40 +0000 (15:09 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Thu, 8 Dec 2016 15:09:40 +0000 (15:09 +0000)
This commit provides class property code completion results. It supports
explicit and implicit class properties, but the special block completion is done
only for explicit properties right now.

rdar://25636195

Differential Revision: https://reviews.llvm.org/D27053

llvm-svn: 289058

clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaCodeComplete.cpp
clang/test/Index/complete-block-properties.m
clang/test/Index/complete-properties.m

index d5a6912..fcc0d53 100644 (file)
@@ -9662,6 +9662,9 @@ public:
                                           bool AtParameterName,
                                           ParsedType ReturnType,
                                           ArrayRef<IdentifierInfo *> SelIdents);
+  void CodeCompleteObjCClassPropertyRefExpr(Scope *S, IdentifierInfo &ClassName,
+                                            SourceLocation ClassNameLoc,
+                                            bool IsBaseExprStatement);
   void CodeCompletePreprocessorDirective(bool InConditional);
   void CodeCompleteInPreprocessorConditionalExclusion(Scope *S);
   void CodeCompletePreprocessorMacroName(bool IsDefinition);
index b9988bb..4b0a834 100644 (file)
@@ -885,7 +885,13 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
          // 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 2a78c98..4bcf405 100644 (file)
@@ -3654,22 +3654,20 @@ static void AddObjCBlockCall(ASTContext &Context, const PrintingPolicy &Policy,
   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 @@ static void AddObjCProperties(const CodeCompletionContext &CCContext,
         !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 @@ static void AddObjCProperties(const CodeCompletionContext &CCContext,
     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 @@ static void AddObjCProperties(const CodeCompletionContext &CCContext,
                  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<ObjCProtocolDecl>(Container)) {
     for (auto *P : Protocol->protocols())
       AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods,
                         CurContext, AddedProperties, Results,
-                        IsBaseExprStatement);
+                        IsBaseExprStatement, IsClassProperty);
   } 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,
-                          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<ObjCCategoryDecl>(Container)) {
     // Look through protocols.
     for (auto *P : Category->protocols())
       AddObjCProperties(CCContext, P, AllowCategories, AllowNullaryMethods,
                         CurContext, AddedProperties, Results,
-                        IsBaseExprStatement);
+                        IsBaseExprStatement, IsClassProperty);
   }
 }
 
@@ -3909,6 +3932,30 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
                             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 47eee02..d166147 100644 (file)
@@ -70,3 +70,19 @@ void noQualifierParens(NoQualifierParens *f) {
 //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 a445056..0c49819 100644 (file)
@@ -94,3 +94,84 @@ id test(I3 *i3) {
 
 // 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 <ProtocolClassProperties>
+
+@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