23 #include "clang/AST/Decl.h" 24 #include "clang/AST/DeclCXX.h" 25 #include "clang/AST/Type.h" 26 #include "clang/Sema/CodeCompleteConsumer.h" 27 #include "llvm/Support/Casting.h" 28 #include "gmock/gmock.h" 29 #include "gtest/gtest.h" 41 TEST(QualityTests, SymbolQualitySignalExtraction) {
46 int _f() { return _X; } 48 #define DECL_NAME(x, y) x##_##y##_Decl 49 #define DECL(x, y) class DECL_NAME(x, y) {}; 50 DECL(X, Y); // X_Y_Decl 53 auto Symbols = Header.headerSymbols();
54 auto AST = Header.
build();
77 Quality.
merge(CodeCompletionResult(&
findDecl(AST,
"_f"), 42));
84 Quality.
merge(CodeCompletionResult(
"if"));
88 TEST(QualityTests, SymbolRelevanceSignalExtraction) {
94 namespace hdr { class Bar {}; } // namespace hdr 96 #define DEFINE_FLAG(X) \ 106 using flags::FLAGS_FOO; 108 int ::header_main() {} 112 int deprecated() { return 0; } 114 namespace { struct X { void y() { int z; } }; } 117 auto AST = Test.
build();
120 Relevance.
merge(CodeCompletionResult(&
findDecl(AST,
"deprecated"),
128 Relevance.
merge(CodeCompletionResult(&
findDecl(AST,
"main"), 42));
130 <<
"Decl in current file";
132 Relevance.
merge(CodeCompletionResult(&
findDecl(AST,
"header"), 42));
135 Relevance.
merge(CodeCompletionResult(&
findDecl(AST,
"header_main"), 42));
137 <<
"Current file and header";
139 auto constructShadowDeclCompletionResult = [&](
const std::string DeclName) {
141 *dyn_cast<UsingDecl>(&
findDecl(AST, [&](
const NamedDecl &ND) {
142 if (
const UsingDecl *Using = dyn_cast<UsingDecl>(&ND))
143 if (Using->shadow_size() &&
144 Using->getQualifiedNameAsString() == DeclName)
148 CodeCompletionResult
Result(Shadow->getTargetDecl(), 42);
149 Result.ShadowDecl = Shadow;
154 Relevance.
merge(constructShadowDeclCompletionResult(
"Bar"));
156 <<
"Using declaration in main file";
157 Relevance.
merge(constructShadowDeclCompletionResult(
"FLAGS_FOO"));
159 <<
"Using declaration in main file";
172 Relevance.
merge(CodeCompletionResult(&
findDecl(AST,
"S::S"), 42));
178 BaseMember.InBaseClass =
true;
179 Relevance.
merge(BaseMember);
186 bool Matched =
false;
193 EXPECT_TRUE(Matched);
197 TEST(QualityTests, SymbolQualitySignalsSanity) {
203 EXPECT_LT(Deprecated.evaluate(), Default.
evaluate());
207 EXPECT_LT(ReservedName.evaluate(), Default.
evaluate());
211 EXPECT_LT(ImplementationDetail.evaluate(), Default.
evaluate());
215 ManyReferences.References = 1000;
216 EXPECT_GT(WithReferences.evaluate(), Default.
evaluate());
217 EXPECT_GT(ManyReferences.evaluate(), WithReferences.evaluate());
229 EXPECT_GT(Variable.evaluate(), Default.
evaluate());
230 EXPECT_GT(Keyword.evaluate(), Variable.evaluate());
231 EXPECT_LT(Macro.evaluate(), Default.
evaluate());
232 EXPECT_LT(Operator.evaluate(), Default.
evaluate());
233 EXPECT_LT(Constructor.evaluate(), Function.evaluate());
234 EXPECT_LT(Destructor.evaluate(), Constructor.evaluate());
237 TEST(QualityTests, SymbolRelevanceSignalsSanity) {
243 EXPECT_LT(Forbidden.evaluate(), Default.
evaluate());
247 EXPECT_LT(PoorNameMatch.evaluate(), Default.
evaluate());
251 EXPECT_GT(WithSemaFileProximity.evaluate(), Default.
evaluate());
257 WithSemaScopeProximity.SemaSaysInScope =
true;
258 EXPECT_GT(WithSemaScopeProximity.evaluate(), Default.
evaluate());
262 WithIndexScopeProximity.SymbolScope =
"x::";
263 EXPECT_GT(WithSemaScopeProximity.evaluate(), Default.
evaluate());
266 IndexProximate.
SymbolURI =
"unittest:/foo/bar.h";
267 llvm::StringMap<SourceParams> ProxSources;
268 ProxSources.try_emplace(
testPath(
"foo/baz.h"));
270 IndexProximate.FileProximityMatch = &Distance;
271 EXPECT_GT(IndexProximate.evaluate(), Default.
evaluate());
273 IndexDistant.
SymbolURI =
"unittest:/elsewhere/path.h";
274 EXPECT_GT(IndexProximate.evaluate(), IndexDistant.evaluate())
275 << IndexProximate << IndexDistant;
276 EXPECT_GT(IndexDistant.evaluate(), Default.
evaluate());
280 EXPECT_LT(Scoped.evaluate(), Default.
evaluate());
282 EXPECT_GT(Scoped.evaluate(), Default.
evaluate());
286 EXPECT_EQ(Instance.evaluate(), Default.
evaluate());
287 Instance.Context = CodeCompletionContext::CCC_DotMemberAccess;
288 EXPECT_LT(Instance.evaluate(), Default.
evaluate());
289 Instance.IsInstanceMember =
true;
290 EXPECT_EQ(Instance.evaluate(), Default.
evaluate());
294 EXPECT_LT(InBaseClass.evaluate(), Default.
evaluate());
296 llvm::StringSet<> Words = {
"one",
"two",
"three"};
299 WithoutMatchingWord.
Name =
"four";
303 WithMatchingWord.Name =
"TheTwoTowers";
304 EXPECT_GT(WithMatchingWord.evaluate(), Default.
evaluate());
307 TEST(QualityTests, ScopeProximity) {
309 ScopeDistance ScopeProximity({
"x::y::z::",
"x::",
"llvm::",
""});
313 float NotMatched = Relevance.
evaluate();
316 float Global = Relevance.
evaluate();
317 EXPECT_GT(Global, NotMatched);
320 float NonParent = Relevance.
evaluate();
321 EXPECT_GT(NonParent, Global);
324 float GrandParent = Relevance.
evaluate();
325 EXPECT_GT(GrandParent, Global);
328 float Parent = Relevance.
evaluate();
329 EXPECT_GT(Parent, GrandParent);
332 float Enclosing = Relevance.
evaluate();
333 EXPECT_GT(Enclosing, Parent);
336 TEST(QualityTests, SortText) {
337 EXPECT_LT(
sortText(std::numeric_limits<float>::infinity()),
343 EXPECT_LT(
sortText(-10),
sortText(-std::numeric_limits<float>::infinity()));
349 TEST(QualityTests, NoBoostForClassConstructor) {
356 auto Symbols = Header.headerSymbols();
357 auto AST = Header.
build();
359 const NamedDecl *Foo = &
findDecl(AST,
"Foo");
361 Cls.
merge(CodeCompletionResult(Foo, 0));
363 const NamedDecl *CtorDecl = &
findDecl(AST, [](
const NamedDecl &ND) {
364 return (ND.getQualifiedNameAsString() ==
"Foo::Foo") &&
365 isa<CXXConstructorDecl>(&ND);
368 Ctor.
merge(CodeCompletionResult(CtorDecl, 0));
374 TEST(QualityTests, IsInstanceMember) {
380 template <typename T> void tpl(T *t) {} 385 auto Symbols = Header.headerSymbols();
400 auto AST = Header.
build();
401 const NamedDecl *Foo = &
findDecl(AST,
"Foo::foo");
402 const NamedDecl *Bar = &
findDecl(AST,
"Foo::bar");
403 const NamedDecl *Tpl = &
findDecl(AST,
"Foo::tpl");
406 Rel.
merge(CodeCompletionResult(Foo, 0));
408 Rel.
merge(CodeCompletionResult(Bar, 0));
411 Rel.
merge(CodeCompletionResult(Tpl, 0));
415 TEST(QualityTests, ConstructorDestructor) {
423 auto Symbols = Header.headerSymbols();
424 auto AST = Header.
build();
426 const NamedDecl *CtorDecl = &
findDecl(AST, [](
const NamedDecl &ND) {
427 return (ND.getQualifiedNameAsString() ==
"Foo::Foo") &&
428 isa<CXXConstructorDecl>(&ND);
430 const NamedDecl *DtorDecl = &
findDecl(AST, [](
const NamedDecl &ND) {
431 return (ND.getQualifiedNameAsString() ==
"Foo::~Foo") &&
432 isa<CXXDestructorDecl>(&ND);
436 CtorQ.
merge(CodeCompletionResult(CtorDecl, 0));
441 CtorQ.merge(CtorSym);
445 DtorQ.
merge(CodeCompletionResult(DtorDecl, 0));
453 bool operator<(const Foo& f1); 456 auto AST = Header.
build();
459 if (
const auto *OD = dyn_cast<FunctionDecl>(&ND))
460 if (OD->isOverloadedOperator())
465 Q.
merge(CodeCompletionResult(Operator, 0));
469 TEST(QualityTests, ItemWithFixItsRankedDown) {
476 auto AST = Header.
build();
479 RelevanceWithFixIt.
merge(CodeCompletionResult(&
findDecl(AST,
"x"), 0,
nullptr,
480 false,
true, {FixItHint{}}));
484 RelevanceWithoutFixIt.
merge(
485 CodeCompletionResult(&
findDecl(AST,
"x"), 0,
nullptr,
false,
true, {}));
SignatureQualitySignals Quality
static int LLVM_ATTRIBUTE_UNUSED UnittestSchemeAnchorDest
bool AnyScope
If set to true, allow symbols from any scope.
void merge(const CodeCompletionResult &SemaCCResult)
llvm::Optional< llvm::StringRef > SymbolScope
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
enum clang::clangd::SymbolQualitySignals::SymbolCategory Category
void merge(const CodeCompletionResult &SemaResult)
std::string sortText(float Score, llvm::StringRef Name)
Returns a string that sorts in the same order as (-Score, Tiebreak), for LSP.
virtual bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref< void(const Symbol &)> Callback) const =0
Matches symbols in the index fuzzily and applies Callback on each matched symbol before returning...
bool ImplementationDetail
Attributes of a symbol that affect how much we like it.
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
llvm::StringRef Name
The name of the symbol (for ContextWords). Must be explicitly assigned.
enum clang::clangd::SymbolRelevanceSignals::AccessibleScope Scope
unsigned References
The number of translation units that reference this symbol from their main file.
bool IncludeFixIts
Include completions that require small corrections, e.g.
bool NeedsFixIts
Whether fixits needs to be applied for that completion or not.
llvm::StringSet * ContextWords
Lowercase words relevant to the context (e.g. near the completion point).
static TestTU withHeaderCode(llvm::StringRef HeaderCode)
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
*that are placed right before the argument **code *void f(bool foo)
Checks that argument comments match parameter names.
std::string Query
A query string for the fuzzy find.
ScopeDistance * ScopeProximityMatch
llvm::StringRef SymbolURI
These are used to calculate proximity between the index symbol and the query.
volatile int UnittestSchemeAnchorSource
The class presents a C++ symbol, e.g.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Support lookups like FileDistance, but the lookup keys are symbol scopes.
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
std::unique_ptr< SymbolIndex > index() const
float SemaFileProximityScore
FIXME: unify with index proximity score - signals should be source-independent.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
float NameMatch
0-1+ fuzzy-match score for unqualified name. Must be explicitly assigned.
Attributes of a symbol-query pair that affect how much we like it.
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
const SymbolIndex * Index