[cfe-commits] r100587 - in /cfe/trunk: include/clang/Parse/Action.h lib/Parse/ParseObjc.cpp lib/Sema/CodeCompleteConsumer.cpp lib/Sema/Sema.h lib/Sema/SemaCodeComplete.cpp test/Index/complete-method-decls.m

Douglas Gregor dgregor at apple.com
Tue Apr 6 17:21:17 PDT 2010


Author: dgregor
Date: Tue Apr  6 19:21:17 2010
New Revision: 100587

URL: http://llvm.org/viewvc/llvm-project?rev=100587&view=rev
Log:
Implement code completion for Objective-C method declarations and
definitions, e.g., after 

  -

or

  - (id)

we'll find all of the "likely" instance methods that one would want to
declare or define at this point. In the latter case, we only produce
results whose return types match "id".


Added:
    cfe/trunk/test/Index/complete-method-decls.m
Modified:
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/lib/Parse/ParseObjc.cpp
    cfe/trunk/lib/Sema/CodeCompleteConsumer.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=100587&r1=100586&r2=100587&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Tue Apr  6 19:21:17 2010
@@ -2795,6 +2795,32 @@
                                                    IdentifierInfo *PropertyName,
                                                   DeclPtrTy ObjCImpDecl) {
   }
+
+  /// \brief Code completion for an Objective-C method declaration or
+  /// definition, which may occur within an interface, category,
+  /// extension, protocol, or implementation thereof (where applicable).
+  ///
+  /// This code completion action is invoked after the "-" or "+" that
+  /// starts a method declaration or definition, and after the return
+  /// type such a declaration (e.g., "- (id)").
+  ///
+  /// \param S The scope in which the completion occurs.
+  ///
+  /// \param IsInstanceMethod Whether this is an instance method
+  /// (introduced with '-'); otherwise, it's a class method
+  /// (introduced with '+').
+  ///
+  /// \param ReturnType If non-NULL, the specified return type of the method
+  /// being declared or defined.
+  ///
+  /// \param IDecl The interface, category, protocol, or
+  /// implementation, or category implementation in which this method
+  /// declaration or definition occurs.
+  virtual void CodeCompleteObjCMethodDecl(Scope *S, 
+                                          bool IsInstanceMethod,
+                                          TypeTy *ReturnType,
+                                          DeclPtrTy IDecl) {
+  }
   //@}
 };
 

Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=100587&r1=100586&r2=100587&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
+++ cfe/trunk/lib/Parse/ParseObjc.cpp Tue Apr  6 19:21:17 2010
@@ -787,6 +787,12 @@
                                           tok::ObjCKeywordKind MethodImplKind) {
   ParsingDeclRAIIObject PD(*this);
 
+  if (Tok.is(tok::code_completion)) {
+    Actions.CodeCompleteObjCMethodDecl(CurScope, mType == tok::minus, 
+                                       /*ReturnType=*/0, IDecl);
+    ConsumeToken();
+  }
+
   // Parse the return type if present.
   TypeTy *ReturnType = 0;
   ObjCDeclSpec DSRet;
@@ -798,6 +804,12 @@
   if (getLang().ObjC2 && Tok.is(tok::kw___attribute))
     MethodAttrs.reset(ParseGNUAttributes());
 
+  if (Tok.is(tok::code_completion)) {
+    Actions.CodeCompleteObjCMethodDecl(CurScope, mType == tok::minus, 
+                                       ReturnType, IDecl);
+    ConsumeToken();
+  }
+
   // Now parse the selector.
   SourceLocation selLoc;
   IdentifierInfo *SelIdent = ParseObjCSelectorPiece(selLoc);

Modified: cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp?rev=100587&r1=100586&r2=100587&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp (original)
+++ cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp Tue Apr  6 19:21:17 2010
@@ -86,7 +86,7 @@
     break;
 
   case CK_Colon:
-    this->Text = ": ";
+    this->Text = ":";
     break;
 
   case CK_SemiColon:

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=100587&r1=100586&r2=100587&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Apr  6 19:21:17 2010
@@ -4299,6 +4299,10 @@
   virtual void CodeCompleteObjCPropertySynthesizeIvar(Scope *S, 
                                                   IdentifierInfo *PropertyName,
                                                       DeclPtrTy ObjCImpDecl);
+  virtual void CodeCompleteObjCMethodDecl(Scope *S, 
+                                          bool IsInstanceMethod,
+                                          TypeTy *ReturnType,
+                                          DeclPtrTy IDecl);
   //@}
   
   //===--------------------------------------------------------------------===//

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=100587&r1=100586&r2=100587&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Tue Apr  6 19:21:17 2010
@@ -3411,3 +3411,224 @@
   
   HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
 }
+
+typedef llvm::DenseMap<Selector, ObjCMethodDecl *> KnownMethodsMap;
+
+/// \brief Find all of the methods that reside in the given container
+/// (and its superclasses, protocols, etc.) that meet the given
+/// criteria. Insert those methods into the map of known methods,
+/// indexed by selector so they can be easily found.
+static void FindImplementableMethods(ASTContext &Context,
+                                     ObjCContainerDecl *Container,
+                                     bool WantInstanceMethods,
+                                     QualType ReturnType,
+                                     bool IsInImplementation,
+                                     KnownMethodsMap &KnownMethods) {
+  if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container)) {
+    // Recurse into protocols.
+    const ObjCList<ObjCProtocolDecl> &Protocols
+      = IFace->getReferencedProtocols();
+    for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+           E = Protocols.end(); 
+         I != E; ++I)
+      FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType,
+                               IsInImplementation, KnownMethods);
+
+    // If we're not in the implementation of a class, also visit the
+    // superclass.
+    if (!IsInImplementation && IFace->getSuperClass())
+      FindImplementableMethods(Context, IFace->getSuperClass(), 
+                               WantInstanceMethods, ReturnType,
+                               IsInImplementation, KnownMethods);
+
+    // Add methods from any class extensions (but not from categories;
+    // those should go into category implementations).
+    for (ObjCCategoryDecl *Cat = IFace->getCategoryList(); Cat;
+         Cat = Cat->getNextClassCategory()) {
+      if (!Cat->IsClassExtension())
+        continue;
+
+      FindImplementableMethods(Context, Cat, WantInstanceMethods, ReturnType,
+                               IsInImplementation, KnownMethods);      
+    }
+  }
+
+  if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(Container)) {
+    // Recurse into protocols.
+    const ObjCList<ObjCProtocolDecl> &Protocols
+      = Category->getReferencedProtocols();
+    for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+           E = Protocols.end(); 
+         I != E; ++I)
+      FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType,
+                               IsInImplementation, KnownMethods);
+  }
+
+  if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) {
+    // Recurse into protocols.
+    const ObjCList<ObjCProtocolDecl> &Protocols
+      = Protocol->getReferencedProtocols();
+    for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+           E = Protocols.end(); 
+         I != E; ++I)
+      FindImplementableMethods(Context, *I, WantInstanceMethods, ReturnType,
+                               IsInImplementation, KnownMethods);
+  }
+
+  // Add methods in this container. This operation occurs last because
+  // we want the methods from this container to override any methods
+  // we've previously seen with the same selector.
+  for (ObjCContainerDecl::method_iterator M = Container->meth_begin(),
+                                       MEnd = Container->meth_end();
+       M != MEnd; ++M) {
+    if ((*M)->isInstanceMethod() == WantInstanceMethods) {
+      if (!ReturnType.isNull() &&
+          !Context.hasSameUnqualifiedType(ReturnType, (*M)->getResultType()))
+        continue;
+
+      KnownMethods[(*M)->getSelector()] = *M;
+    }
+  }
+}
+
+void Sema::CodeCompleteObjCMethodDecl(Scope *S, 
+                                      bool IsInstanceMethod,
+                                      TypeTy *ReturnTy,
+                                      DeclPtrTy IDecl) {
+  // Determine the return type of the method we're declaring, if
+  // provided.
+  QualType ReturnType = GetTypeFromParser(ReturnTy);
+
+  // Determine where we should start searching for methods, and where we 
+  ObjCContainerDecl *SearchDecl = 0, *CurrentDecl = 0;
+  bool IsInImplementation = false;
+  if (Decl *D = IDecl.getAs<Decl>()) {
+    if (ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D)) {
+      SearchDecl = Impl->getClassInterface();
+      CurrentDecl = Impl;
+      IsInImplementation = true;
+    } else if (ObjCCategoryImplDecl *CatImpl 
+                                       = dyn_cast<ObjCCategoryImplDecl>(D)) {
+      SearchDecl = CatImpl->getCategoryDecl();
+      CurrentDecl = CatImpl;
+      IsInImplementation = true;
+    } else {
+      SearchDecl = dyn_cast<ObjCContainerDecl>(D);
+      CurrentDecl = SearchDecl;
+    }
+  }
+
+  if (!SearchDecl && S) {
+    if (DeclContext *DC = static_cast<DeclContext *>(S->getEntity())) {
+      SearchDecl = dyn_cast<ObjCContainerDecl>(DC);
+      CurrentDecl = SearchDecl;
+    }
+  }
+
+  if (!SearchDecl || !CurrentDecl) {
+    HandleCodeCompleteResults(this, CodeCompleter, 0, 0);
+    return;
+  }
+    
+  // Find all of the methods that we could declare/implement here.
+  KnownMethodsMap KnownMethods;
+  FindImplementableMethods(Context, SearchDecl, IsInstanceMethod, 
+                           ReturnType, IsInImplementation, KnownMethods);
+  
+  // Erase any methods that have already been declared or
+  // implemented here.
+  for (ObjCContainerDecl::method_iterator M = CurrentDecl->meth_begin(),
+                                       MEnd = CurrentDecl->meth_end();
+       M != MEnd; ++M) {
+    if ((*M)->isInstanceMethod() != IsInstanceMethod)
+      continue;
+    
+    KnownMethodsMap::iterator Pos = KnownMethods.find((*M)->getSelector());
+    if (Pos != KnownMethods.end())
+      KnownMethods.erase(Pos);
+  }
+
+  // Add declarations or definitions for each of the known methods.
+  typedef CodeCompleteConsumer::Result Result;
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+  PrintingPolicy Policy(Context.PrintingPolicy);
+  Policy.AnonymousTagLocations = false;
+  for (KnownMethodsMap::iterator M = KnownMethods.begin(), 
+                              MEnd = KnownMethods.end();
+       M != MEnd; ++M) {
+    ObjCMethodDecl *Method = M->second;
+    CodeCompletionString *Pattern = new CodeCompletionString;
+    
+    // If the result type was not already provided, add it to the
+    // pattern as (type).
+    if (ReturnType.isNull()) {
+      std::string TypeStr;
+      Method->getResultType().getAsStringInternal(TypeStr, Policy);
+      Pattern->AddChunk(CodeCompletionString::CK_LeftParen);
+      Pattern->AddTextChunk(TypeStr);
+      Pattern->AddChunk(CodeCompletionString::CK_RightParen);
+    }
+
+    Selector Sel = Method->getSelector();
+
+    // Add the first part of the selector to the pattern.
+    Pattern->AddTypedTextChunk(Sel.getIdentifierInfoForSlot(0)->getName());
+
+    // Add parameters to the pattern.
+    unsigned I = 0;
+    for (ObjCMethodDecl::param_iterator P = Method->param_begin(), 
+                                     PEnd = Method->param_end();
+         P != PEnd; (void)++P, ++I) {
+      // Add the part of the selector name.
+      if (I == 0)
+        Pattern->AddChunk(CodeCompletionString::CK_Colon);
+      else if (I < Sel.getNumArgs()) {
+        Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace);
+        Pattern->AddTextChunk(Sel.getIdentifierInfoForSlot(1)->getName());
+        Pattern->AddChunk(CodeCompletionString::CK_Colon);
+      } else
+        break;
+
+      // Add the parameter type.
+      std::string TypeStr;
+      (*P)->getOriginalType().getAsStringInternal(TypeStr, Policy);
+      Pattern->AddChunk(CodeCompletionString::CK_LeftParen);
+      Pattern->AddTextChunk(TypeStr);
+      Pattern->AddChunk(CodeCompletionString::CK_RightParen);
+      
+      if (IdentifierInfo *Id = (*P)->getIdentifier())
+        Pattern->AddTextChunk(Id->getName());
+    }
+
+    if (Method->isVariadic()) {
+      if (Method->param_size() > 0)
+        Pattern->AddChunk(CodeCompletionString::CK_Comma);
+      Pattern->AddTextChunk("...");
+    }
+
+    if (IsInImplementation) {
+      // We will be defining the method here, so add a compound statement.
+      Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace);
+      Pattern->AddChunk(CodeCompletionString::CK_LeftBrace);
+      Pattern->AddChunk(CodeCompletionString::CK_VerticalSpace);
+      if (!Method->getResultType()->isVoidType()) {
+        // If the result type is not void, add a return clause.
+        Pattern->AddTextChunk("return");
+        Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace);
+        Pattern->AddPlaceholderChunk("expression");
+        Pattern->AddChunk(CodeCompletionString::CK_SemiColon);
+      } else
+        Pattern->AddPlaceholderChunk("statements");
+        
+      Pattern->AddChunk(CodeCompletionString::CK_VerticalSpace);
+      Pattern->AddChunk(CodeCompletionString::CK_RightBrace);
+    }
+
+    Results.AddResult(Result(Pattern));
+  }
+
+  Results.ExitScope();
+  
+  HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
+}

Added: cfe/trunk/test/Index/complete-method-decls.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/complete-method-decls.m?rev=100587&view=auto
==============================================================================
--- cfe/trunk/test/Index/complete-method-decls.m (added)
+++ cfe/trunk/test/Index/complete-method-decls.m Tue Apr  6 19:21:17 2010
@@ -0,0 +1,82 @@
+/* Note: the RUN lines are near the end of the file, since line/column
+   matter for this test. */
+
+ at protocol P1
+- (id)abc;
+- (id)initWithInt:(int)x;
+- (id)initWithTwoInts:(int)x second:(int)y;
+- (int)getInt;
+- (id)getSelf;
+ at end
+
+ at protocol P2<P1>
++ (id)alloc;
+ at end
+
+ at interface A <P1>
+- (id)init;
+- (int)getValue;
+ at end
+
+ at interface B : A<P2>
+- (id)initWithInt:(int)x;
+- (int)getSecondValue;
+- (id)getSelf;
+- (int)setValue:(int)x;
+ at end
+
+ at interface B (FooBar)
+- (id)categoryFunction:(int)x;
+ at end
+
+ at implementation B
+- (int)getSecondValue { return 0; }
+- (id)init { return self; }
+- (id)getSelf { return self; }
+- (void)setValue:(int)x { }
+- (id)initWithTwoInts:(int)x second:(int)y { return self; }
++ (id)alloc { return 0; }
+ at end
+
+ at implementation B (FooBar)
+- (id)categoryFunction:(int)x { return self; }
+ at end
+
+// RUN: c-index-test -code-completion-at=%s:17:3 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText abc}
+// CHECK-CC1: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText getInt}
+// CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText getSelf}
+// CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText initWithInt}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}
+// CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}
+// RUN: c-index-test -code-completion-at=%s:17:7 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: NotImplemented:{TypedText abc}
+// CHECK-CC2-NEXT: NotImplemented:{TypedText getSelf}
+// CHECK-CC2: NotImplemented:{TypedText initWithInt}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}
+// CHECK-CC2: NotImplemented:{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}
+// RUN: c-index-test -code-completion-at=%s:24:7 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: NotImplemented:{TypedText abc}
+// CHECK-CC3-NEXT: NotImplemented:{TypedText getSelf}
+// CHECK-CC3: NotImplemented:{TypedText init}
+// CHECK-CC3: NotImplemented:{TypedText initWithInt}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}
+// CHECK-CC3: NotImplemented:{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}
+// RUN: c-index-test -code-completion-at=%s:33:3 %s | FileCheck -check-prefix=CHECK-CC4 %s
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText abc}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText getInt}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText getSecondValue}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText getSelf}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText initWithInt}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC4: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText setValue}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// RUN: c-index-test -code-completion-at=%s:33:8 %s | FileCheck -check-prefix=CHECK-CC5 %s
+// CHECK-CC5: NotImplemented:{TypedText getInt}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC5: NotImplemented:{TypedText getSecondValue}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC5-NOT: {TypedText getSelf}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// CHECK-CC5: NotImplemented:{TypedText setValue}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{LeftBrace {}{VerticalSpace
+// RUN: c-index-test -code-completion-at=%s:37:7 %s | FileCheck -check-prefix=CHECK-CC6 %s
+// CHECK-CC6: NotImplemented:{TypedText abc}{HorizontalSpace  }{LeftBrace {}{VerticalSpace 
+// CHECK-CC6-NOT: getSelf
+// CHECK-CC6: NotImplemented:{TypedText initWithInt}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{LeftBrace {}{VerticalSpace 
+// CHECK-CC6: NotImplemented:{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}{HorizontalSpace  }{LeftBrace {}{VerticalSpace 
+// RUN: c-index-test -code-completion-at=%s:42:3 %s | FileCheck -check-prefix=CHECK-CC7 %s
+// CHECK-CC7: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText categoryFunction}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace  }{LeftBrace {}{VerticalSpace 
+





More information about the cfe-commits mailing list