diff options
-rw-r--r-- | include/clang/Parse/Action.h | 8 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 7 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 98 | ||||
-rw-r--r-- | test/Index/complete-objc-message.m | 14 |
5 files changed, 119 insertions, 10 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 122fe850a1..d5488e8210 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -2853,6 +2853,14 @@ public: unsigned NumMethods) { } + /// \brief Code completion for the receiver in an Objective-C message send. + /// + /// This code completion action is invoked when we see a '[' that indicates + /// the start of an Objective-C message send. + /// + /// \param S The scope in which the Objective-C message send occurs. + virtual void CodeCompleteObjCMessageReceiver(Scope *S) { } + /// \brief Code completion for an ObjC message expression that sends /// a message to the superclass. /// diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 9cfe73456a..b76b677af0 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1807,6 +1807,13 @@ Parser::OwningExprResult Parser::ParseObjCMessageExpression() { assert(Tok.is(tok::l_square) && "'[' expected"); SourceLocation LBracLoc = ConsumeBracket(); // consume '[' + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCMessageReceiver(CurScope); + ConsumeCodeCompletionToken(); + SkipUntil(tok::r_square); + return ExprError(); + } + if (getLang().CPlusPlus) { // We completely separate the C and C++ cases because C++ requires // more complicated (read: slower) parsing. diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index dfc45ace0e..24961c7520 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -4440,7 +4440,7 @@ public: virtual void CodeCompleteObjCPropertySetter(Scope *S, DeclPtrTy ClassDecl, DeclPtrTy *Methods, unsigned NumMethods); - + virtual void CodeCompleteObjCMessageReceiver(Scope *S); virtual void CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc, IdentifierInfo **SelIdents, unsigned NumSelIdents); diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index d8c1a5cd7e..a455e6115f 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -222,6 +222,7 @@ namespace { bool IsType(NamedDecl *ND) const; bool IsMember(NamedDecl *ND) const; bool IsObjCIvar(NamedDecl *ND) const; + bool IsObjCMessageReceiver(NamedDecl *ND) const; //@} }; } @@ -732,6 +733,78 @@ bool ResultBuilder::IsMember(NamedDecl *ND) const { isa<ObjCPropertyDecl>(ND); } +/// \brief Get the type that a given expression will have if this declaration +/// is used as an expression in its "typical" code-completion form. +static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) { + ND = cast<NamedDecl>(ND->getUnderlyingDecl()); + + if (TypeDecl *Type = dyn_cast<TypeDecl>(ND)) + return C.getTypeDeclType(Type); + if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND)) + return C.getObjCInterfaceType(Iface); + + QualType T; + if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) + T = Function->getResultType(); + else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) + T = Method->getResultType(); + else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) + T = FunTmpl->getTemplatedDecl()->getResultType(); + else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND)) + T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext())); + else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) + T = Property->getType(); + else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) + T = Value->getType(); + else + return QualType(); + + return T.getNonReferenceType(); +} + +static bool isObjCReceiverType(ASTContext &C, QualType T) { + T = C.getCanonicalType(T); + switch (T->getTypeClass()) { + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + return true; + + case Type::Builtin: + switch (cast<BuiltinType>(T)->getKind()) { + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + return true; + + default: + break; + } + return false; + + default: + break; + } + + if (!C.getLangOptions().CPlusPlus) + return false; + + // FIXME: We could perform more analysis here to determine whether a + // particular class type has any conversions to Objective-C types. For now, + // just accept all class types. + return T->isDependentType() || T->isRecordType(); +} + +bool ResultBuilder::IsObjCMessageReceiver(NamedDecl *ND) const { + QualType T = getDeclUsageType(SemaRef.Context, ND); + if (T.isNull()) + return false; + + T = SemaRef.Context.getBaseElementType(T); + return isObjCReceiverType(SemaRef.Context, T); +} + + /// \rief Determines whether the given declaration is an Objective-C /// instance variable. bool ResultBuilder::IsObjCIvar(NamedDecl *ND) const { @@ -3021,6 +3094,31 @@ static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) { .Default(0); } +void Sema::CodeCompleteObjCMessageReceiver(Scope *S) { + typedef CodeCompleteConsumer::Result Result; + ResultBuilder Results(*this); + + // Find anything that looks like it could be a message receiver. + Results.setFilter(&ResultBuilder::IsObjCMessageReceiver); + CodeCompletionDeclConsumer Consumer(Results, CurContext); + Results.EnterNewScope(); + LookupVisibleDecls(S, LookupOrdinaryName, Consumer); + + // If we are in an Objective-C method inside a class that has a superclass, + // add "super" as an option. + if (ObjCMethodDecl *Method = getCurMethodDecl()) + if (ObjCInterfaceDecl *Iface = Method->getClassInterface()) + if (Iface->getSuperClass()) + Results.AddResult(Result("super")); + + Results.ExitScope(); + + if (CodeCompleter->includeMacros()) + AddMacroResults(PP, Results); + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); + +} + void Sema::CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc, IdentifierInfo **SelIdents, unsigned NumSelIdents) { diff --git a/test/Index/complete-objc-message.m b/test/Index/complete-objc-message.m index e65a056e36..321d75f3fe 100644 --- a/test/Index/complete-objc-message.m +++ b/test/Index/complete-objc-message.m @@ -51,7 +51,7 @@ void func() { return 3; } @end - +MyClass *getMyClass(); @implementation MySubClass + (int)MySubClassMethod { return 2; @@ -160,14 +160,13 @@ void msg_id(id x) { // CHECK-CC9: ObjCInstanceMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText Arg2:}{Placeholder (int)i2} // CHECK-CC9: ObjCInstanceMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText OtherArg:}{Placeholder (id)obj} // RUN: c-index-test -code-completion-at=%s:61:11 %s | FileCheck -check-prefix=CHECK-CCA %s -// CHECK-CCA: {ResultType SEL}{TypedText _cmd} // CHECK-CCA: TypedefDecl:{TypedText Class} -// CHECK-CCA: ObjCInterfaceDecl:{TypedText Foo} -// CHECK-CCA: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )} +// CHECK-CCA-NEXT: ObjCInterfaceDecl:{TypedText Foo} +// CHECK-CCA-NOT: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )} +// CHECK-CCA:FunctionDecl:{ResultType MyClass *}{TypedText getMyClass}{LeftParen (}{RightParen )} // CHECK-CCA: TypedefDecl:{TypedText id} // CHECK-CCA: ObjCInterfaceDecl:{TypedText MyClass} // CHECK-CCA: ObjCInterfaceDecl:{TypedText MySubClass} -// CHECK-CCA: TypedefDecl:{TypedText SEL} // CHECK-CCA: {ResultType Class}{TypedText self} // CHECK-CCA: {TypedText super} // 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) { // CHECK-CCE: ObjCClassMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText Arg2:}{Placeholder (int)i2} // CHECK-CCE: ObjCClassMethodDecl:{ResultType int}{Informative Method:}{Informative Arg1:}{TypedText OtherArg:}{Placeholder (id)obj} // RUN: c-index-test -code-completion-at=%s:61:11 %s | FileCheck -check-prefix=CHECK-CCF %s -// CHECK-CCF: {ResultType SEL}{TypedText _cmd} // CHECK-CCF: TypedefDecl:{TypedText Class} // CHECK-CCF: ObjCInterfaceDecl:{TypedText Foo} -// CHECK-CCF: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )} +// CHECK-CCF-NOT: FunctionDecl:{ResultType void}{TypedText func}{LeftParen (}{RightParen )} // CHECK-CCF: TypedefDecl:{TypedText id} // CHECK-CCF: ObjCInterfaceDecl:{TypedText MyClass} // CHECK-CCF: ObjCInterfaceDecl:{TypedText MySubClass} -// CHECK-CCF: TypedefDecl:{TypedText SEL} // CHECK-CCF: {ResultType Class}{TypedText self} // CHECK-CCF: {TypedText super} // 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) { // CHECK-CCH: ObjCClassMethodDecl:{ResultType id}{TypedText new} // CHECK-CCH: ObjCClassMethodDecl:{ResultType int}{TypedText OtherMethod:}{Placeholder (float)f}{HorizontalSpace }{Text Arg1:}{Placeholder (int)i1}{HorizontalSpace }{Text Arg2:}{Placeholder (int)i2} // CHECK-CCH: ObjCClassMethodDecl:{ResultType id}{TypedText protocolClassMethod} - |