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. 

llvm-svn: 100548
This commit is contained in:
Douglas Gregor 2010-04-06 19:22:33 +00:00
parent 08355d6cda
commit 9d2ddb2e5d
2 changed files with 110 additions and 0 deletions

View 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

View File

@ -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}