[cfe-commits] r89334 - in /cfe/trunk: include/clang/Parse/Action.h include/clang/Parse/Parser.h include/clang/Sema/CodeCompleteConsumer.h lib/Parse/ParseObjc.cpp lib/Sema/Sema.h lib/Sema/SemaCodeComplete.cpp

Douglas Gregor dgregor at apple.com
Wed Nov 18 23:41:16 PST 2009


Author: dgregor
Date: Thu Nov 19 01:41:15 2009
New Revision: 89334

URL: http://llvm.org/viewvc/llvm-project?rev=89334&view=rev
Log:
Objective-C code completion within properties after "setter = " or
"getter = ", to provide suitable method names.

Modified:
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
    cfe/trunk/lib/Parse/ParseObjc.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Thu Nov 19 01:41:15 2009
@@ -2330,7 +2330,45 @@
   ///
   /// \param S the scope in which the operator keyword occurs.  
   virtual void CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { }
-  
+
+  /// \brief Code completion for the getter of an Objective-C property 
+  /// declaration.  
+  ///
+  /// This code completion action is invoked when the code-completion
+  /// token is found after the "getter = " in a property declaration.
+  ///
+  /// \param S the scope in which the property is being declared.
+  ///
+  /// \param ClassDecl the Objective-C class or category in which the property
+  /// is being defined.
+  ///
+  /// \param Methods the set of methods declared thus far within \p ClassDecl.
+  ///
+  /// \param NumMethods the number of methods in \p Methods
+  virtual void CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl,
+                                              DeclPtrTy *Methods,
+                                              unsigned NumMethods) {
+  }
+
+  /// \brief Code completion for the setter of an Objective-C property 
+  /// declaration.  
+  ///
+  /// This code completion action is invoked when the code-completion
+  /// token is found after the "setter = " in a property declaration.
+  ///
+  /// \param S the scope in which the property is being declared.
+  ///
+  /// \param ClassDecl the Objective-C class or category in which the property
+  /// is being defined.
+  ///
+  /// \param Methods the set of methods declared thus far within \p ClassDecl.
+  ///
+  /// \param NumMethods the number of methods in \p Methods
+  virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl,
+                                              DeclPtrTy *Methods,
+                                              unsigned NumMethods) {
+  }
+
   /// \brief Code completion for an ObjC message expression that refers to
   /// a class method.
   ///

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Nov 19 01:41:15 2009
@@ -810,7 +810,8 @@
   DeclPtrTy ParseObjCMethodDecl(SourceLocation mLoc, tok::TokenKind mType,
                                 DeclPtrTy classDecl,
             tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword);
-  void ParseObjCPropertyAttribute(ObjCDeclSpec &DS);
+  void ParseObjCPropertyAttribute(ObjCDeclSpec &DS, DeclPtrTy ClassDecl,
+                                  DeclPtrTy *Methods, unsigned NumMethods);
 
   DeclPtrTy ParseObjCMethodDefinition();
 

Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Thu Nov 19 01:41:15 2009
@@ -270,7 +270,11 @@
     /// \brief Whether this declaration is the beginning of a 
     /// nested-name-specifier and, therefore, should be followed by '::'.
     bool StartsNestedNameSpecifier : 1;
-    
+
+    /// \brief Whether all parameters (of a function, Objective-C
+    /// method, etc.) should be considered "informative".
+    bool AllParametersAreInformative : 1;
+
     /// \brief If the result should have a nested-name-specifier, this is it.
     /// When \c QualifierIsInformative, the nested-name-specifier is 
     /// informative rather than required.
@@ -283,25 +287,29 @@
       : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), 
         StartParameter(0), Hidden(false), 
         QualifierIsInformative(QualifierIsInformative),
-        StartsNestedNameSpecifier(false), Qualifier(Qualifier) { }
+        StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
+        Qualifier(Qualifier) { }
     
     /// \brief Build a result that refers to a keyword or symbol.
     Result(const char *Keyword, unsigned Rank)
       : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), StartParameter(0),
         Hidden(false), QualifierIsInformative(0), 
-        StartsNestedNameSpecifier(false), Qualifier(0) { }
+        StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
+        Qualifier(0) { }
     
     /// \brief Build a result that refers to a macro.
     Result(IdentifierInfo *Macro, unsigned Rank)
      : Kind(RK_Macro), Macro(Macro), Rank(Rank), StartParameter(0), 
        Hidden(false), QualifierIsInformative(0), 
-       StartsNestedNameSpecifier(false), Qualifier(0) { }
+       StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
+       Qualifier(0) { }
 
     /// \brief Build a result that refers to a pattern.
     Result(CodeCompletionString *Pattern, unsigned Rank)
       : Kind(RK_Pattern), Pattern(Pattern), Rank(Rank), StartParameter(0), 
         Hidden(false), QualifierIsInformative(0), 
-        StartsNestedNameSpecifier(false), Qualifier(0) { }
+        StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
+        Qualifier(0) { }
     
     /// \brief Retrieve the declaration stored in this result.
     NamedDecl *getDeclaration() const {

Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
+++ cfe/trunk/lib/Parse/ParseObjc.cpp Thu Nov 19 01:41:15 2009
@@ -326,7 +326,8 @@
       ObjCDeclSpec OCDS;
       // Parse property attribute list, if any.
       if (Tok.is(tok::l_paren))
-        ParseObjCPropertyAttribute(OCDS);
+        ParseObjCPropertyAttribute(OCDS, interfaceDecl,
+                                   allMethods.data(), allMethods.size());
 
       struct ObjCPropertyCallback : FieldCallback {
         Parser &P;
@@ -425,7 +426,9 @@
 ///     copy
 ///     nonatomic
 ///
-void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
+void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, DeclPtrTy ClassDecl,
+                                        DeclPtrTy *Methods, 
+                                        unsigned NumMethods) {
   assert(Tok.getKind() == tok::l_paren);
   SourceLocation LHSLoc = ConsumeParen(); // consume '('
 
@@ -462,6 +465,16 @@
                            tok::r_paren))
         return;
 
+      if (Tok.is(tok::code_completion)) {
+        if (II->getNameStart()[0] == 's')
+          Actions.CodeCompleteObjCPropertySetter(CurScope, ClassDecl,
+                                                 Methods, NumMethods);
+        else
+          Actions.CodeCompleteObjCPropertyGetter(CurScope, ClassDecl,
+                                                 Methods, NumMethods);
+        ConsumeToken();
+      }
+
       if (Tok.isNot(tok::identifier)) {
         Diag(Tok, diag::err_expected_ident);
         SkipUntil(tok::r_paren);

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Thu Nov 19 01:41:15 2009
@@ -3647,6 +3647,13 @@
   virtual void CodeCompleteOperatorName(Scope *S);
   
   virtual void CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS);
+  virtual void CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl,
+                                              DeclPtrTy *Methods,
+                                              unsigned NumMethods);
+  virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl,
+                                              DeclPtrTy *Methods,
+                                              unsigned NumMethods);
+
   virtual void CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName,
                                             SourceLocation FNameLoc,
                                             IdentifierInfo **SelIdents, 

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=89334&r1=89333&r2=89334&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Thu Nov 19 01:41:15 2009
@@ -967,7 +967,7 @@
         if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(Idx))
           Keyword += II->getName().str();
         Keyword += ":";
-        if (Idx < StartParameter) {
+        if (Idx < StartParameter || AllParametersAreInformative) {
           Result->AddInformativeChunk(Keyword);
         } else if (Idx == StartParameter)
           Result->AddTypedTextChunk(Keyword);
@@ -984,7 +984,10 @@
       Arg = "(" + Arg + ")";
       if (IdentifierInfo *II = (*P)->getIdentifier())
         Arg += II->getName().str();
-      Result->AddPlaceholderChunk(Arg);
+      if (AllParametersAreInformative)
+        Result->AddInformativeChunk(Arg);
+      else
+        Result->AddPlaceholderChunk(Arg);
     }
 
     return Result;
@@ -1721,6 +1724,35 @@
   HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
 }
 
+/// \brief Descripts the kind of Objective-C method that we want to find
+/// via code completion.
+enum ObjCMethodKind {
+  MK_Any, //< Any kind of method, provided it means other specified criteria.
+  MK_ZeroArgSelector, //< Zero-argument (unary) selector.
+  MK_OneArgSelector //< One-argument selector.
+};
+
+static bool isAcceptableObjCMethod(ObjCMethodDecl *Method,
+                                   ObjCMethodKind WantKind,
+                                   IdentifierInfo **SelIdents,
+                                   unsigned NumSelIdents) {
+  Selector Sel = Method->getSelector();
+  if (NumSelIdents > Sel.getNumArgs())
+    return false;
+      
+  switch (WantKind) {
+  case MK_Any:             break;
+  case MK_ZeroArgSelector: return Sel.isUnarySelector();
+  case MK_OneArgSelector:  return Sel.getNumArgs() == 1;
+  }
+
+  for (unsigned I = 0; I != NumSelIdents; ++I)
+    if (SelIdents[I] != Sel.getIdentifierInfoForSlot(I))
+      return false;
+
+  return true;
+}
+                                   
 /// \brief Add all of the Objective-C methods in the given Objective-C 
 /// container to the set of results.
 ///
@@ -1740,6 +1772,7 @@
 /// \param Results the structure into which we'll add results.
 static void AddObjCMethods(ObjCContainerDecl *Container, 
                            bool WantInstanceMethods,
+                           ObjCMethodKind WantKind,
                            IdentifierInfo **SelIdents,
                            unsigned NumSelIdents,
                            DeclContext *CurContext,
@@ -1751,20 +1784,12 @@
     if ((*M)->isInstanceMethod() == WantInstanceMethods) {
       // Check whether the selector identifiers we've been given are a 
       // subset of the identifiers for this particular method.
-      Selector Sel = (*M)->getSelector();
-      if (NumSelIdents > Sel.getNumArgs())
+      if (!isAcceptableObjCMethod(*M, WantKind, SelIdents, NumSelIdents))
         continue;
-      
-      bool Failed = false;
-      for (unsigned I = 0; I != NumSelIdents && !Failed; ++I)
-        if (SelIdents[I] != Sel.getIdentifierInfoForSlot(I))
-          Failed = true;
-      
-      if (Failed)
-        continue;
-      
+
       Result R = Result(*M, 0);
       R.StartParameter = NumSelIdents;
+      R.AllParametersAreInformative = (WantKind != MK_Any);
       Results.MaybeAddResult(R, CurContext);
     }
   }
@@ -1778,14 +1803,14 @@
   for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
                                             E = Protocols.end(); 
        I != E; ++I)
-    AddObjCMethods(*I, WantInstanceMethods, SelIdents, NumSelIdents, 
+    AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, NumSelIdents, 
                    CurContext, Results);
   
   // Add methods in categories.
   for (ObjCCategoryDecl *CatDecl = IFace->getCategoryList(); CatDecl;
        CatDecl = CatDecl->getNextClassCategory()) {
-    AddObjCMethods(CatDecl, WantInstanceMethods, SelIdents, NumSelIdents,
-                   CurContext, Results);
+    AddObjCMethods(CatDecl, WantInstanceMethods, WantKind, SelIdents, 
+                   NumSelIdents, CurContext, Results);
     
     // Add a categories protocol methods.
     const ObjCList<ObjCProtocolDecl> &Protocols 
@@ -1793,24 +1818,104 @@
     for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
                                               E = Protocols.end();
          I != E; ++I)
-      AddObjCMethods(*I, WantInstanceMethods, SelIdents, NumSelIdents,
-                     CurContext, Results);
+      AddObjCMethods(*I, WantInstanceMethods, WantKind, SelIdents, 
+                     NumSelIdents, CurContext, Results);
     
     // Add methods in category implementations.
     if (ObjCCategoryImplDecl *Impl = CatDecl->getImplementation())
-      AddObjCMethods(Impl, WantInstanceMethods, SelIdents, NumSelIdents,
-                     CurContext, Results);
+      AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents, 
+                     NumSelIdents, CurContext, Results);
   }
   
   // Add methods in superclass.
   if (IFace->getSuperClass())
-    AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, SelIdents, 
-                   NumSelIdents, CurContext,Results);
+    AddObjCMethods(IFace->getSuperClass(), WantInstanceMethods, WantKind, 
+                   SelIdents, NumSelIdents, CurContext, Results);
 
   // Add methods in our implementation, if any.
   if (ObjCImplementationDecl *Impl = IFace->getImplementation())
-    AddObjCMethods(Impl, WantInstanceMethods, SelIdents, NumSelIdents, 
-                   CurContext, Results);
+    AddObjCMethods(Impl, WantInstanceMethods, WantKind, SelIdents,
+                   NumSelIdents, CurContext, Results);
+}
+
+
+void Sema::CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl,
+                                          DeclPtrTy *Methods,
+                                          unsigned NumMethods) {
+  typedef CodeCompleteConsumer::Result Result;
+
+  // Try to find the interface where getters might live.
+  ObjCInterfaceDecl *Class
+    = dyn_cast_or_null<ObjCInterfaceDecl>(ClassDecl.getAs<Decl>());
+  if (!Class) {
+    if (ObjCCategoryDecl *Category
+          = dyn_cast_or_null<ObjCCategoryDecl>(ClassDecl.getAs<Decl>()))
+      Class = Category->getClassInterface();
+
+    if (!Class)
+      return;
+  }
+
+  // Find all of the potential getters.
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+
+  // FIXME: We need to do this because Objective-C methods don't get
+  // pushed into DeclContexts early enough. Argh!
+  for (unsigned I = 0; I != NumMethods; ++I) { 
+    if (ObjCMethodDecl *Method
+            = dyn_cast_or_null<ObjCMethodDecl>(Methods[I].getAs<Decl>()))
+      if (Method->isInstanceMethod() &&
+          isAcceptableObjCMethod(Method, MK_ZeroArgSelector, 0, 0)) {
+        Result R = Result(Method, 0);
+        R.AllParametersAreInformative = true;
+        Results.MaybeAddResult(R, CurContext);
+      }
+  }
+
+  AddObjCMethods(Class, true, MK_ZeroArgSelector, 0, 0, CurContext, Results);
+  Results.ExitScope();
+  HandleCodeCompleteResults(this, CodeCompleter,Results.data(),Results.size());
+}
+
+void Sema::CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ObjCImplDecl,
+                                          DeclPtrTy *Methods,
+                                          unsigned NumMethods) {
+  typedef CodeCompleteConsumer::Result Result;
+
+  // Try to find the interface where setters might live.
+  ObjCInterfaceDecl *Class
+    = dyn_cast_or_null<ObjCInterfaceDecl>(ObjCImplDecl.getAs<Decl>());
+  if (!Class) {
+    if (ObjCCategoryDecl *Category
+          = dyn_cast_or_null<ObjCCategoryDecl>(ObjCImplDecl.getAs<Decl>()))
+      Class = Category->getClassInterface();
+
+    if (!Class)
+      return;
+  }
+
+  // Find all of the potential getters.
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+
+  // FIXME: We need to do this because Objective-C methods don't get
+  // pushed into DeclContexts early enough. Argh!
+  for (unsigned I = 0; I != NumMethods; ++I) { 
+    if (ObjCMethodDecl *Method
+            = dyn_cast_or_null<ObjCMethodDecl>(Methods[I].getAs<Decl>()))
+      if (Method->isInstanceMethod() &&
+          isAcceptableObjCMethod(Method, MK_OneArgSelector, 0, 0)) {
+        Result R = Result(Method, 0);
+        R.AllParametersAreInformative = true;
+        Results.MaybeAddResult(R, CurContext);
+      }
+  }
+
+  AddObjCMethods(Class, true, MK_OneArgSelector, 0, 0, CurContext, Results);
+
+  Results.ExitScope();
+  HandleCodeCompleteResults(this, CodeCompleter,Results.data(),Results.size());
 }
 
 void Sema::CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName,
@@ -1873,7 +1978,8 @@
   // superclasses, categories, implementation, etc.
   ResultBuilder Results(*this);
   Results.EnterNewScope();
-  AddObjCMethods(CDecl, false, SelIdents, NumSelIdents, CurContext, Results);  
+  AddObjCMethods(CDecl, false, MK_Any, SelIdents, NumSelIdents, CurContext, 
+                 Results);  
   Results.ExitScope();
   
   // This also suppresses remaining diagnostics.
@@ -1910,8 +2016,8 @@
       ReceiverType->isObjCQualifiedClassType()) {
     if (ObjCMethodDecl *CurMethod = getCurMethodDecl()) {
       if (ObjCInterfaceDecl *ClassDecl = CurMethod->getClassInterface())
-        AddObjCMethods(ClassDecl, false, SelIdents, NumSelIdents, CurContext, 
-                       Results);
+        AddObjCMethods(ClassDecl, false, MK_Any, SelIdents, NumSelIdents, 
+                       CurContext, Results);
     }
   } 
   // Handle messages to a qualified ID ("id<foo>").
@@ -1921,20 +2027,22 @@
     for (ObjCObjectPointerType::qual_iterator I = QualID->qual_begin(),
                                               E = QualID->qual_end(); 
          I != E; ++I)
-      AddObjCMethods(*I, true, SelIdents, NumSelIdents, CurContext, Results);
+      AddObjCMethods(*I, true, MK_Any, SelIdents, NumSelIdents, CurContext, 
+                     Results);
   }
   // Handle messages to a pointer to interface type.
   else if (const ObjCObjectPointerType *IFacePtr
                               = ReceiverType->getAsObjCInterfacePointerType()) {
     // Search the class, its superclasses, etc., for instance methods.
-    AddObjCMethods(IFacePtr->getInterfaceDecl(), true, SelIdents, NumSelIdents,
-                   CurContext, Results);
+    AddObjCMethods(IFacePtr->getInterfaceDecl(), true, MK_Any, SelIdents,
+                   NumSelIdents, CurContext, Results);
     
     // Search protocols for instance methods.
     for (ObjCObjectPointerType::qual_iterator I = IFacePtr->qual_begin(),
          E = IFacePtr->qual_end(); 
          I != E; ++I)
-      AddObjCMethods(*I, true, SelIdents, NumSelIdents, CurContext, Results);
+      AddObjCMethods(*I, true, MK_Any, SelIdents, NumSelIdents, CurContext, 
+                     Results);
   }
   
   Results.ExitScope();





More information about the cfe-commits mailing list