Index: clang-tools-extra/trunk/clangd/CodeComplete.h =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.h +++ clang-tools-extra/trunk/clangd/CodeComplete.h @@ -21,6 +21,7 @@ #include "Protocol.h" #include "index/Index.h" #include "clang/Frontend/PrecompiledPreamble.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Tooling/CompilationDatabase.h" @@ -144,6 +145,7 @@ struct CodeCompleteResult { std::vector Completions; bool HasMore = false; + CodeCompletionContext::Kind Context = CodeCompletionContext::CCC_Other; }; raw_ostream &operator<<(raw_ostream &, const CodeCompleteResult &); Index: clang-tools-extra/trunk/clangd/CodeComplete.cpp =================================================================== --- clang-tools-extra/trunk/clangd/CodeComplete.cpp +++ clang-tools-extra/trunk/clangd/CodeComplete.cpp @@ -1062,6 +1062,7 @@ Output.Completions.back().Score = C.second; } Output.HasMore = Incomplete; + Output.Context = Recorder->CCContext.getKind(); return Output; } @@ -1156,6 +1157,7 @@ CompletionCandidate::Bundle Bundle) { SymbolQualitySignals Quality; SymbolRelevanceSignals Relevance; + Relevance.Context = Recorder->CCContext.getKind(); Relevance.Query = SymbolRelevanceSignals::CodeComplete; Relevance.FileProximityMatch = FileProximity.getPointer(); auto &First = Bundle.front(); @@ -1290,6 +1292,7 @@ raw_ostream &operator<<(raw_ostream &OS, const CodeCompleteResult &R) { OS << "CodeCompleteResult: " << R.Completions.size() << (R.HasMore ? "+" : "") + << " (" << getCompletionKindString(R.Context) << ")" << " items:\n"; for (const auto &C : R.Completions) OS << C << "\n"; Index: clang-tools-extra/trunk/clangd/Quality.h =================================================================== --- clang-tools-extra/trunk/clangd/Quality.h +++ clang-tools-extra/trunk/clangd/Quality.h @@ -26,6 +26,7 @@ //===---------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_QUALITY_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_QUALITY_H +#include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include @@ -98,6 +99,11 @@ Generic, } Query = Generic; + CodeCompletionContext::Kind Context = CodeCompletionContext::CCC_Other; + + // Whether symbol is an instance member of a class. + bool IsInstanceMember = false; + void merge(const CodeCompletionResult &SemaResult); void merge(const Symbol &IndexResult); Index: clang-tools-extra/trunk/clangd/Quality.cpp =================================================================== --- clang-tools-extra/trunk/clangd/Quality.cpp +++ clang-tools-extra/trunk/clangd/Quality.cpp @@ -11,7 +11,9 @@ #include "URI.h" #include "index/Index.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" @@ -139,6 +141,27 @@ llvm_unreachable("Unknown index::SymbolKind"); } +static bool isInstanceMember(const NamedDecl *ND) { + if (!ND) + return false; + if (const auto *TP = dyn_cast(ND)) + ND = TP->TemplateDecl::getTemplatedDecl(); + if (const auto *CM = dyn_cast(ND)) + return !CM->isStatic(); + return isa(ND); // Note that static fields are VarDecl. +} + +static bool isInstanceMember(const index::SymbolInfo &D) { + switch (D.Kind) { + case index::SymbolKind::InstanceMethod: + case index::SymbolKind::InstanceProperty: + case index::SymbolKind::Field: + return true; + default: + return false; + } +} + void SymbolQualitySignals::merge(const CodeCompletionResult &SemaCCResult) { if (SemaCCResult.Availability == CXAvailability_Deprecated) Deprecated = true; @@ -231,6 +254,7 @@ // relevant to non-completion requests, we should recognize class members etc. SymbolURI = IndexResult.CanonicalDeclaration.FileURI; + IsInstanceMember |= isInstanceMember(IndexResult.SymInfo); } void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) { @@ -247,6 +271,7 @@ ? 1.0 : 0.6; SemaProximityScore = std::max(DeclProximity, SemaProximityScore); + IsInstanceMember |= isInstanceMember(SemaCCResult.Declaration); } // Declarations are scoped, others (like macros) are assumed global. @@ -296,6 +321,13 @@ } } + // Penalize non-instance members when they are accessed via a class instance. + if (!IsInstanceMember && + (Context == CodeCompletionContext::CCC_DotMemberAccess || + Context == CodeCompletionContext::CCC_ArrowMemberAccess)) { + Score *= 0.5; + } + return Score; } @@ -303,6 +335,8 @@ OS << formatv("=== Symbol relevance: {0}\n", S.evaluate()); OS << formatv("\tName match: {0}\n", S.NameMatch); OS << formatv("\tForbidden: {0}\n", S.Forbidden); + OS << formatv("\tIsInstanceMember: {0}\n", S.IsInstanceMember); + OS << formatv("\tContext: {0}\n", getCompletionKindString(S.Context)); OS << formatv("\tSymbol URI: {0}\n", S.SymbolURI); if (S.FileProximityMatch) { auto Score = proximityScore(S.SymbolURI, S.FileProximityMatch); Index: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp @@ -18,6 +18,7 @@ #include "SyncAPI.h" #include "TestFS.h" #include "index/MemIndex.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/Support/Error.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" @@ -1321,6 +1322,21 @@ UnorderedElementsAre(AllOf(Scope("ns::X::"), Named("x_")))); } +TEST(CompletionTest, CodeCompletionContext) { + auto Results = completions( + R"cpp( + namespace ns { + class X { public: X(); int x_; }; + void f() { + X x; + x.^; + } + } + )cpp"); + + EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/unittests/clangd/QualityTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clangd/QualityTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/QualityTests.cpp @@ -23,6 +23,7 @@ #include "TestTU.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/Support/Casting.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -227,6 +228,14 @@ EXPECT_EQ(Scoped.evaluate(), Default.evaluate()); Scoped.Query = SymbolRelevanceSignals::CodeComplete; EXPECT_GT(Scoped.evaluate(), Default.evaluate()); + + SymbolRelevanceSignals Instance; + Instance.IsInstanceMember = false; + EXPECT_EQ(Instance.evaluate(), Default.evaluate()); + Instance.Context = CodeCompletionContext::CCC_DotMemberAccess; + EXPECT_LT(Instance.evaluate(), Default.evaluate()); + Instance.IsInstanceMember = true; + EXPECT_EQ(Instance.evaluate(), Default.evaluate()); } TEST(QualityTests, SortText) { @@ -267,6 +276,47 @@ EXPECT_EQ(Ctor.Scope, SymbolRelevanceSignals::GlobalScope); } +TEST(QualityTests, IsInstanceMember) { + auto Header = TestTU::withHeaderCode(R"cpp( + class Foo { + public: + static void foo() {} + + template void tpl(T *t) {} + + void bar() {} + }; + )cpp"); + auto Symbols = Header.headerSymbols(); + + SymbolRelevanceSignals Rel; + const Symbol &FooSym = findSymbol(Symbols, "Foo::foo"); + Rel.merge(FooSym); + EXPECT_FALSE(Rel.IsInstanceMember); + const Symbol &BarSym = findSymbol(Symbols, "Foo::bar"); + Rel.merge(BarSym); + EXPECT_TRUE(Rel.IsInstanceMember); + + Rel.IsInstanceMember =false; + const Symbol &TplSym = findSymbol(Symbols, "Foo::tpl"); + Rel.merge(TplSym); + EXPECT_TRUE(Rel.IsInstanceMember); + + auto AST = Header.build(); + const NamedDecl *Foo = &findDecl(AST, "Foo::foo"); + const NamedDecl *Bar = &findDecl(AST, "Foo::bar"); + const NamedDecl *Tpl = &findDecl(AST, "Foo::tpl"); + + Rel.IsInstanceMember = false; + Rel.merge(CodeCompletionResult(Foo, /*Priority=*/0)); + EXPECT_FALSE(Rel.IsInstanceMember); + Rel.merge(CodeCompletionResult(Bar, /*Priority=*/0)); + EXPECT_TRUE(Rel.IsInstanceMember); + Rel.IsInstanceMember = false; + Rel.merge(CodeCompletionResult(Tpl, /*Priority=*/0)); + EXPECT_TRUE(Rel.IsInstanceMember); +} + } // namespace } // namespace clangd } // namespace clang