From 33224e61bfca370850abae89bbd415a4dabe07fa Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 18 Sep 2009 17:42:29 +0000 Subject: [PATCH] For code completion in C++ member access expressions and tag names, look into the current scope for anything that could start a nested-names-specifier. These results are ranked worse than any of the results actually found in the lexical scope. Perform a little more pruning of the result set, eliminating constructors, __va_list_tag, and any duplication of declarations in the result set. For the latter, implemented NamespaceDecl::getCanonicalDecl. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82231 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 2 ++ include/clang/Sema/CodeCompleteConsumer.h | 7 ++++ lib/Sema/CodeCompleteConsumer.cpp | 41 ++++++++++++++++++----- test/CodeCompletion/tag.cpp | 20 +++++++++++ 4 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 test/CodeCompletion/tag.cpp diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 21b459a0ea..231e36ab5f 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -201,6 +201,8 @@ public: } void setOriginalNamespace(NamespaceDecl *ND) { OrigNamespace = ND; } + virtual NamespaceDecl *getCanonicalDecl() { return OrigNamespace; } + virtual SourceRange getSourceRange() const { return SourceRange(getLocation(), RBracLoc); } diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 425a937a93..ce5707ed3a 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -15,6 +15,7 @@ #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" +#include "llvm/ADT/SmallPtrSet.h" #include #include #include @@ -25,6 +26,7 @@ class raw_ostream; namespace clang { +class Decl; class DeclContext; class NamedDecl; class Scope; @@ -89,6 +91,11 @@ public: /// \brief The actual results we have found. std::vector Results; + /// \brief A record of all of the declarations we have found and placed + /// into the result set, used to ensure that no declaration ever gets into + /// the result set twice. + llvm::SmallPtrSet AllDeclsFound; + /// \brief A mapping from declaration names to the declarations that have /// this name within a particular scope and their index within the list of /// results. diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index fb82a6c331..7dee12a3dc 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -53,8 +53,10 @@ CodeCompleteConsumer::CodeCompleteMemberReferenceExpr(Scope *S, // The "template" keyword can follow "->" or "." in the grammar. Results.MaybeAddResult(Result("template", NextRank++)); - // FIXME: For C++, we also need to look into the current scope, since - // we could have the start of a nested-name-specifier. + // We could have the start of a nested-name-specifier. Add those + // results as well. + Results.setFilter(&CodeCompleteConsumer::IsNestedNameSpecifier); + CollectLookupResults(S, NextRank, Results); } // Hand off the results found for code completion. @@ -83,10 +85,14 @@ void CodeCompleteConsumer::CodeCompleteTag(Scope *S, ElaboratedType::TagKind TK) } ResultSet Results(*this, Filter); - CollectLookupResults(S, 0, Results); + unsigned NextRank = CollectLookupResults(S, 0, Results); - // FIXME: In C++, we could have the start of a nested-name-specifier. - // Add those results (with a poorer rank, naturally). + if (getSema().getLangOptions().CPlusPlus) { + // We could have the start of a nested-name-specifier. Add those + // results as well. + Results.setFilter(&CodeCompleteConsumer::IsNestedNameSpecifier); + CollectLookupResults(S, NextRank, Results); + } ProcessCodeCompleteResults(Results.data(), Results.size()); } @@ -121,10 +127,6 @@ void CodeCompleteConsumer::ResultSet::MaybeAddResult(Result R) { // FIXME: Using declarations // FIXME: Separate overload sets - // Filter out any unwanted results. - if (Filter && !(Completer.*Filter)(R.Declaration)) - return; - Decl *CanonDecl = R.Declaration->getCanonicalDecl(); unsigned IDNS = CanonDecl->getIdentifierNamespace(); @@ -134,6 +136,23 @@ void CodeCompleteConsumer::ResultSet::MaybeAddResult(Result R) { (IDNS & (Decl::IDNS_OrdinaryFriend | Decl::IDNS_TagFriend))) return; + if (const IdentifierInfo *Id = R.Declaration->getIdentifier()) { + // __va_list_tag is a freak of nature. Find it and skip it. + if (Id->isStr("__va_list_tag")) + return; + + // FIXME: Should we filter out other names in the implementation's + // namespace, e.g., those containing a __ or that start with _[A-Z]? + } + + // C++ constructors are never found by name lookup. + if (isa(CanonDecl)) + return; + + // Filter out any unwanted results. + if (Filter && !(Completer.*Filter)(R.Declaration)) + return; + ShadowMap &SMap = ShadowMaps.back(); ShadowMap::iterator I, IEnd; for (llvm::tie(I, IEnd) = SMap.equal_range(R.Declaration->getDeclName()); @@ -187,6 +206,10 @@ void CodeCompleteConsumer::ResultSet::MaybeAddResult(Result R) { } } + // Make sure that any given declaration only shows up in the result set once. + if (!AllDeclsFound.insert(CanonDecl)) + return; + // Insert this result into the set of results and into the current shadow // map. SMap.insert(std::make_pair(R.Declaration->getDeclName(), diff --git a/test/CodeCompletion/tag.cpp b/test/CodeCompletion/tag.cpp new file mode 100644 index 0000000000..addad9d769 --- /dev/null +++ b/test/CodeCompletion/tag.cpp @@ -0,0 +1,20 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +class X { }; +struct Y { }; + +namespace N { + template class Z; +} + +namespace N { + class Y; + + void test() { + // CHECK-CC1: Y : 2 + // CHECK-CC1: Z : 2 + // CHECK-CC1: X : 3 + // CHECK-CC1: Y : 3 + // CHECK-CC1: N : 6 + class -- 2.40.0