From 0563c2677cfc20c9450342ddc53aa96971671c71 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 22 Sep 2009 23:15:58 +0000 Subject: [PATCH] Tweak the code-completion results ranking and formation, so that members found in base classes have the same ranking as members found in derived classes. However, we will introduce an informative note for members found in base classes, showing (as a nested-name-specifier) the qualification to name the base class, to make it clear which members are from bases. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82586 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/CodeCompleteConsumer.h | 47 ++++++++++++----- lib/Sema/CodeCompleteConsumer.cpp | 44 ++++++++++------ lib/Sema/SemaCodeComplete.cpp | 63 ++++++++++++++--------- test/CodeCompletion/member-access.cpp | 18 +++---- test/CodeCompletion/namespace-alias.cpp | 4 +- test/CodeCompletion/operator.cpp | 2 +- test/CodeCompletion/tag.cpp | 10 ++-- test/CodeCompletion/using-namespace.cpp | 4 +- test/CodeCompletion/using.cpp | 6 +-- 9 files changed, 124 insertions(+), 74 deletions(-) diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 68e07e9608..6a10b28be9 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -48,7 +48,10 @@ public: CK_Optional, /// \brief A string that acts as a placeholder for, e.g., a function /// call argument. - CK_Placeholder + CK_Placeholder, + /// \brief A piece of text that describes something about the result but + /// should not be inserted into the buffer. + CK_Informative }; /// \brief One piece of the code completion string. @@ -58,7 +61,8 @@ public: ChunkKind Kind; union { - /// \brief The text string associated with a CK_Text chunk. + /// \brief The text string associated with a CK_Text, CK_Placeholder, + /// or CK_Informative chunk. /// The string is owned by the chunk and will be deallocated /// (with delete[]) when the chunk is destroyed. const char *Text; @@ -67,13 +71,14 @@ public: /// The optional code completion string is owned by the chunk, and will /// be deallocated (with delete) when the chunk is destroyed. CodeCompletionString *Optional; - - /// \brief Placeholder text associated with a CK_Placeholder chunk. - /// The string is owned by the chunk and will be deallocated (with - /// delete[]) when the chunk is destroyed. - const char *Placeholder; }; + Chunk() : Kind(CK_Text), Text(0) { } + + private: + Chunk(ChunkKind Kind, const char *Text); + + public: /// \brief Create a new text chunk. static Chunk CreateText(const char *Text); @@ -82,7 +87,10 @@ public: /// \brief Create a new placeholder chunk. static Chunk CreatePlaceholder(const char *Placeholder); - + + /// \brief Create a new informative chunk. + static Chunk CreateInformative(const char *Informative); + /// \brief Destroy this chunk, deallocating any memory it owns. void Destroy(); }; @@ -118,6 +126,12 @@ public: void AddPlaceholderChunk(const char *Placeholder) { Chunks.push_back(Chunk::CreatePlaceholder(Placeholder)); } + + /// \brief Add a new informative chunk. + /// The text will be copied. + void AddInformativeChunk(const char *Text) { + Chunks.push_back(Chunk::CreateInformative(Text)); + } /// \brief Retrieve a string representation of the code completion string, /// which is mainly useful for debugging. @@ -156,19 +170,26 @@ public: /// \brief Whether this result is hidden by another name. bool Hidden : 1; - /// \brief If the result requires a nested-name-specifier for name lookup - /// to function properly, this is the nested-name-specifier. + /// \brief Whether this result was found via lookup into a base class. + bool QualifierIsInformative : 1; + + /// \brief If the result should have a nested-name-specifier, this is it. + /// When \c QualifierIsInformative, the nested-name-specifier is + /// informative rather than required. NestedNameSpecifier *Qualifier; /// \brief Build a result that refers to a declaration. Result(NamedDecl *Declaration, unsigned Rank, - NestedNameSpecifier *Qualifier = 0) + NestedNameSpecifier *Qualifier = 0, + bool QualifierIsInformative = false) : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), - Hidden(false), Qualifier(Qualifier) { } + Hidden(false), QualifierIsInformative(QualifierIsInformative), + Qualifier(Qualifier) { } /// \brief Build a result that refers to a keyword or symbol. Result(const char *Keyword, unsigned Rank) - : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false) { } + : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false), + QualifierIsInformative(0), Qualifier(0) { } /// \brief Retrieve the declaration stored in this result. NamedDecl *getDeclaration() const { diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index 1e505090fb..f490a2b523 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -26,14 +26,19 @@ using namespace clang; //===----------------------------------------------------------------------===// // Code completion string implementation //===----------------------------------------------------------------------===// -CodeCompletionString::Chunk -CodeCompletionString::Chunk::CreateText(const char *Text) { - Chunk Result; - Result.Kind = CK_Text; +CodeCompletionString::Chunk::Chunk(ChunkKind Kind, const char *Text) + : Kind(Kind), Text(0) +{ + assert((Kind == CK_Text || Kind == CK_Placeholder || Kind == CK_Informative) + && "Invalid text chunk kind"); char *New = new char [std::strlen(Text) + 1]; std::strcpy(New, Text); - Result.Text = New; - return Result; + this->Text = New; +} + +CodeCompletionString::Chunk +CodeCompletionString::Chunk::CreateText(const char *Text) { + return Chunk(CK_Text, Text); } CodeCompletionString::Chunk @@ -47,20 +52,26 @@ CodeCompletionString::Chunk::CreateOptional( CodeCompletionString::Chunk CodeCompletionString::Chunk::CreatePlaceholder(const char *Placeholder) { - Chunk Result; - Result.Kind = CK_Placeholder; - char *New = new char [std::strlen(Placeholder) + 1]; - std::strcpy(New, Placeholder); - Result.Placeholder = New; - return Result; + return Chunk(CK_Placeholder, Placeholder); +} + +CodeCompletionString::Chunk +CodeCompletionString::Chunk::CreateInformative(const char *Informative) { + return Chunk(CK_Informative, Informative); } void CodeCompletionString::Chunk::Destroy() { switch (Kind) { - case CK_Text: delete [] Text; break; - case CK_Optional: delete Optional; break; - case CK_Placeholder: delete [] Placeholder; break; + case CK_Optional: + delete Optional; + break; + + case CK_Text: + case CK_Placeholder: + case CK_Informative: + delete [] Text; + break; } } @@ -77,7 +88,8 @@ std::string CodeCompletionString::getAsString() const { switch (C->Kind) { case CK_Text: OS << C->Text; break; case CK_Optional: OS << "{#" << C->Optional->getAsString() << "#}"; break; - case CK_Placeholder: OS << "<#" << C->Placeholder << "#>"; break; + case CK_Placeholder: OS << "<#" << C->Text << "#>"; break; + case CK_Informative: OS << "[#" << C->Text << "#]"; break; } } diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index b19fda216d..f57480e13e 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -197,8 +197,8 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { // Look through using declarations. if (UsingDecl *Using = dyn_cast(R.Declaration)) - return MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier), - CurContext); + MaybeAddResult(Result(Using->getTargetDecl(), R.Rank, R.Qualifier), + CurContext); // Handle each declaration in an overload set separately. if (OverloadedFunctionDecl *Ovl @@ -281,6 +281,7 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { I->second.first)) { // Note that this result was hidden. R.Hidden = true; + R.QualifierIsInformative = false; if (!R.Qualifier) R.Qualifier = getRequiredQualification(SemaRef.Context, @@ -300,6 +301,18 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { if (!AllDeclsFound.insert(CanonDecl)) return; + // If this result is supposed to have an informative qualifier, add one. + if (R.QualifierIsInformative && !R.Qualifier) { + DeclContext *Ctx = R.Declaration->getDeclContext(); + if (NamespaceDecl *Namespace = dyn_cast(Ctx)) + R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, Namespace); + else if (TagDecl *Tag = dyn_cast(Ctx)) + R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, 0, false, + SemaRef.Context.getTypeDeclType(Tag).getTypePtr()); + else + R.QualifierIsInformative = false; + } + // Insert this result into the set of results and into the current shadow // map. SMap.insert(std::make_pair(R.Declaration->getDeclName(), @@ -398,9 +411,7 @@ static DeclContext *findOuterContext(Scope *S) { /// /// \param Ctx the declaration context from which we will gather results. /// -/// \param InitialRank the initial rank given to results in this declaration -/// context. Larger rank values will be used for, e.g., members found in -/// base classes. +/// \param Rank the rank given to results in this declaration context. /// /// \param Visited the set of declaration contexts that have already been /// visited. Declaration contexts will only be visited once. @@ -408,18 +419,22 @@ static DeclContext *findOuterContext(Scope *S) { /// \param Results the result set that will be extended with any results /// found within this declaration context (and, for a C++ class, its bases). /// +/// \param InBaseClass whether we are in a base class. +/// /// \returns the next higher rank value, after considering all of the /// names within this declaration context. static unsigned CollectMemberLookupResults(DeclContext *Ctx, - unsigned InitialRank, + unsigned Rank, DeclContext *CurContext, llvm::SmallPtrSet &Visited, - ResultBuilder &Results) { + ResultBuilder &Results, + bool InBaseClass = false) { // Make sure we don't visit the same context twice. if (!Visited.insert(Ctx->getPrimaryContext())) - return InitialRank; + return Rank; // Enumerate all of the results in this context. + typedef CodeCompleteConsumer::Result Result; Results.EnterNewScope(); for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx; CurCtx = CurCtx->getNextContext()) { @@ -427,13 +442,11 @@ static unsigned CollectMemberLookupResults(DeclContext *Ctx, DEnd = CurCtx->decls_end(); D != DEnd; ++D) { if (NamedDecl *ND = dyn_cast(*D)) - Results.MaybeAddResult(CodeCompleteConsumer::Result(ND, InitialRank), - CurContext); + Results.MaybeAddResult(Result(ND, Rank, 0, InBaseClass), CurContext); } } // Traverse the contexts of inherited classes. - unsigned NextRank = InitialRank; if (CXXRecordDecl *Record = dyn_cast(Ctx)) { for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(), BEnd = Record->bases_end(); @@ -468,19 +481,15 @@ static unsigned CollectMemberLookupResults(DeclContext *Ctx, // c->A::member // Collect results from this base class (and its bases). - NextRank = std::max(NextRank, - CollectMemberLookupResults(Record->getDecl(), - InitialRank + 1, - CurContext, - Visited, - Results)); + CollectMemberLookupResults(Record->getDecl(), Rank, CurContext, Visited, + Results, /*InBaseClass=*/true); } } // FIXME: Look into base classes in Objective-C! Results.ExitScope(); - return NextRank; + return Rank + 1; } /// \brief Collect the results of searching for members within the given @@ -735,6 +744,7 @@ static void AddTemplateParameterChunks(ASTContext &Context, /// provided nested-name-specifier is non-NULL. void AddQualifierToCompletionString(CodeCompletionString *Result, NestedNameSpecifier *Qualifier, + bool QualifierIsInformative, ASTContext &Context) { if (!Qualifier) return; @@ -744,7 +754,10 @@ void AddQualifierToCompletionString(CodeCompletionString *Result, llvm::raw_string_ostream OS(PrintedNNS); Qualifier->print(OS, Context.PrintingPolicy); } - Result->AddTextChunk(PrintedNNS.c_str()); + if (QualifierIsInformative) + Result->AddInformativeChunk(PrintedNNS.c_str()); + else + Result->AddTextChunk(PrintedNNS.c_str()); } /// \brief If possible, create a new code completion string for the given @@ -762,7 +775,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (FunctionDecl *Function = dyn_cast(ND)) { CodeCompletionString *Result = new CodeCompletionString; - AddQualifierToCompletionString(Result, Qualifier, S.Context); + AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, + S.Context); Result->AddTextChunk(Function->getNameAsString().c_str()); Result->AddTextChunk("("); AddFunctionParameterChunks(S.Context, Function, Result); @@ -772,7 +786,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (FunctionTemplateDecl *FunTmpl = dyn_cast(ND)) { CodeCompletionString *Result = new CodeCompletionString; - AddQualifierToCompletionString(Result, Qualifier, S.Context); + AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, + S.Context); FunctionDecl *Function = FunTmpl->getTemplatedDecl(); Result->AddTextChunk(Function->getNameAsString().c_str()); @@ -825,7 +840,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (TemplateDecl *Template = dyn_cast(ND)) { CodeCompletionString *Result = new CodeCompletionString; - AddQualifierToCompletionString(Result, Qualifier, S.Context); + AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, + S.Context); Result->AddTextChunk(Template->getNameAsString().c_str()); Result->AddTextChunk("<"); AddTemplateParameterChunks(S.Context, Template, Result); @@ -835,7 +851,8 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) { if (Qualifier) { CodeCompletionString *Result = new CodeCompletionString; - AddQualifierToCompletionString(Result, Qualifier, S.Context); + AddQualifierToCompletionString(Result, Qualifier, QualifierIsInformative, + S.Context); Result->AddTextChunk(ND->getNameAsString().c_str()); return Result; } diff --git a/test/CodeCompletion/member-access.cpp b/test/CodeCompletion/member-access.cpp index cbd19db1a5..b23436d8c8 100644 --- a/test/CodeCompletion/member-access.cpp +++ b/test/CodeCompletion/member-access.cpp @@ -28,15 +28,15 @@ public: void test(const Proxy &p) { p-> // RUN: clang-cc -fsyntax-only -code-completion-at=%s:29:6 %s -o - | FileCheck -check-prefix=CC1 %s && + // CHECK-CC1: member1 : 0 : [#Base1::#]member1 + // CHECK-CC1: member1 : 0 : [#Base2::#]member1 + // CHECK-CC1: member2 : 0 : [#Base1::#]member2 + // CHECK-CC1: member3 : 0 // CHECK-CC1: member4 : 0 - // CHECK-CC1: memfun3 : 0 - // CHECK-CC1: memfun1 : 1 - // CHECK-CC1: memfun1 : 1 - // CHECK-CC1: memfun2 : 1 - // CHECK-CC1: member1 : 2 - // CHECK-CC1: member1 : 2 - // CHECK-CC1: member2 : 2 - // CHECK-CC1: member3 : 2 - // CHECK-CC1: memfun1 : 2 (Hidden) : Base2::memfun1(<#int#>) + // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#float#>) + // CHECK-CC1: memfun1 : 0 : [#Base3::#]memfun1(<#double#>) + // CHECK-CC1: memfun2 : 0 : [#Base3::#]memfun2(<#int#>) + // CHECK-CC1: memfun3 : 0 : memfun3(<#int#>) + // CHECK-CC1: memfun1 : 0 (Hidden) : Base2::memfun1(<#int#>) // RUN: true diff --git a/test/CodeCompletion/namespace-alias.cpp b/test/CodeCompletion/namespace-alias.cpp index cae3d561d1..c92e554e5f 100644 --- a/test/CodeCompletion/namespace-alias.cpp +++ b/test/CodeCompletion/namespace-alias.cpp @@ -15,7 +15,7 @@ namespace N2 { // CHECK-CC1: I1 : 1 // CHECK-CC1: I4 : 1 // CHECK-CC1: I5 : 1 - // CHECK-CC1: N2 : 2 - // CHECK-NEXT-CC1: N4 : 2 + // CHECK-CC1: N2 : 3 + // CHECK-NEXT-CC1: N4 : 3 // RUN: true \ No newline at end of file diff --git a/test/CodeCompletion/operator.cpp b/test/CodeCompletion/operator.cpp index 72a3f6bb71..a3950f6b89 100644 --- a/test/CodeCompletion/operator.cpp +++ b/test/CodeCompletion/operator.cpp @@ -14,5 +14,5 @@ void f() { // CHECK-CC1: short : 0 // CHECK-CC1: Integer : 2 // CHECK-CC1: T : 2 - // CHECK-CC1: N : 5 + // CHECK-CC1: N : 6 // RUN: true diff --git a/test/CodeCompletion/tag.cpp b/test/CodeCompletion/tag.cpp index 201aec4dd3..2642b7c731 100644 --- a/test/CodeCompletion/tag.cpp +++ b/test/CodeCompletion/tag.cpp @@ -18,9 +18,9 @@ namespace N { // RUN: clang-cc -fsyntax-only -code-completion-at=%s:17:10 %s -o - | FileCheck -check-prefix=CC1 %s && // CHECK-CC1: Y : 2 // CHECK-CC1: Z : 2 - // CHECK-CC1: A : 3 - // CHECK-CC1: X : 3 - // CHECK-CC1: Y : 3 - // CHECK-CC1: M : 6 - // CHECK-CC1: N : 6 + // CHECK-CC1: A : 4 + // CHECK-CC1: X : 4 + // CHECK-CC1: Y : 4 + // CHECK-CC1: M : 9 + // CHECK-CC1: N : 9 // RUN: true diff --git a/test/CodeCompletion/using-namespace.cpp b/test/CodeCompletion/using-namespace.cpp index 95bff9b5ee..3e8cd53723 100644 --- a/test/CodeCompletion/using-namespace.cpp +++ b/test/CodeCompletion/using-namespace.cpp @@ -16,6 +16,6 @@ namespace N2 { // CHECK-CC1: I1 : 2 // CHECK-CC1: I4 : 2 // CHECK-CC1: I5 : 2 - // CHECK-CC1: N2 : 3 - // CHECK-NEXT-CC1: N4 : 3 + // CHECK-CC1: N2 : 4 + // CHECK-NEXT-CC1: N4 : 4 // RUN: true diff --git a/test/CodeCompletion/using.cpp b/test/CodeCompletion/using.cpp index 27b85fc766..dac556e151 100644 --- a/test/CodeCompletion/using.cpp +++ b/test/CodeCompletion/using.cpp @@ -18,8 +18,8 @@ namespace N2 { // CHECK-CC1: I1 : 2 // CHECK-CC1: I4 : 2 // CHECK-CC1: I5 : 2 - // CHECK-CC1: N2 : 3 - // CHECK-CC1: N3 : 3 - // CHECK-NEXT-CC1: N4 : 3 + // CHECK-CC1: N2 : 4 + // CHECK-CC1: N3 : 4 + // CHECK-NEXT-CC1: N4 : 4 // RUN: true -- 2.40.0