]> granicus.if.org Git - clang/commitdiff
Add basic code completion support for ObjC messages.
authorSteve Naroff <snaroff@apple.com>
Sat, 7 Nov 2009 02:08:14 +0000 (02:08 +0000)
committerSteve Naroff <snaroff@apple.com>
Sat, 7 Nov 2009 02:08:14 +0000 (02:08 +0000)
Still a work in progress...

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

include/clang/Parse/Action.h
lib/Parse/ParseObjc.cpp
lib/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp
test/CodeCompletion/objc-message.m [new file with mode: 0644]

index 657a14fff0828de4aa86ad7afff7a35ee2acf1e1..929e18ba01dabe60a6525bfac64e2e5e3a9dd1e7 100644 (file)
@@ -2329,6 +2329,26 @@ public:
   ///
   /// \param S the scope in which the operator keyword occurs.  
   virtual void CodeCompleteObjCProperty(Scope *S, ObjCDeclSpec &ODS) { }
+  
+  /// \brief Code completion for an ObjC factory method (from within a message 
+  /// expression).
+  ///
+  /// This code completion action is invoked when the code-completion token is
+  /// found after the class name.
+  ///
+  /// \param S the scope in which the message expression occurs. 
+  /// \param FName the factory name. 
+  virtual void CodeCompleteObjCFactoryMethod(Scope *S, IdentifierInfo *FName){ }
+  
+  /// \brief Code completion for an ObjC instance method (from within a message 
+  /// expression).
+  ///
+  /// This code completion action is invoked when the code-completion token is
+  /// found after the receiver expression.
+  ///
+  /// \param S the scope in which the operator keyword occurs.  
+  /// \param Receiver an expression for the receiver of the message. 
+  virtual void CodeCompleteObjCInstanceMethod(Scope *S, ExprTy *Receiver) { }
   //@}
 };
 
index 71aeb30927a3a932ff65cf8c313f3464bdd4030a..d06d8b6e9c72de8d7c2995fdb1170558e13ba564 100644 (file)
@@ -1549,6 +1549,13 @@ Parser::ParseObjCMessageExpressionBody(SourceLocation LBracLoc,
                                        SourceLocation NameLoc,
                                        IdentifierInfo *ReceiverName,
                                        ExprArg ReceiverExpr) {
+  if (Tok.is(tok::code_completion)) {
+    if (ReceiverName)
+      Actions.CodeCompleteObjCFactoryMethod(CurScope, ReceiverName);
+    else
+      Actions.CodeCompleteObjCInstanceMethod(CurScope, ReceiverExpr.release());
+    ConsumeToken();
+  }
   // Parse objc-selector
   SourceLocation Loc;
   IdentifierInfo *selIdent = ParseObjCSelectorPiece(Loc);
index cad06d7e0a06adc659e6c7ce5debabeceae25dde..45d6b57437dd122368a08d6ea36c6933f379661c 100644 (file)
@@ -3764,6 +3764,8 @@ public:
   virtual void CodeCompleteOperatorName(Scope *S);
   
   virtual void CodeCompleteObjCProperty(Scope *S, ObjCDeclSpec &ODS);
+  virtual void CodeCompleteObjCFactoryMethod(Scope *S, IdentifierInfo *FName);
+  virtual void CodeCompleteObjCInstanceMethod(Scope *S, ExprTy *Receiver);
   //@}
   
   //===--------------------------------------------------------------------===//
index 9963fc3d45786e8b0bb7f4cac0e95b75c74e9ef4..c6323956cc91945c2ad3cebb7fa70b71c5d45300 100644 (file)
@@ -1518,3 +1518,114 @@ void Sema::CodeCompleteObjCProperty(Scope *S, ObjCDeclSpec &ODS) {
   Results.ExitScope();
   HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
 }
+
+void Sema::CodeCompleteObjCFactoryMethod(Scope *S, IdentifierInfo *FName) {
+  typedef CodeCompleteConsumer::Result Result;
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+  
+  ObjCInterfaceDecl *CDecl = getObjCInterfaceDecl(FName);
+    
+  while (CDecl != NULL) {
+    for (ObjCInterfaceDecl::classmeth_iterator I = CDecl->classmeth_begin(), 
+                                               E = CDecl->classmeth_end(); 
+         I != E; ++I) {
+      Results.MaybeAddResult(Result(*I, 0), CurContext);
+    }
+    // Add class methods in protocols.
+    const ObjCList<ObjCProtocolDecl> &Protocols=CDecl->getReferencedProtocols();
+    for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+         E = Protocols.end(); I != E; ++I) {
+      for (ObjCProtocolDecl::classmeth_iterator I2 = (*I)->classmeth_begin(), 
+                                                E2 = (*I)->classmeth_end(); 
+           I2 != E2; ++I2) {
+        Results.MaybeAddResult(Result(*I2, 0), CurContext);
+      }
+    }
+    // Add class methods in categories.
+    ObjCCategoryDecl *CatDecl = CDecl->getCategoryList();
+    while (CatDecl) {
+      for (ObjCCategoryDecl::classmeth_iterator I = CatDecl->classmeth_begin(), 
+                                                E = CatDecl->classmeth_end(); 
+           I != E; ++I) {
+        Results.MaybeAddResult(Result(*I, 0), CurContext);
+      }
+      // Add a categories protocol methods.
+      const ObjCList<ObjCProtocolDecl> &Protocols =
+        CatDecl->getReferencedProtocols();
+      for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+           E = Protocols.end(); I != E; ++I) {
+        for (ObjCProtocolDecl::classmeth_iterator I2 = (*I)->classmeth_begin(), 
+                                                  E2 = (*I)->classmeth_end(); 
+             I2 != E2; ++I2) {
+          Results.MaybeAddResult(Result(*I2, 0), CurContext);
+        }
+      }
+      CatDecl = CatDecl->getNextClassCategory();
+    }
+    CDecl = CDecl->getSuperClass();
+  }
+  Results.ExitScope();
+  // This also suppresses remaining diagnostics.
+  HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
+}
+
+void Sema::CodeCompleteObjCInstanceMethod(Scope *S, ExprTy *Receiver) {
+  typedef CodeCompleteConsumer::Result Result;
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+  
+  Expr *RecExpr = static_cast<Expr *>(Receiver);
+  QualType RecType = RecExpr->getType();
+  
+  const ObjCObjectPointerType* OCOPT = RecType->getAs<ObjCObjectPointerType>();
+  
+  if (!OCOPT)
+    return;
+  
+  // FIXME: handle 'id', 'Class', and qualified types. 
+  ObjCInterfaceDecl *CDecl = OCOPT->getInterfaceDecl();
+
+  while (CDecl != NULL) {
+    for (ObjCInterfaceDecl::instmeth_iterator I = CDecl->instmeth_begin(), 
+                                               E = CDecl->instmeth_end(); 
+         I != E; ++I) {
+      Results.MaybeAddResult(Result(*I, 0), CurContext);
+    }
+    // Add class methods in protocols.
+    const ObjCList<ObjCProtocolDecl> &Protocols=CDecl->getReferencedProtocols();
+    for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+         E = Protocols.end(); I != E; ++I) {
+      for (ObjCProtocolDecl::instmeth_iterator I2 = (*I)->instmeth_begin(), 
+                                                E2 = (*I)->instmeth_end(); 
+           I2 != E2; ++I2) {
+        Results.MaybeAddResult(Result(*I2, 0), CurContext);
+      }
+    }
+    // Add class methods in categories.
+    ObjCCategoryDecl *CatDecl = CDecl->getCategoryList();
+    while (CatDecl) {
+      for (ObjCCategoryDecl::instmeth_iterator I = CatDecl->instmeth_begin(), 
+                                                E = CatDecl->instmeth_end(); 
+           I != E; ++I) {
+        Results.MaybeAddResult(Result(*I, 0), CurContext);
+      }
+      // Add a categories protocol methods.
+      const ObjCList<ObjCProtocolDecl> &Protocols =
+        CatDecl->getReferencedProtocols();
+      for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(),
+           E = Protocols.end(); I != E; ++I) {
+        for (ObjCProtocolDecl::instmeth_iterator I2 = (*I)->instmeth_begin(), 
+                                                  E2 = (*I)->instmeth_end(); 
+             I2 != E2; ++I2) {
+          Results.MaybeAddResult(Result(*I2, 0), CurContext);
+        }
+      }
+      CatDecl = CatDecl->getNextClassCategory();
+    }
+    CDecl = CDecl->getSuperClass();
+  }
+  Results.ExitScope();
+  // This also suppresses remaining diagnostics.
+  HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());  
+}
diff --git a/test/CodeCompletion/objc-message.m b/test/CodeCompletion/objc-message.m
new file mode 100644 (file)
index 0000000..d95a36d
--- /dev/null
@@ -0,0 +1,35 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+@protocol FooTestProtocol
++ protocolClassMethod;
+- protocolInstanceMethod;
+@end
+@interface Foo <FooTestProtocol> {
+  void *isa;
+}
++ (int)classMethod1:a withKeyword:b;
++ (void)classMethod2;
++ new;
+- instanceMethod1;
+@end
+
+@interface Foo (FooTestCategory)
++ categoryClassMethod;
+- categoryInstanceMethod;
+@end
+
+void func() {
+  Foo *obj = [Foo new];
+  [obj xx];
+}
+// RUN: clang-cc -fsyntax-only -code-completion-at=%s:23:19 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s &&
+// CHECK-CC1: categoryClassMethod : 0
+// CHECK-CC1: classMethod2 : 0
+// CHECK-CC1: new : 0
+// CHECK-CC1: protocolClassMethod : 0
+// CHECK-CC1: classMethod1:withKeyword: : 0
+// RUN: clang-cc -fsyntax-only -code-completion-at=%s:24:8 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: categoryInstanceMethod : 0
+// CHECK-CC2: instanceMethod1 : 0
+// CHECK-CC2: protocolInstanceMethod : 0