]> granicus.if.org Git - clang/commitdiff
When sending a message to "id", apply some heuristics to try to narrow
authorDouglas Gregor <dgregor@apple.com>
Tue, 6 Apr 2010 19:22:33 +0000 (19:22 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 6 Apr 2010 19:22:33 +0000 (19:22 +0000)
down the set of code-completion results based on Objective-C
conventions.

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

lib/Sema/SemaCodeComplete.cpp
test/Index/complete-objc-message-id.m [new file with mode: 0644]

index ac56fab9e8a98fc2a84b27cdf22037d2a601ebcf..49a32a6bddd393b96e0fed054edfd3f648e23e38 100644 (file)
@@ -20,6 +20,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include <list>
 #include <map>
 #include <vector>
@@ -2909,6 +2910,65 @@ void Sema::CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ObjCImplDecl,
   HandleCodeCompleteResults(this, CodeCompleter,Results.data(),Results.size());
 }
 
+/// \brief When we have an expression with type "id", we may assume
+/// that it has some more-specific class type based on knowledge of
+/// common uses of Objective-C. This routine returns that class type,
+/// or NULL if no better result could be determined.
+static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) {
+  ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E);
+  if (!Msg)
+    return 0;
+
+  Selector Sel = Msg->getSelector();
+  if (Sel.isNull())
+    return 0;
+
+  IdentifierInfo *Id = Sel.getIdentifierInfoForSlot(0);
+  if (!Id)
+    return 0;
+
+  ObjCMethodDecl *Method = Msg->getMethodDecl();
+  if (!Method)
+    return 0;
+
+  // Determine the class that we're sending the message to.
+  ObjCInterfaceDecl *IFace = Msg->getClassInfo().Decl;
+  if (!IFace) {
+    if (Expr *Receiver = Msg->getReceiver()) {
+      QualType T = Receiver->getType();
+      if (const ObjCObjectPointerType *Ptr = T->getAs<ObjCObjectPointerType>())
+        IFace = Ptr->getInterfaceDecl();
+    }
+  }
+
+  if (!IFace)
+    return 0;
+
+  ObjCInterfaceDecl *Super = IFace->getSuperClass();
+  if (Method->isInstanceMethod())
+    return llvm::StringSwitch<ObjCInterfaceDecl *>(Id->getName())
+      .Case("retain", IFace)
+      .Case("autorelease", IFace)
+      .Case("copy", IFace)
+      .Case("copyWithZone", IFace)
+      .Case("mutableCopy", IFace)
+      .Case("mutableCopyWithZone", IFace)
+      .Case("awakeFromCoder", IFace)
+      .Case("replacementObjectFromCoder", IFace)
+      .Case("class", IFace)
+      .Case("classForCoder", IFace)
+      .Case("superclass", Super)
+      .Default(0);
+
+  return llvm::StringSwitch<ObjCInterfaceDecl *>(Id->getName())
+    .Case("new", IFace)
+    .Case("alloc", IFace)
+    .Case("allocWithZone", IFace)
+    .Case("class", IFace)
+    .Case("superclass", Super)
+    .Default(0);
+}
+
 void Sema::CodeCompleteObjCClassMessage(Scope *S, IdentifierInfo *FName,
                                         SourceLocation FNameLoc,
                                         IdentifierInfo **SelIdents,
@@ -3032,6 +3092,14 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver,
   // Build the set of methods we can see.
   ResultBuilder Results(*this);
   Results.EnterNewScope();
+
+  // If we're messaging an expression with type "id" or "Class", check
+  // whether we know something special about the receiver that allows
+  // us to assume a more-specific receiver type.
+  if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType())
+    if (ObjCInterfaceDecl *IFace = GetAssumedMessageSendExprType(RecExpr))
+      ReceiverType = Context.getObjCObjectPointerType(
+                                          Context.getObjCInterfaceType(IFace));
   
   // Handle messages to Class. This really isn't a message to an instance
   // method, so we treat it the same way we would treat a message send to a
diff --git a/test/Index/complete-objc-message-id.m b/test/Index/complete-objc-message-id.m
new file mode 100644 (file)
index 0000000..a75ee4a
--- /dev/null
@@ -0,0 +1,42 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test.
+
+@interface A
++ (id)alloc;
++ (id)init;
++ (id)new;
++ (Class)class;
++ (Class)superclass;
+- (id)retain;
+- (id)autorelease;
+- (id)superclass;
+@end
+
+@interface B : A
+- (int)B_method;
+@end
+
+@interface Unrelated
++ (id)icky;
+@end
+
+void message_id(B *b) {
+  [[A alloc] init];
+  [[b retain] B_method];
+  [[b superclass] B_method];
+}
+
+// RUN: c-index-test -code-completion-at=%s:24:14 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: ObjCInstanceMethodDecl:{ResultType id}{TypedText autorelease}
+// CHECK-CC1-NOT: B_method
+// CHECK-CC1: ObjCInstanceMethodDecl:{ResultType id}{TypedText retain}
+// RUN: c-index-test -code-completion-at=%s:25:15 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: ObjCInstanceMethodDecl:{ResultType id}{TypedText autorelease}
+// CHECK-CC2: ObjCInstanceMethodDecl:{ResultType int}{TypedText B_method}
+// CHECK-CC2: ObjCInstanceMethodDecl:{ResultType id}{TypedText retain}
+// RUN: c-index-test -code-completion-at=%s:26:19 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: ObjCInstanceMethodDecl:{ResultType id}{TypedText autorelease}
+// CHECK-CC3-NOT: B_method
+// CHECK-CC3: ObjCInstanceMethodDecl:{ResultType id}{TypedText retain}
+
+