From: Douglas Gregor Date: Fri, 27 Aug 2010 15:10:57 +0000 (+0000) Subject: Add a super-cool code completion for send-to-super. When we're typing X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=03d8aec611380d30ddb0659bb61be9289c3871b3;p=clang Add a super-cool code completion for send-to-super. When we're typing a message send to "super" from a method that appears to be meant to override a superclass method (same kind, same selector, same argument types), provide a "super" completion that fills in the selector along with forwarding the method's arguments (as placeholders). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112263 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 27cda88cb2..46650baf2f 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -30,6 +30,8 @@ namespace clang { /// \brief Default priority values for code-completion results based /// on their kind. enum { + /// \brief Priority for a send-to-super completion. + CCP_SuperCompletion = 8, /// \brief Priority for a declaration that is in the local scope. CCP_LocalDeclaration = 8, /// \brief Priority for a member declaration found from the current diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b1eb2b2adf..43f99ae284 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4367,9 +4367,17 @@ public: virtual void CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, IdentifierInfo **SelIdents, unsigned NumSelIdents); - virtual void CodeCompleteObjCInstanceMessage(Scope *S, Expr *Receiver, + void CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper); + virtual void CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, IdentifierInfo **SelIdents, unsigned NumSelIdents); + void CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper); virtual void CodeCompleteObjCForCollection(Scope *S, DeclGroupPtrTy IterationVar); virtual void CodeCompleteObjCSelector(Scope *S, diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index aade62710c..8e1e6914c6 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3761,6 +3761,110 @@ static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) { .Default(0); } +// Add a special completion for a message send to "super", which fills in the +// most likely case of forwarding all of our arguments to the superclass +// function. +/// +/// \param S The semantic analysis object. +/// +/// \param S NeedSuperKeyword Whether we need to prefix this completion with +/// the "super" keyword. Otherwise, we just need to provide the arguments. +/// +/// \param SelIdents The identifiers in the selector that have already been +/// provided as arguments for a send to "super". +/// +/// \param NumSelIdents The number of identifiers in \p SelIdents. +/// +/// \param Results The set of results to augment. +/// +/// \returns the Objective-C method declaration that would be invoked by +/// this "super" completion. If NULL, no completion was added. +static ObjCMethodDecl *AddSuperSendCompletion(Sema &S, bool NeedSuperKeyword, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + ResultBuilder &Results) { + ObjCMethodDecl *CurMethod = S.getCurMethodDecl(); + if (!CurMethod) + return 0; + + ObjCInterfaceDecl *Class = CurMethod->getClassInterface(); + if (!Class) + return 0; + + // Try to find a superclass method with the same selector. + ObjCMethodDecl *SuperMethod = 0; + while ((Class = Class->getSuperClass()) && !SuperMethod) + SuperMethod = Class->getMethod(CurMethod->getSelector(), + CurMethod->isInstanceMethod()); + + if (!SuperMethod) + return 0; + + // Check whether the superclass method has the same signature. + if (CurMethod->param_size() != SuperMethod->param_size() || + CurMethod->isVariadic() != SuperMethod->isVariadic()) + return 0; + + for (ObjCMethodDecl::param_iterator CurP = CurMethod->param_begin(), + CurPEnd = CurMethod->param_end(), + SuperP = SuperMethod->param_begin(); + CurP != CurPEnd; ++CurP, ++SuperP) { + // Make sure the parameter types are compatible. + if (!S.Context.hasSameUnqualifiedType((*CurP)->getType(), + (*SuperP)->getType())) + return 0; + + // Make sure we have a parameter name to forward! + if (!(*CurP)->getIdentifier()) + return 0; + } + + // We have a superclass method. Now, form the send-to-super completion. + CodeCompletionString *Pattern = new CodeCompletionString; + + // Give this completion a return type. + AddResultTypeChunk(S.Context, SuperMethod, Pattern); + + // If we need the "super" keyword, add it (plus some spacing). + if (NeedSuperKeyword) { + Pattern->AddTypedTextChunk("super"); + Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace); + } + + Selector Sel = CurMethod->getSelector(); + if (Sel.isUnarySelector()) { + if (NeedSuperKeyword) + Pattern->AddTextChunk(Sel.getIdentifierInfoForSlot(0)->getName()); + else + Pattern->AddTypedTextChunk(Sel.getIdentifierInfoForSlot(0)->getName()); + } else { + ObjCMethodDecl::param_iterator CurP = CurMethod->param_begin(); + for (unsigned I = 0, N = Sel.getNumArgs(); I != N; ++I, ++CurP) { + if (I > NumSelIdents) + Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace); + + if (I < NumSelIdents) + Pattern->AddInformativeChunk( + Sel.getIdentifierInfoForSlot(I)->getName().str() + ":"); + else if (NeedSuperKeyword || I > NumSelIdents) { + Pattern->AddTextChunk( + Sel.getIdentifierInfoForSlot(I)->getName().str() + ":"); + Pattern->AddPlaceholderChunk((*CurP)->getIdentifier()->getName()); + } else { + Pattern->AddTypedTextChunk( + Sel.getIdentifierInfoForSlot(I)->getName().str() + ":"); + Pattern->AddPlaceholderChunk((*CurP)->getIdentifier()->getName()); + } + } + } + + Results.AddResult(CodeCompletionResult(Pattern, CCP_SuperCompletion, + SuperMethod->isInstanceMethod() + ? CXCursor_ObjCInstanceMethodDecl + : CXCursor_ObjCClassMethodDecl)); + return SuperMethod; +} + void Sema::CodeCompleteObjCMessageReceiver(Scope *S) { typedef CodeCompletionResult Result; ResultBuilder Results(*this); @@ -3776,8 +3880,11 @@ void Sema::CodeCompleteObjCMessageReceiver(Scope *S) { // add "super" as an option. if (ObjCMethodDecl *Method = getCurMethodDecl()) if (ObjCInterfaceDecl *Iface = Method->getClassInterface()) - if (Iface->getSuperClass()) + if (Iface->getSuperClass()) { Results.AddResult(Result("super")); + + AddSuperSendCompletion(*this, /*NeedSuperKeyword=*/true, 0, 0, Results); + } Results.ExitScope(); @@ -3814,7 +3921,8 @@ void Sema::CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc, ExprResult Super = Owned(new (Context) ObjCSuperExpr(SuperLoc, SuperTy)); return CodeCompleteObjCInstanceMessage(S, (Expr *)Super.get(), - SelIdents, NumSelIdents); + SelIdents, NumSelIdents, + /*IsSuper=*/true); } // Fall through to send to the superclass in CDecl. @@ -3849,12 +3957,19 @@ void Sema::CodeCompleteObjCSuperMessage(Scope *S, SourceLocation SuperLoc, if (CDecl) Receiver = ParsedType::make(Context.getObjCInterfaceType(CDecl)); return CodeCompleteObjCClassMessage(S, Receiver, SelIdents, - NumSelIdents); + NumSelIdents, /*IsSuper=*/true); } void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, IdentifierInfo **SelIdents, unsigned NumSelIdents) { + CodeCompleteObjCClassMessage(S, Receiver, SelIdents, NumSelIdents, false); +} + +void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper) { typedef CodeCompletionResult Result; ObjCInterfaceDecl *CDecl = 0; @@ -3872,6 +3987,15 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, 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)) + Results.Ignore(SuperMethod); + } + if (CDecl) AddObjCMethods(CDecl, false, MK_Any, SelIdents, NumSelIdents, CurContext, Results); @@ -3912,12 +4036,19 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, CodeCompletionContext::CCC_Other, - Results.data(),Results.size()); + Results.data(), Results.size()); } void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, IdentifierInfo **SelIdents, unsigned NumSelIdents) { + CodeCompleteObjCInstanceMessage(S, Receiver, SelIdents, NumSelIdents, false); +} + +void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, + IdentifierInfo **SelIdents, + unsigned NumSelIdents, + bool IsSuper) { typedef CodeCompletionResult Result; Expr *RecExpr = static_cast(Receiver); @@ -3931,6 +4062,15 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, 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)) + Results.Ignore(SuperMethod); + } + // 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. diff --git a/test/Index/complete-super.m b/test/Index/complete-super.m new file mode 100644 index 0000000000..fc60c6c42d --- /dev/null +++ b/test/Index/complete-super.m @@ -0,0 +1,55 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test. + +typedef int Bool; + +@interface A +- (void)add:(int)x to:(int)y; ++ (void)select:(Bool)condition first:(int)x second:(int)y; +- (void)last; ++ (void)last; +@end + +@interface B : A +- (void)add:(int)x to:(int)y; ++ (void)select:(Bool)condition first:(int)x second:(int)y; +@end + +@implementation B +- (void)add:(int)a to:(int)b { + [super add:a to:b]; +} + ++ (void)select:(Bool)condition first:(int)a second:(int)b { + [super selector:condition first:a second:b]; +} +@end + +// Check "super" completion as a message receiver. +// RUN: c-index-test -code-completion-at=%s:20:4 %s | FileCheck -check-prefix=CHECK-ADD-RECEIVER %s +// CHECK-ADD-RECEIVER: ObjCInstanceMethodDecl:{ResultType void}{TypedText super}{HorizontalSpace }{Text add:}{Placeholder a}{HorizontalSpace }{Text to:}{Placeholder b} (8) + +// RUN: c-index-test -code-completion-at=%s:24:4 %s | FileCheck -check-prefix=CHECK-SELECT-RECEIVER %s +// CHECK-SELECT-RECEIVER: ObjCClassMethodDecl:{ResultType void}{TypedText super}{HorizontalSpace }{Text select:}{Placeholder condition}{HorizontalSpace }{Text first:}{Placeholder a}{HorizontalSpace }{Text second:}{Placeholder b} (8) + +// Check "super" completion at the first identifier +// RUN: c-index-test -code-completion-at=%s:20:10 %s | FileCheck -check-prefix=CHECK-ADD-ADD %s +// CHECK-ADD-ADD: ObjCInstanceMethodDecl:{ResultType void}{TypedText add:}{Placeholder a}{HorizontalSpace }{Text to:}{Placeholder b} (8) +// CHECK-ADD-ADD-NOT: add +// CHECK-ADD-ADD: ObjCInstanceMethodDecl:{ResultType void}{TypedText last} (20) + +// RUN: c-index-test -code-completion-at=%s:24:10 %s | FileCheck -check-prefix=CHECK-SELECTOR-SELECTOR %s +// CHECK-SELECTOR-SELECTOR-NOT: x +// CHECK-SELECTOR-SELECTOR: ObjCClassMethodDecl:{ResultType void}{TypedText last} (20) +// CHECK-SELECTOR-SELECTOR: ObjCClassMethodDecl:{ResultType void}{TypedText select:}{Placeholder condition}{HorizontalSpace }{Text first:}{Placeholder a}{HorizontalSpace }{Text second:}{Placeholder b} (8) + +// Check "super" completion at the second identifier +// RUN: c-index-test -code-completion-at=%s:20:16 %s | FileCheck -check-prefix=CHECK-ADD-TO %s +// CHECK-ADD-TO: ObjCInstanceMethodDecl:{ResultType void}{Informative add:}{TypedText to:}{Placeholder b} (8) + +// RUN: c-index-test -code-completion-at=%s:24:28 %s | FileCheck -check-prefix=CHECK-SELECTOR-FIRST %s +// CHECK-SELECTOR-FIRST: ObjCClassMethodDecl:{ResultType void}{Informative select:}{TypedText first:}{Placeholder a}{HorizontalSpace }{Text second:}{Placeholder b} (8) + +// Check "super" completion at the third identifier +// RUN: c-index-test -code-completion-at=%s:24:37 %s | FileCheck -check-prefix=CHECK-SELECTOR-SECOND %s +// CHECK-SELECTOR-SECOND: ObjCClassMethodDecl:{ResultType void}{Informative select:}{Informative first:}{TypedText second:}{Placeholder b} (8)