From: Douglas Gregor Date: Fri, 27 Aug 2010 21:18:54 +0000 (+0000) Subject: Implement the "call super" code completion for C++. If the virtual X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f696152ccc574b4921b2cd1c3d8afbce0d72e510;p=clang Implement the "call super" code completion for C++. If the virtual member function you're typing in overrides another virtual function, this fills in a (qualified!) call to that virtual function to make such delegation easy. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112294 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 57ec3297dc..9a2cfe75c1 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -157,7 +157,7 @@ namespace { /// results. bool includeCodePatterns() const { return SemaRef.CodeCompleter && - SemaRef.CodeCompleter->includeCodePatterns(); + SemaRef.CodeCompleter->includeCodePatterns(); } /// \brief Set the filter used for code-completion results. @@ -2383,10 +2383,84 @@ static enum CodeCompletionContext::Kind mapCodeCompletionContext(Sema &S, return CodeCompletionContext::CCC_Other; } +/// \brief If we're in a C++ virtual member function, add completion results +/// that invoke the functions we override, since it's common to invoke the +/// overridden function as well as adding new functionality. +/// +/// \param S The semantic analysis object for which we are generating results. +/// +/// \param InContext This context in which the nested-name-specifier preceding +/// the code-completion point +static void MaybeAddOverrideCalls(Sema &S, DeclContext *InContext, + ResultBuilder &Results) { + // Look through blocks. + DeclContext *CurContext = S.CurContext; + while (isa(CurContext)) + CurContext = CurContext->getParent(); + + + CXXMethodDecl *Method = dyn_cast(CurContext); + if (!Method || !Method->isVirtual()) + return; + + // We need to have names for all of the parameters, if we're going to + // generate a forwarding call. + for (CXXMethodDecl::param_iterator P = Method->param_begin(), + PEnd = Method->param_end(); + P != PEnd; + ++P) { + if (!(*P)->getDeclName()) + return; + } + + for (CXXMethodDecl::method_iterator M = Method->begin_overridden_methods(), + MEnd = Method->end_overridden_methods(); + M != MEnd; ++M) { + CodeCompletionString *Pattern = new CodeCompletionString; + CXXMethodDecl *Overridden = const_cast(*M); + if (Overridden->getCanonicalDecl() == Method->getCanonicalDecl()) + continue; + + // If we need a nested-name-specifier, add one now. + if (!InContext) { + NestedNameSpecifier *NNS + = getRequiredQualification(S.Context, CurContext, + Overridden->getDeclContext()); + if (NNS) { + std::string Str; + llvm::raw_string_ostream OS(Str); + NNS->print(OS, S.Context.PrintingPolicy); + Pattern->AddTextChunk(OS.str()); + } + } else if (!InContext->Equals(Overridden->getDeclContext())) + continue; + + Pattern->AddTypedTextChunk(Overridden->getNameAsString()); + Pattern->AddChunk(CodeCompletionString::CK_LeftParen); + bool FirstParam = true; + for (CXXMethodDecl::param_iterator P = Method->param_begin(), + PEnd = Method->param_end(); + P != PEnd; ++P) { + if (FirstParam) + FirstParam = false; + else + Pattern->AddChunk(CodeCompletionString::CK_Comma); + + Pattern->AddPlaceholderChunk((*P)->getIdentifier()->getName()); + } + Pattern->AddChunk(CodeCompletionString::CK_RightParen); + Results.AddResult(CodeCompletionResult(Pattern, + CCP_SuperCompletion, + CXCursor_CXXMethod)); + Results.Ignore(Overridden); + } +} + void Sema::CodeCompleteOrdinaryName(Scope *S, ParserCompletionContext CompletionContext) { typedef CodeCompletionResult Result; ResultBuilder Results(*this); + Results.EnterNewScope(); // Determine how to filter results, e.g., so that the names of // values (functions, enumerators, function templates, etc.) are @@ -2417,6 +2491,9 @@ void Sema::CodeCompleteOrdinaryName(Scope *S, Results.setFilter(&ResultBuilder::IsOrdinaryName); else Results.setFilter(&ResultBuilder::IsOrdinaryNonTypeName); + + if (getLangOptions().CPlusPlus) + MaybeAddOverrideCalls(*this, /*InContext=*/0, Results); break; case PCC_RecoveryInFunction: @@ -2435,7 +2512,6 @@ void Sema::CodeCompleteOrdinaryName(Scope *S, LookupVisibleDecls(S, LookupOrdinaryName, Consumer, CodeCompleter->includeGlobals()); - Results.EnterNewScope(); AddOrdinaryNameResults(CompletionContext, S, *this, Results); Results.ExitScope(); @@ -3047,17 +3123,28 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS, return; ResultBuilder Results(*this); - CodeCompletionDeclConsumer Consumer(Results, CurContext); - LookupVisibleDecls(Ctx, LookupOrdinaryName, Consumer); + Results.EnterNewScope(); // The "template" keyword can follow "::" in the grammar, but only // put it into the grammar if the nested-name-specifier is dependent. NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep(); if (!Results.empty() && NNS->isDependent()) Results.AddResult("template"); + + // Add calls to overridden virtual functions, if there are any. + // + // FIXME: This isn't wonderful, because we don't know whether we're actually + // in a context that permits expressions. This is a general issue with + // qualified-id completions. + if (!EnteringContext) + MaybeAddOverrideCalls(*this, Ctx, Results); + Results.ExitScope(); + CodeCompletionDeclConsumer Consumer(Results, CurContext); + LookupVisibleDecls(Ctx, LookupOrdinaryName, Consumer); + HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_Name, Results.data(),Results.size()); } diff --git a/test/Index/complete-super.cpp b/test/Index/complete-super.cpp new file mode 100644 index 0000000000..a10cafc2c7 --- /dev/null +++ b/test/Index/complete-super.cpp @@ -0,0 +1,33 @@ +// The run lines are below, because this test is line- and +// column-number sensitive. + +struct A { + virtual void foo(int x, int y); + virtual void bar(double x); + virtual void bar(float x); +}; + +struct B : A { + void foo(int a, int b); + void bar(float real); +}; + +void B::foo(int a, int b) { + A::foo(a, b); +} + +void B::bar(float real) { + A::bar(real); +} + +// RUN: c-index-test -code-completion-at=%s:16:3 %s | FileCheck -check-prefix=CHECK-FOO-UNQUAL %s +// CHECK-FOO-UNQUAL: CXXMethod:{Text A::}{TypedText foo}{LeftParen (}{Placeholder a}{Comma , }{Placeholder b}{RightParen )} (8) + +// RUN: c-index-test -code-completion-at=%s:20:3 %s | FileCheck -check-prefix=CHECK-BAR-UNQUAL %s +// CHECK-BAR-UNQUAL: CXXMethod:{Text A::}{TypedText bar}{LeftParen (}{Placeholder real}{RightParen )} (8) +// CHECK-BAR-UNQUAL: FunctionDecl:{ResultType void}{TypedText bar}{LeftParen (}{Placeholder float real}{RightParen )} (14) +// CHECK-BAR-UNQUAL: FunctionDecl:{ResultType void}{Text A::}{TypedText bar}{LeftParen (}{Placeholder double x}{RightParen )} (16) + +// RUN: c-index-test -code-completion-at=%s:16:6 %s | FileCheck -check-prefix=CHECK-FOO-QUAL %s +// CHECK-FOO-QUAL: CXXMethod:{TypedText foo}{LeftParen (}{Placeholder a}{Comma , }{Placeholder b}{RightParen )} (8) +