зеркало из https://github.com/microsoft/clang-1.git
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
This commit is contained in:
Родитель
914803f5d5
Коммит
22f569918d
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
Загрузка…
Ссылка в новой задаче