]> granicus.if.org Git - clang/commitdiff
Teach code completion to provide property results when the property
authorDouglas Gregor <dgregor@apple.com>
Tue, 2 Nov 2010 20:36:02 +0000 (20:36 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 2 Nov 2010 20:36:02 +0000 (20:36 +0000)
can be used to automatically synthesize an ivar.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118052 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaLookup.cpp
test/Index/complete-synthesized.m [new file with mode: 0644]

index 6f52bbbb7603ea7ce01dc89a485f785d485e2578..54357eb1da1e300ee4d802e493daa4351e686614 100644 (file)
@@ -1377,6 +1377,14 @@ public:
                                 ObjCIvarDecl **Fields, unsigned nIvars,
                                 SourceLocation Loc);
 
+  /// \brief Determine whether we can synthesize a provisional ivar for the
+  /// given name.
+  ObjCPropertyDecl *canSynthesizeProvisionalIvar(IdentifierInfo *II);
+
+  /// \brief Determine whether we can synthesize a provisional ivar for the
+  /// given property.
+  bool canSynthesizeProvisionalIvar(ObjCPropertyDecl *Property);
+
   /// ImplMethodsVsClassMethods - This is main routine to warn if any method
   /// remains unimplemented in the class or category @implementation.
   void ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
index b07ae391f960cd339fb5ff7796d2732364546600..c245e38bb408bd7b5880f66b3a6f79652369499c 100644 (file)
@@ -147,9 +147,13 @@ namespace {
     /// \brief The selector that we prefer.
     Selector PreferredSelector;
     
-    /// \brief The completion context in which 
+    /// \brief The completion context in which we are gathering results.
     CodeCompletionContext CompletionContext;
     
+    /// \brief If we are in an instance method definition, the @implementation
+    /// object.
+    ObjCImplementationDecl *ObjCImplementation;
+    
     void AdjustResultPriorityForDecl(Result &R);
 
     void MaybeAddConstructorResults(Result R);
@@ -160,7 +164,27 @@ namespace {
                            LookupFilter Filter = 0)
       : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false),
         HasObjectTypeQualifiers(false), 
-        CompletionContext(CompletionContext) { }
+        CompletionContext(CompletionContext),
+        ObjCImplementation(0) 
+    { 
+      // If this is an Objective-C instance method definition, dig out the 
+      // corresponding implementation.
+      switch (CompletionContext.getKind()) {
+      case CodeCompletionContext::CCC_Expression:
+      case CodeCompletionContext::CCC_ObjCMessageReceiver:
+      case CodeCompletionContext::CCC_ParenthesizedExpression:
+      case CodeCompletionContext::CCC_Statement:
+      case CodeCompletionContext::CCC_Recovery:
+        if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl())
+          if (Method->isInstanceMethod())
+            if (ObjCInterfaceDecl *Interface = Method->getClassInterface())
+              ObjCImplementation = Interface->getImplementation();
+        break;
+          
+      default:
+        break;
+      }
+    }
     
     /// \brief Whether we should include code patterns in the completion
     /// results.
@@ -203,7 +227,7 @@ namespace {
     void setPreferredSelector(Selector Sel) {
       PreferredSelector = Sel;
     }
-    
+        
     /// \brief Retrieve the code-completion context for which results are
     /// being collected.
     const CodeCompletionContext &getCompletionContext() const { 
@@ -919,9 +943,14 @@ bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const {
   unsigned IDNS = Decl::IDNS_Ordinary;
   if (SemaRef.getLangOptions().CPlusPlus)
     IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member;
-  else if (SemaRef.getLangOptions().ObjC1 && isa<ObjCIvarDecl>(ND))
-    return true;
-
+  else if (SemaRef.getLangOptions().ObjC1) {
+    if (isa<ObjCIvarDecl>(ND))
+      return true;
+    if (isa<ObjCPropertyDecl>(ND) &&
+        SemaRef.canSynthesizeProvisionalIvar(cast<ObjCPropertyDecl>(ND)))
+      return true;
+  }
+  
   return ND->getIdentifierNamespace() & IDNS;
 }
 
@@ -935,9 +964,14 @@ bool ResultBuilder::IsOrdinaryNonTypeName(NamedDecl *ND) const {
   unsigned IDNS = Decl::IDNS_Ordinary;
   if (SemaRef.getLangOptions().CPlusPlus)
     IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member;
-  else if (SemaRef.getLangOptions().ObjC1 && isa<ObjCIvarDecl>(ND))
-    return true;
-  
+  else if (SemaRef.getLangOptions().ObjC1) {
+    if (isa<ObjCIvarDecl>(ND))
+      return true;
+    if (isa<ObjCPropertyDecl>(ND) &&
+        SemaRef.canSynthesizeProvisionalIvar(cast<ObjCPropertyDecl>(ND)))
+      return true;
+  }
   return ND->getIdentifierNamespace() & IDNS;
 }
 
index 9e28172c97e4a3e6919237814636638bb1159932..fdc4828cf11a0cbedb033c997d84f5b36d1f59c9 100644 (file)
@@ -1027,25 +1027,41 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
   return true;
 }
 
-static ObjCPropertyDecl *OkToSynthesizeProvisionalIvar(Sema &SemaRef,
-                                                       IdentifierInfo *II,
-                                                       SourceLocation NameLoc) {
-  ObjCMethodDecl *CurMeth = SemaRef.getCurMethodDecl();
+ObjCPropertyDecl *Sema::canSynthesizeProvisionalIvar(IdentifierInfo *II) {
+  ObjCMethodDecl *CurMeth = getCurMethodDecl();
   ObjCInterfaceDecl *IDecl = CurMeth->getClassInterface();
   if (!IDecl)
     return 0;
   ObjCImplementationDecl *ClassImpDecl = IDecl->getImplementation();
   if (!ClassImpDecl)
     return 0;
-  ObjCPropertyDecl *property = SemaRef.LookupPropertyDecl(IDecl, II);
+  ObjCPropertyDecl *property = LookupPropertyDecl(IDecl, II);
   if (!property)
     return 0;
   if (ObjCPropertyImplDecl *PIDecl = ClassImpDecl->FindPropertyImplDecl(II))
-    if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic)
+    if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic ||
+        PIDecl->getPropertyIvarDecl())
       return 0;
   return property;
 }
 
+bool Sema::canSynthesizeProvisionalIvar(ObjCPropertyDecl *Property) {
+  ObjCMethodDecl *CurMeth = getCurMethodDecl();
+  ObjCInterfaceDecl *IDecl = CurMeth->getClassInterface();
+  if (!IDecl)
+    return false;
+  ObjCImplementationDecl *ClassImpDecl = IDecl->getImplementation();
+  if (!ClassImpDecl)
+    return false;
+  if (ObjCPropertyImplDecl *PIDecl
+                = ClassImpDecl->FindPropertyImplDecl(Property->getIdentifier()))
+    if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic ||
+        PIDecl->getPropertyIvarDecl())
+      return false;
+  
+  return true;
+}
+
 static ObjCIvarDecl *SynthesizeProvisionalIvar(Sema &SemaRef,
                                                LookupResult &Lookup,
                                                IdentifierInfo *II,
@@ -1228,8 +1244,7 @@ ExprResult Sema::ActOnIdExpression(Scope *S,
     if (getLangOptions().ObjCNonFragileABI && IvarLookupFollowUp &&
         !getLangOptions().ObjCNonFragileABI2 &&
         Var->isFileVarDecl()) {
-      ObjCPropertyDecl *Property = 
-        OkToSynthesizeProvisionalIvar(*this, II, NameLoc);
+      ObjCPropertyDecl *Property = canSynthesizeProvisionalIvar(II);
       if (Property) {
         Diag(NameLoc, diag::warn_ivar_variable_conflict) << Var->getDeclName();
         Diag(Property->getLocation(), diag::note_property_declare);
index acc5b354f8abbfcf33294eb88fcbe86e35ed1c89..f0c21f4396263560ac44f8c35d724e3371cf9408 100644 (file)
@@ -2630,9 +2630,27 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
           // For instance methods, look for ivars in the method's interface.
           LookupResult IvarResult(Result.getSema(), Result.getLookupName(),
                                   Result.getNameLoc(), Sema::LookupMemberName);
-          if (ObjCInterfaceDecl *IFace = Method->getClassInterface())
+          if (ObjCInterfaceDecl *IFace = Method->getClassInterface()) {
             LookupVisibleDecls(IFace, IvarResult, /*QualifiedNameLookup=*/false, 
                                /*InBaseClass=*/false, Consumer, Visited);
+            
+            // Look for properties from which we can synthesize ivars, if
+            // permitted.
+            if (Result.getSema().getLangOptions().ObjCNonFragileABI2 &&
+                IFace->getImplementation() &&
+                Result.getLookupKind() == Sema::LookupOrdinaryName) {
+              for (ObjCInterfaceDecl::prop_iterator 
+                        P = IFace->prop_begin(),
+                     PEnd = IFace->prop_end();
+                   P != PEnd; ++P) {
+                if (Result.getSema().canSynthesizeProvisionalIvar(*P) &&
+                    !IFace->lookupInstanceVariable((*P)->getIdentifier())) {
+                  Consumer.FoundDecl(*P, Visited.checkHidden(*P), false);
+                  Visited.add(*P);
+                }
+              }                  
+            }                
+          }
         }
 
         // We've already performed all of the name lookup that we need
diff --git a/test/Index/complete-synthesized.m b/test/Index/complete-synthesized.m
new file mode 100644 (file)
index 0000000..a97eeab
--- /dev/null
@@ -0,0 +1,56 @@
+// Note: this test is line- and column-sensitive. Test commands are at
+// the end.
+
+
+@interface A
+@property int prop1;
+@end
+
+@interface B : A {
+  float _prop2;
+}
+@property float prop2;
+@property short prop3;
+@end
+
+@interface B ()
+@property double prop4;
+@end
+
+@implementation B
+@synthesize prop2 = _prop2;
+
+- (int)method {
+  return _prop2;
+}
+
+@dynamic prop3;
+
+- (short)method2 {
+  return prop4;
+}
+
+- (short)method3 {
+  return prop3;
+}
+@end
+
+// RUN: c-index-test -code-completion-at=%s:24:1 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: NotImplemented:{TypedText _Bool} (50)
+// CHECK-CC1: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35)
+// CHECK-CC1-NOT: prop2
+// CHECK-CC1: ObjCPropertyDecl:{ResultType short}{TypedText prop3} (35)
+// CHECK-CC1: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35)
+
+// RUN: c-index-test -code-completion-at=%s:30:2 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: NotImplemented:{TypedText _Bool} (50)
+// CHECK-CC2: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35)
+// CHECK-CC2-NOT: prop3
+// CHECK-CC2: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35)
+
+// RUN: c-index-test -code-completion-at=%s:34:2 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: NotImplemented:{TypedText _Bool} (50)
+// CHECK-CC3: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35)
+// CHECK-CC3: ObjCIvarDecl:{ResultType double}{TypedText prop4}
+// CHECK-CC3-NOT: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35)
+// CHECK-CC1: restrict