From: Douglas Gregor Date: Thu, 16 Sep 2010 15:14:18 +0000 (+0000) Subject: Implement code completion for Objective-C class message sends that are X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c7b6d883360092808b0ae81b7829fa8196ef42a1;p=clang Implement code completion for Objective-C class message sends that are missing the opening bracket '[', e.g., NSArray at function scope. Previously, we would only give trivial completions (const, volatile, etc.), because we're in a "declaration name" scope. Now, we also provide completions for class methods of NSArray, e.g., alloc Note that we already had support for this after the first argument, e.g., NSArray method:x would get code completion for class methods of NSArray whose selector starts with "method:". This was already present because we recover as if NSArray method:x were a class message send missing the opening bracket (which was committed in r114057). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@114078 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8b8f2d82c0..4164f0e15a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4303,9 +4303,9 @@ public: void CodeCompleteOrdinaryName(Scope *S, ParserCompletionContext CompletionContext); - void CodeCompleteDeclarator(Scope *S, - bool AllowNonIdentifiers, - bool AllowNestedNameSpecifiers); + void CodeCompleteDeclSpec(Scope *S, DeclSpec &DS, + bool AllowNonIdentifiers, + bool AllowNestedNameSpecifiers); struct CodeCompleteExpressionData; void CodeCompleteExpression(Scope *S, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ef495e33d8..bef9f9585d 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -905,8 +905,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, = DSContext == DSC_top_level || (DSContext == DSC_class && DS.isFriendSpecified()); - Actions.CodeCompleteDeclarator(getCurScope(), AllowNonIdentifiers, - AllowNestedNameSpecifiers); + Actions.CodeCompleteDeclSpec(getCurScope(), DS, + AllowNonIdentifiers, + AllowNestedNameSpecifiers); ConsumeCodeCompletionToken(); return; } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index fd6b5518d7..3ec68029b2 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -2626,9 +2626,16 @@ void Sema::CodeCompleteOrdinaryName(Scope *S, Results.data(),Results.size()); } -void Sema::CodeCompleteDeclarator(Scope *S, - bool AllowNonIdentifiers, - bool AllowNestedNameSpecifiers) { +static void AddClassMessageCompletions(Sema &SemaRef, Scope *S, + ParsedType Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper, + ResultBuilder &Results); + +void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS, + bool AllowNonIdentifiers, + bool AllowNestedNameSpecifiers) { typedef CodeCompletionResult Result; ResultBuilder Results(*this); Results.EnterNewScope(); @@ -2654,6 +2661,27 @@ void Sema::CodeCompleteDeclarator(Scope *S, } Results.ExitScope(); + // If we're in a context where we might have an expression (rather than a + // declaration), and what we've seen so far is an Objective-C type that could + // be a receiver of a class message, this may be a class message send with + // the initial opening bracket '[' missing. Add appropriate completions. + if (AllowNonIdentifiers && !AllowNestedNameSpecifiers && + DS.getTypeSpecType() == DeclSpec::TST_typename && + DS.getStorageClassSpecAsWritten() == DeclSpec::SCS_unspecified && + !DS.isThreadSpecified() && !DS.isExternInLinkageSpec() && + DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified && + DS.getTypeSpecSign() == DeclSpec::TSS_unspecified && + DS.getTypeQualifiers() == 0 && + S && + (S->getFlags() & Scope::DeclScope) != 0 && + (S->getFlags() & (Scope::ClassScope | Scope::TemplateParamScope | + Scope::FunctionPrototypeScope | + Scope::AtCatchScope)) == 0) { + ParsedType T = DS.getRepAsType(); + if (!T.get().isNull() && T.get()->isObjCObjectOrInterfaceType()) + AddClassMessageCompletions(*this, S, T, 0, 0, false, Results); + } + // Note that we intentionally suppress macro results here, since we do not // encourage using macros to produce the names of entities. @@ -4282,62 +4310,64 @@ void Sema::CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc, NumSelIdents, /*IsSuper=*/true); } -void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, - IdentifierInfo **SelIdents, - unsigned NumSelIdents, - bool IsSuper) { +static void AddClassMessageCompletions(Sema &SemaRef, Scope *S, + ParsedType Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper, + ResultBuilder &Results) { typedef CodeCompletionResult Result; ObjCInterfaceDecl *CDecl = 0; - + // If the given name refers to an interface type, retrieve the // corresponding declaration. if (Receiver) { - QualType T = GetTypeFromParser(Receiver, 0); + QualType T = SemaRef.GetTypeFromParser(Receiver, 0); if (!T.isNull()) if (const ObjCObjectType *Interface = T->getAs()) CDecl = Interface->getInterface(); } - + // Add all of the factory methods in this Objective-C class, its protocols, // superclasses, categories, implementation, etc. - ResultBuilder Results(*this); Results.EnterNewScope(); - + // If this is a send-to-super, try to add the special "super" send // completion. if (IsSuper) { if (ObjCMethodDecl *SuperMethod - = AddSuperSendCompletion(*this, false, SelIdents, NumSelIdents, - Results)) + = AddSuperSendCompletion(SemaRef, false, SelIdents, NumSelIdents, + Results)) Results.Ignore(SuperMethod); } - + // If we're inside an Objective-C method definition, prefer its selector to // others. - if (ObjCMethodDecl *CurMethod = getCurMethodDecl()) + if (ObjCMethodDecl *CurMethod = SemaRef.getCurMethodDecl()) Results.setPreferredSelector(CurMethod->getSelector()); - + if (CDecl) - AddObjCMethods(CDecl, false, MK_Any, SelIdents, NumSelIdents, CurContext, - Results); + AddObjCMethods(CDecl, false, MK_Any, SelIdents, NumSelIdents, + SemaRef.CurContext, Results); else { // We're messaging "id" as a type; provide all class/factory methods. - + // If we have an external source, load the entire class method // pool from the AST file. - if (ExternalSource) { - for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors(); + if (SemaRef.ExternalSource) { + for (uint32_t I = 0, + N = SemaRef.ExternalSource->GetNumExternalSelectors(); I != N; ++I) { - Selector Sel = ExternalSource->GetExternalSelector(I); - if (Sel.isNull() || MethodPool.count(Sel)) + Selector Sel = SemaRef.ExternalSource->GetExternalSelector(I); + if (Sel.isNull() || SemaRef.MethodPool.count(Sel)) continue; - - ReadMethodPool(Sel); + + SemaRef.ReadMethodPool(Sel); } } - - for (GlobalMethodPool::iterator M = MethodPool.begin(), - MEnd = MethodPool.end(); + + for (Sema::GlobalMethodPool::iterator M = SemaRef.MethodPool.begin(), + MEnd = SemaRef.MethodPool.end(); M != MEnd; ++M) { for (ObjCMethodList *MethList = &M->second.second; MethList && MethList->Method; @@ -4345,16 +4375,25 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents, NumSelIdents)) continue; - + Result R(MethList->Method, 0); R.StartParameter = NumSelIdents; R.AllParametersAreInformative = false; - Results.MaybeAddResult(R, CurContext); + Results.MaybeAddResult(R, SemaRef.CurContext); } } } + + Results.ExitScope(); +} - Results.ExitScope(); +void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper) { + ResultBuilder Results(*this); + AddClassMessageCompletions(*this, S, Receiver, SelIdents, NumSelIdents, IsSuper, + Results); HandleCodeCompleteResults(this, CodeCompleter, CodeCompletionContext::CCC_Other, Results.data(), Results.size()); diff --git a/test/Index/complete-objc-message.m b/test/Index/complete-objc-message.m index 02d7f2140c..6a96b95223 100644 --- a/test/Index/complete-objc-message.m +++ b/test/Index/complete-objc-message.m @@ -137,6 +137,8 @@ void test_ranking(B *b) { void test_overload3(Overload *ovl) { ovl Method:1 Arg1:1 OtherArg:ovl]; + Overload2 Method:1 Arg1:1 OtherArg:ovl]; + (Overload2 Method:1 Arg1:1 OtherArg:ovl]); } // RUN: c-index-test -code-completion-at=%s:23:19 %s | FileCheck -check-prefix=CHECK-CC1 %s @@ -245,3 +247,10 @@ void test_overload3(Overload *ovl) { // RUN: c-index-test -code-completion-at=%s:139:7 %s | FileCheck -check-prefix=CHECK-CC7 %s // RUN: c-index-test -code-completion-at=%s:139:16 %s | FileCheck -check-prefix=CHECK-CC8 %s // RUN: c-index-test -code-completion-at=%s:139:23 %s | FileCheck -check-prefix=CHECK-CC9 %s + +// RUN: c-index-test -code-completion-at=%s:140:14 %s | FileCheck -check-prefix=CHECK-CCC %s +// RUN: c-index-test -code-completion-at=%s:140:23 %s | FileCheck -check-prefix=CHECK-CCD %s +// RUN: c-index-test -code-completion-at=%s:140:30 %s | FileCheck -check-prefix=CHECK-CCE %s +// RUN: c-index-test -code-completion-at=%s:141:14 %s | FileCheck -check-prefix=CHECK-CCC %s +// RUN: c-index-test -code-completion-at=%s:141:23 %s | FileCheck -check-prefix=CHECK-CCD %s +// RUN: c-index-test -code-completion-at=%s:141:30 %s | FileCheck -check-prefix=CHECK-CCE %s