Index: clang-tools-extra/clangd/Quality.cpp =================================================================== --- clang-tools-extra/clangd/Quality.cpp +++ clang-tools-extra/clangd/Quality.cpp @@ -41,6 +41,17 @@ return false; } +static bool hasUsingDeclInMainFile(const CodeCompletionResult &R) { + const auto &Context = R.Declaration->getASTContext(); + const auto &SourceMgr = Context.getSourceManager(); + if (R.ShadowDecl) { + const auto Loc = SourceMgr.getSpellingLoc(R.ShadowDecl->getLocation()); + if (SourceMgr.isWrittenInMainFile(Loc)) + return true; + } + return false; +} + static SymbolQualitySignals::SymbolCategory categorize(const NamedDecl &ND) { class Switch : public ConstDeclVisitor { @@ -231,8 +242,10 @@ // We boost things that have decls in the main file. We give a fixed score // for all other declarations in sema as they are already included in the // translation unit. - float DeclProximity = - hasDeclInMainFile(*SemaCCResult.Declaration) ? 1.0 : 0.6; + float DeclProximity = (hasDeclInMainFile(*SemaCCResult.Declaration) || + hasUsingDeclInMainFile(SemaCCResult)) + ? 1.0 + : 0.6; SemaProximityScore = std::max(DeclProximity, SemaProximityScore); } Index: clang-tools-extra/unittests/clangd/QualityTests.cpp =================================================================== --- clang-tools-extra/unittests/clangd/QualityTests.cpp +++ clang-tools-extra/unittests/clangd/QualityTests.cpp @@ -79,8 +79,18 @@ Test.HeaderCode = R"cpp( int header(); int header_main(); + + namespace foo { + class Bar {}; + } // namespace foo + + namespace bar { + class Foo {}; + } // namespace bar )cpp"; Test.Code = R"cpp( + using foo::Bar; + int ::header_main() {} int main(); @@ -109,7 +119,36 @@ Relevance = {}; Relevance.merge(CodeCompletionResult(&findDecl(AST, "header_main"), 42)); EXPECT_FLOAT_EQ(Relevance.SemaProximityScore, 1.0f) - << "Current file and header"; + << "Current file and definition in header"; + + Relevance = {}; + const auto SymbolName = "Bar"; + const NamedDecl *FoundDecl; + UsingShadowDecl *Shadow; + auto findUsingDecl = [&SymbolName, &Shadow, + &FoundDecl](const NamedDecl &ND) -> bool { + if (const UsingDecl *Using = dyn_cast(&ND)) { + if (UsingShadowDecl *ShadowDecl = *Using->shadow_begin()) { + if (ShadowDecl->getTargetDecl()->getName() == SymbolName) { + Shadow = ShadowDecl; + FoundDecl = Shadow->getTargetDecl(); + return true; + } + } + } + return false; + }; + findAnyDecl(AST, findUsingDecl); + CodeCompletionResult Result(FoundDecl, 42); + Result.setShadowDecl(Shadow); + Relevance.merge(Result); + EXPECT_FLOAT_EQ(Relevance.SemaProximityScore, 1.0f) + << "Uprank declaration which has using directive in current directory"; + + Relevance = {}; + Relevance.merge(CodeCompletionResult(&findDecl(AST, "bar::Foo"), 42)); + EXPECT_FLOAT_EQ(Relevance.SemaProximityScore, 0.6f) + << "Only definition in header"; Relevance = {}; Relevance.merge(CodeCompletionResult(&findAnyDecl(AST, "X"), 42)); Index: clang-tools-extra/unittests/clangd/TestTU.cpp =================================================================== --- clang-tools-extra/unittests/clangd/TestTU.cpp +++ clang-tools-extra/unittests/clangd/TestTU.cpp @@ -72,6 +72,7 @@ return *Result; } +// Finds Decl in AST matching fully qualified name. const NamedDecl &findDecl(ParsedAST &AST, llvm::StringRef QName) { llvm::SmallVector Components; QName.split(Components, "::"); @@ -114,6 +115,7 @@ return *Visitor.Decls.front(); } +// Finds any Decl having given unqualified name. const NamedDecl &findAnyDecl(ParsedAST &AST, llvm::StringRef Name) { return findAnyDecl(AST, [Name](const NamedDecl &ND) { if (auto *ID = ND.getIdentifier()) Index: clang/include/clang/Sema/CodeCompleteConsumer.h =================================================================== --- clang/include/clang/Sema/CodeCompleteConsumer.h +++ clang/include/clang/Sema/CodeCompleteConsumer.h @@ -45,8 +45,9 @@ class NamedDecl; class NestedNameSpecifier; class Preprocessor; -class Sema; class RawComment; +class Sema; +class UsingShadowDecl; /// Default priority values for code-completion results based /// on their kind. @@ -836,18 +837,25 @@ /// informative rather than required. NestedNameSpecifier *Qualifier = nullptr; + /// If this Decl was unshadowed by using declaration, this would would store + /// a pointer to the UsingShadowDecl which was used in the unshadowing + /// process. This information can be used to uprank CodeCompletionResults + /// which have corresponding `using decl::qualified::name;` nearby. + const UsingShadowDecl *ShadowDecl = nullptr; + /// Build a result that refers to a declaration. CodeCompletionResult(const NamedDecl *Declaration, unsigned Priority, NestedNameSpecifier *Qualifier = nullptr, bool QualifierIsInformative = false, bool Accessible = true, - std::vector FixIts = std::vector()) + std::vector FixIts = std::vector(), + const UsingShadowDecl *ShadowDecl = nullptr) : Declaration(Declaration), Priority(Priority), Kind(RK_Declaration), FixIts(std::move(FixIts)), Hidden(false), QualifierIsInformative(QualifierIsInformative), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - DeclaringEntity(false), Qualifier(Qualifier) { - //FIXME: Add assert to check FixIts range requirements. + DeclaringEntity(false), Qualifier(Qualifier), ShadowDecl(ShadowDecl) { + // FIXME: Add assert to check FixIts range requirements. computeCursorKindAndAvailability(Accessible); } @@ -900,6 +908,10 @@ return Keyword; } + void setShadowDecl(const UsingShadowDecl *Shadow) { + this->ShadowDecl = Shadow; + } + /// Create a new code-completion string that describes how to insert /// this result into a program. /// Index: clang/lib/Sema/SemaCodeComplete.cpp =================================================================== --- clang/lib/Sema/SemaCodeComplete.cpp +++ clang/lib/Sema/SemaCodeComplete.cpp @@ -859,12 +859,12 @@ } // Look through using declarations. - if (const UsingShadowDecl *Using = - dyn_cast(R.Declaration)) { - MaybeAddResult(Result(Using->getTargetDecl(), - getBasePriority(Using->getTargetDecl()), - R.Qualifier), - CurContext); + if (const UsingShadowDecl *Using = dyn_cast(R.Declaration)) { + CodeCompletionResult Result(Using->getTargetDecl(), + getBasePriority(Using->getTargetDecl()), + R.Qualifier); + Result.setShadowDecl(Using); + MaybeAddResult(Result, CurContext); return; } @@ -977,10 +977,11 @@ // Look through using declarations. if (const UsingShadowDecl *Using = dyn_cast(R.Declaration)) { - AddResult(Result(Using->getTargetDecl(), - getBasePriority(Using->getTargetDecl()), - R.Qualifier), - CurContext, Hiding); + CodeCompletionResult Result(Using->getTargetDecl(), + getBasePriority(Using->getTargetDecl()), + R.Qualifier); + Result.setShadowDecl(Using); + AddResult(Result, CurContext, Hiding); return; } @@ -1004,10 +1005,10 @@ if (AsNestedNameSpecifier) { R.StartsNestedNameSpecifier = true; R.Priority = CCP_NestedNameSpecifier; - } - else if (Filter == &ResultBuilder::IsMember && !R.Qualifier && InBaseClass && - isa(R.Declaration->getDeclContext() - ->getRedeclContext())) + } else if (Filter == &ResultBuilder::IsMember && !R.Qualifier && + InBaseClass && + isa( + R.Declaration->getDeclContext()->getRedeclContext())) R.QualifierIsInformative = true; // If this result is supposed to have an informative qualifier, add one.