From: Douglas Gregor Date: Tue, 6 Apr 2010 19:22:33 +0000 (+0000) Subject: When sending a message to "id", apply some heuristics to try to narrow X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=22f569918dd599eb6d810e066eddc4ae74c6b2b0;p=clang When sending a message to "id", apply some heuristics to try to narrow 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 --- diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index ac56fab9e8..49a32a6bdd 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -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 #include #include @@ -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(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()) + IFace = Ptr->getInterfaceDecl(); + } + } + + if (!IFace) + return 0; + + ObjCInterfaceDecl *Super = IFace->getSuperClass(); + if (Method->isInstanceMethod()) + return llvm::StringSwitch(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(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 index 0000000000..a75ee4a81d --- /dev/null +++ b/test/Index/complete-objc-message-id.m @@ -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} + +