Skip to content

Commit a817a19

Browse files
committed
Implement a code-completion hook for the receiver of an Objective-C
message. This completion gives better results than just using the "expression" completion, which is effectively what happened before. llvm-svn: 104895
1 parent 3d3ee87 commit a817a19

File tree

5 files changed

+119
-10
lines changed

5 files changed

+119
-10
lines changed

clang/include/clang/Parse/Action.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2853,6 +2853,14 @@ class Action : public ActionBase {
28532853
unsigned NumMethods) {
28542854
}
28552855

2856+
/// \brief Code completion for the receiver in an Objective-C message send.
2857+
///
2858+
/// This code completion action is invoked when we see a '[' that indicates
2859+
/// the start of an Objective-C message send.
2860+
///
2861+
/// \param S The scope in which the Objective-C message send occurs.
2862+
virtual void CodeCompleteObjCMessageReceiver(Scope *S) { }
2863+
28562864
/// \brief Code completion for an ObjC message expression that sends
28572865
/// a message to the superclass.
28582866
///

clang/lib/Parse/ParseObjc.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,13 @@ Parser::OwningExprResult Parser::ParseObjCMessageExpression() {
18071807
assert(Tok.is(tok::l_square) && "'[' expected");
18081808
SourceLocation LBracLoc = ConsumeBracket(); // consume '['
18091809

1810+
if (Tok.is(tok::code_completion)) {
1811+
Actions.CodeCompleteObjCMessageReceiver(CurScope);
1812+
ConsumeCodeCompletionToken();
1813+
SkipUntil(tok::r_square);
1814+
return ExprError();
1815+
}
1816+
18101817
if (getLang().CPlusPlus) {
18111818
// We completely separate the C and C++ cases because C++ requires
18121819
// more complicated (read: slower) parsing.

clang/lib/Sema/Sema.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4440,7 +4440,7 @@ class Sema : public Action {
44404440
virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl,
44414441
DeclPtrTy *Methods,
44424442
unsigned NumMethods);
4443-
4443+
virtual void CodeCompleteObjCMessageReceiver(Scope *S);
44444444
virtual void CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc,
44454445
IdentifierInfo **SelIdents,
44464446
unsigned NumSelIdents);

clang/lib/Sema/SemaCodeComplete.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ namespace {
222222
bool IsType(NamedDecl *ND) const;
223223
bool IsMember(NamedDecl *ND) const;
224224
bool IsObjCIvar(NamedDecl *ND) const;
225+
bool IsObjCMessageReceiver(NamedDecl *ND) const;
225226
//@}
226227
};
227228
}
@@ -732,6 +733,78 @@ bool ResultBuilder::IsMember(NamedDecl *ND) const {
732733
isa<ObjCPropertyDecl>(ND);
733734
}
734735

736+
/// \brief Get the type that a given expression will have if this declaration
737+
/// is used as an expression in its "typical" code-completion form.
738+
static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) {
739+
ND = cast<NamedDecl>(ND->getUnderlyingDecl());
740+
741+
if (TypeDecl *Type = dyn_cast<TypeDecl>(ND))
742+
return C.getTypeDeclType(Type);
743+
if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND))
744+
return C.getObjCInterfaceType(Iface);
745+
746+
QualType T;
747+
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND))
748+
T = Function->getResultType();
749+
else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND))
750+
T = Method->getResultType();
751+
else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND))
752+
T = FunTmpl->getTemplatedDecl()->getResultType();
753+
else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND))
754+
T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext()));
755+
else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND))
756+
T = Property->getType();
757+
else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND))
758+
T = Value->getType();
759+
else
760+
return QualType();
761+
762+
return T.getNonReferenceType();
763+
}
764+
765+
static bool isObjCReceiverType(ASTContext &C, QualType T) {
766+
T = C.getCanonicalType(T);
767+
switch (T->getTypeClass()) {
768+
case Type::ObjCObject:
769+
case Type::ObjCInterface:
770+
case Type::ObjCObjectPointer:
771+
return true;
772+
773+
case Type::Builtin:
774+
switch (cast<BuiltinType>(T)->getKind()) {
775+
case BuiltinType::ObjCId:
776+
case BuiltinType::ObjCClass:
777+
case BuiltinType::ObjCSel:
778+
return true;
779+
780+
default:
781+
break;
782+
}
783+
return false;
784+
785+
default:
786+
break;
787+
}
788+
789+
if (!C.getLangOptions().CPlusPlus)
790+
return false;
791+
792+
// FIXME: We could perform more analysis here to determine whether a
793+
// particular class type has any conversions to Objective-C types. For now,
794+
// just accept all class types.
795+
return T->isDependentType() || T->isRecordType();
796+
}
797+
798+
bool ResultBuilder::IsObjCMessageReceiver(NamedDecl *ND) const {
799+
QualType T = getDeclUsageType(SemaRef.Context, ND);
800+
if (T.isNull())
801+
return false;
802+
803+
T = SemaRef.Context.getBaseElementType(T);
804+
return isObjCReceiverType(SemaRef.Context, T);
805+
}
806+
807+
735808
/// \rief Determines whether the given declaration is an Objective-C
736809
/// instance variable.
737810
bool ResultBuilder::IsObjCIvar(NamedDecl *ND) const {
@@ -3021,6 +3094,31 @@ static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) {
30213094
.Default(0);
30223095
}
30233096

3097+
void Sema::CodeCompleteObjCMessageReceiver(Scope *S) {
3098+
typedef CodeCompleteConsumer::Result Result;
3099+
ResultBuilder Results(*this);
3100+
3101+
// Find anything that looks like it could be a message receiver.
3102+
Results.setFilter(&ResultBuilder::IsObjCMessageReceiver);
3103+
CodeCompletionDeclConsumer Consumer(Results, CurContext);
3104+
Results.EnterNewScope();
3105+
LookupVisibleDecls(S, LookupOrdinaryName, Consumer);
3106+
3107+
// If we are in an Objective-C method inside a class that has a superclass,
3108+
// add "super" as an option.
3109+
if (ObjCMethodDecl *Method = getCurMethodDecl())
3110+
if (ObjCInterfaceDecl *Iface = Method->getClassInterface())
3111+
if (Iface->getSuperClass())
3112+
Results.AddResult(Result("super"));
3113+
3114+
Results.ExitScope();
3115+
3116+
if (CodeCompleter->includeMacros())
3117+
AddMacroResults(PP, Results);
3118+
HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
3119+
3120+
}
3121+
30243122
void Sema::CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc,
30253123
IdentifierInfo **SelIdents,
30263124
unsigned NumSelIdents) {

clang/test/Index/complete-objc-message.m

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ - (int)MyPrivateInstMethod {
5151
return 3;
5252
}
5353
@end
54-
54+
MyClass *getMyClass();
5555
@implementation MySubClass
5656
+ (int)MySubClassMethod {
5757
return 2;
@@ -160,14 +160,13 @@ void msg_id(id x) {
160160
// CHECK-CC9: ObjCInstanceMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText Arg2:}{Placeholder (int)i2}
161161
// CHECK-CC9: ObjCInstanceMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText OtherArg:}{Placeholder (id)obj}
162162
// RUN: c-index-test -code-completion-at=%s:61:11 %s | FileCheck -check-prefix=CHECK-CCA %s
163-
// CHECK-CCA: {ResultType SEL}{TypedText _cmd}
164163
// CHECK-CCA: TypedefDecl:{TypedText Class}
165-
// CHECK-CCA: ObjCInterfaceDecl:{TypedText Foo}
166-
// CHECK-CCA: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )}
164+
// CHECK-CCA-NEXT: ObjCInterfaceDecl:{TypedText Foo}
165+
// CHECK-CCA-NOT: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )}
166+
// CHECK-CCA:FunctionDecl:{ResultType MyClass *}{TypedText getMyClass}{LeftParen (}{RightParen )}
167167
// CHECK-CCA: TypedefDecl:{TypedText id}
168168
// CHECK-CCA: ObjCInterfaceDecl:{TypedText MyClass}
169169
// CHECK-CCA: ObjCInterfaceDecl:{TypedText MySubClass}
170-
// CHECK-CCA: TypedefDecl:{TypedText SEL}
171170
// CHECK-CCA: {ResultType Class}{TypedText self}
172171
// CHECK-CCA: {TypedText super}
173172
// RUN: c-index-test -code-completion-at=%s:103:6 %s | FileCheck -check-prefix=CHECK-CCB %s
@@ -188,14 +187,12 @@ void msg_id(id x) {
188187
// CHECK-CCE: ObjCClassMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText Arg2:}{Placeholder (int)i2}
189188
// CHECK-CCE: ObjCClassMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText OtherArg:}{Placeholder (id)obj}
190189
// RUN: c-index-test -code-completion-at=%s:61:11 %s | FileCheck -check-prefix=CHECK-CCF %s
191-
// CHECK-CCF: {ResultType SEL}{TypedText _cmd}
192190
// CHECK-CCF: TypedefDecl:{TypedText Class}
193191
// CHECK-CCF: ObjCInterfaceDecl:{TypedText Foo}
194-
// CHECK-CCF: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )}
192+
// CHECK-CCF-NOT: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )}
195193
// CHECK-CCF: TypedefDecl:{TypedText id}
196194
// CHECK-CCF: ObjCInterfaceDecl:{TypedText MyClass}
197195
// CHECK-CCF: ObjCInterfaceDecl:{TypedText MySubClass}
198-
// CHECK-CCF: TypedefDecl:{TypedText SEL}
199196
// CHECK-CCF: {ResultType Class}{TypedText self}
200197
// CHECK-CCF: {TypedText super}
201198
// RUN: c-index-test -code-completion-at=%s:120:6 %s | FileCheck -check-prefix=CHECK-CCG %s
@@ -230,4 +227,3 @@ void msg_id(id x) {
230227
// CHECK-CCH: ObjCClassMethodDecl:{ResultType id}{TypedText new}
231228
// CHECK-CCH: ObjCClassMethodDecl:{ResultType int}{TypedText OtherMethod:}{Placeholder (float)f}{HorizontalSpace }{Text Arg1:}{Placeholder (int)i1}{HorizontalSpace }{Text Arg2:}{Placeholder (int)i2}
232229
// CHECK-CCH: ObjCClassMethodDecl:{ResultType id}{TypedText protocolClassMethod}
233-

0 commit comments

Comments
 (0)