diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -259,6 +259,7 @@ ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST; ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; + CodeCompleteOpts.MainFileSignals = IP->Signals; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -19,6 +19,7 @@ #include "Headers.h" #include "Protocol.h" #include "Quality.h" +#include "TUScheduler.h" #include "index/Index.h" #include "index/Symbol.h" #include "index/SymbolOrigin.h" @@ -110,6 +111,7 @@ /// clangd. const SymbolIndex *Index = nullptr; + const ASTSignals *MainFileSignals = nullptr; /// Include completions that require small corrections, e.g. change '.' to /// '->' on member access etc. bool IncludeFixIts = false; diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1686,6 +1686,7 @@ if (PreferredType) Relevance.HadContextType = true; Relevance.ContextWords = &ContextWords; + Relevance.MainFileSignals = Opts.MainFileSignals; auto &First = Bundle.front(); if (auto FuzzyScore = fuzzyScore(First)) diff --git a/clang-tools-extra/clangd/Quality.h b/clang-tools-extra/clangd/Quality.h --- a/clang-tools-extra/clangd/Quality.h +++ b/clang-tools-extra/clangd/Quality.h @@ -29,6 +29,7 @@ #include "ExpectedTypes.h" #include "FileDistance.h" +#include "TUScheduler.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" @@ -140,6 +141,10 @@ /// CompletionPrefix. unsigned FilterLength = 0; + const ASTSignals *MainFileSignals = nullptr; + unsigned MainFileRefs = 0; + unsigned ScopeRefsInFile = 0; + /// Set of derived signals computed by calculateDerivedSignals(). Must not be /// set explicitly. struct DerivedSignals { diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -294,6 +294,13 @@ if (!(IndexResult.Flags & Symbol::VisibleOutsideFile)) { Scope = AccessibleScope::FileScope; } + if (MainFileSignals) { + MainFileRefs = + std::max(MainFileRefs, MainFileSignals->Symbols.lookup(IndexResult.ID)); + ScopeRefsInFile = + std::max(ScopeRefsInFile, + MainFileSignals->RelatedNamespaces.lookup(IndexResult.Scope)); + } } void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) { @@ -314,6 +321,21 @@ IsInstanceMember |= isInstanceMember(SemaCCResult.Declaration); InBaseClass |= SemaCCResult.InBaseClass; } + if (MainFileSignals) { + if ((SemaCCResult.Kind == CodeCompletionResult::RK_Declaration) || + (SemaCCResult.Kind == CodeCompletionResult::RK_Pattern)) { + if (const NamedDecl *ND = SemaCCResult.getDeclaration()) { + MainFileRefs = std::max( + MainFileRefs, MainFileSignals->Symbols.lookup(getSymbolID(ND))); + std::string Qual; + llvm::raw_string_ostream OS(Qual); + ND->printNestedNameSpecifier(OS); + if (!Qual.empty()) + ScopeRefsInFile = std::max( + ScopeRefsInFile, MainFileSignals->RelatedNamespaces.lookup(Qual)); + } + } + } // Declarations are scoped, others (like macros) are assumed global. if (SemaCCResult.Declaration) diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -15,6 +15,7 @@ #include "Quality.h" #include "SourceCode.h" #include "SyncAPI.h" +#include "TUScheduler.h" #include "TestFS.h" #include "TestIndex.h" #include "TestTU.h" @@ -51,6 +52,8 @@ // GMock helpers for matching completion items. MATCHER_P(Named, Name, "") { return arg.Name == Name; } +MATCHER_P(MainFileRefs, Refs, "") { return arg.MainFileRefs == Refs; } +MATCHER_P(ScopeRefs, Refs, "") { return arg.ScopeRefsInFile == Refs; } MATCHER_P(NameStartsWith, Prefix, "") { return llvm::StringRef(arg.Name).startswith(Prefix); } @@ -1115,6 +1118,63 @@ UnorderedElementsAre(Named("xy1"), Named("xy2"))); } +TEST(CompletionTest, ASTSignals) { + struct Completion { + std::string Name; + unsigned MainFileRefs; + unsigned ScopeRefsInFile; + }; + CodeCompleteOptions Opts; + std::vector RecordedCompletions; + Opts.RecordCCResult = [&RecordedCompletions](const CodeCompletion &CC, + const SymbolQualitySignals &, + const SymbolRelevanceSignals &R, + float Score) { + RecordedCompletions.push_back({CC.Name, R.MainFileRefs, R.ScopeRefsInFile}); + }; + ASTSignals MainFileSignals; + MainFileSignals.Symbols[var("xy1").ID] = 3; + MainFileSignals.Symbols[var("xy2").ID] = 1; + MainFileSignals.Symbols[var("xyindex").ID] = 10; + MainFileSignals.RelatedNamespaces["tar::"] = 5; + MainFileSignals.RelatedNamespaces["clang::"] = 3; + Opts.MainFileSignals = &MainFileSignals; + Opts.AllScopes = true; + completions(R"cpp( + int xy1; + int xy2; + namespace clang { + const int xybar = 1; + int a = xy^ + } + )cpp", + /*IndexSymbols=*/{var("xyindex"), var("tar::xyfoo")}, Opts); + EXPECT_THAT(RecordedCompletions, + UnorderedElementsAre( + AllOf(Named("xy1"), MainFileRefs(3u), ScopeRefs(0u)), + AllOf(Named("xy2"), MainFileRefs(1u), ScopeRefs(0u)), + AllOf(Named("xyindex"), MainFileRefs(10u), ScopeRefs(0u)), + AllOf(Named("xyfoo"), MainFileRefs(0u), ScopeRefs(5u)), + AllOf(Named("xybar"), MainFileRefs(0u), ScopeRefs(3u)))); + // Annotations Test(R"cpp( + // #include "foo.h" + // Fun^ + // )cpp"); + // auto TU = TestTU::withCode(Test.code()); + // TU.AdditionalFiles["foo.h"] = ""; + + // std::string DeclFile = URI::create(testPath("foo")).toString(); + // Symbol Sym = func("Func"); + // Sym.CanonicalDeclaration.FileURI = DeclFile.c_str(); + // Sym.IncludeHeaders.emplace_back("\"foo.h\"", 2); + // Sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000); + + // EXPECT_THAT(completions(TU, Test.point(), {Sym}).Completions, + // UnorderedElementsAre(AllOf(Named("Func"), + // HasInclude("\"foo.h\""), + // Not(InsertInclude())))); +} + SignatureHelp signatures(llvm::StringRef Text, Position Point, std::vector IndexSymbols = {}) { std::unique_ptr Index;