15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/DeclTemplate.h"
19#include "clang/AST/DeclVisitor.h"
20#include "clang/Basic/SourceManager.h"
21#include "clang/Sema/CodeCompleteConsumer.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Support/Casting.h"
24#include "llvm/Support/FormatVariadic.h"
25#include "llvm/Support/MathExtras.h"
26#include "llvm/Support/raw_ostream.h"
35 auto &SourceMgr = D.getASTContext().getSourceManager();
36 for (
auto *Redecl : D.redecls()) {
44 const auto &
Context = R.Declaration->getASTContext();
45 const auto &SourceMgr =
Context.getSourceManager();
54 if (
const auto *FD = dyn_cast<FunctionDecl>(&ND)) {
55 if (FD->isOverloadedOperator())
59 :
public ConstDeclVisitor<Switch, SymbolQualitySignals::SymbolCategory> {
61#define MAP(DeclType, Category) \
62 SymbolQualitySignals::SymbolCategory Visit##DeclType(const DeclType *) { \
63 return SymbolQualitySignals::Category; \
68 MAP(TypeAliasTemplateDecl,
Type);
71 MAP(CXXDestructorDecl, Destructor);
79 return Switch().Visit(&ND);
86 if (R.Kind == CodeCompletionResult::RK_Macro)
90 switch (R.CursorKind) {
91 case CXCursor_CXXMethod:
93 case CXCursor_ModuleImportDecl:
95 case CXCursor_MacroDefinition:
97 case CXCursor_TypeRef:
99 case CXCursor_MemberRef:
101 case CXCursor_Constructor:
111 case index::SymbolKind::Namespace:
112 case index::SymbolKind::NamespaceAlias:
114 case index::SymbolKind::Macro:
116 case index::SymbolKind::Enum:
117 case index::SymbolKind::Struct:
118 case index::SymbolKind::Class:
119 case index::SymbolKind::Protocol:
120 case index::SymbolKind::Extension:
121 case index::SymbolKind::Union:
122 case index::SymbolKind::TypeAlias:
123 case index::SymbolKind::TemplateTypeParm:
124 case index::SymbolKind::TemplateTemplateParm:
125 case index::SymbolKind::Concept:
127 case index::SymbolKind::Function:
128 case index::SymbolKind::ClassMethod:
129 case index::SymbolKind::InstanceMethod:
130 case index::SymbolKind::StaticMethod:
131 case index::SymbolKind::InstanceProperty:
132 case index::SymbolKind::ClassProperty:
133 case index::SymbolKind::StaticProperty:
134 case index::SymbolKind::ConversionFunction:
136 case index::SymbolKind::Destructor:
138 case index::SymbolKind::Constructor:
140 case index::SymbolKind::Variable:
141 case index::SymbolKind::Field:
142 case index::SymbolKind::EnumConstant:
143 case index::SymbolKind::Parameter:
144 case index::SymbolKind::NonTypeTemplateParm:
146 case index::SymbolKind::Using:
147 case index::SymbolKind::Module:
148 case index::SymbolKind::Unknown:
151 llvm_unreachable(
"Unknown index::SymbolKind");
157 if (
const auto *TP = dyn_cast<FunctionTemplateDecl>(ND))
158 ND = TP->TemplateDecl::getTemplatedDecl();
159 if (
const auto *
CM = dyn_cast<CXXMethodDecl>(ND))
160 return !
CM->isStatic();
161 return isa<FieldDecl>(ND);
166 case index::SymbolKind::InstanceMethod:
167 case index::SymbolKind::InstanceProperty:
168 case index::SymbolKind::Field:
176 Deprecated |= (SemaCCResult.Availability == CXAvailability_Deprecated);
179 if (SemaCCResult.Declaration) {
181 if (
auto *
ID = SemaCCResult.Declaration->getIdentifier())
183 }
else if (SemaCCResult.Kind == CodeCompletionResult::RK_Macro)
211 Score *= 6.0 * (1 - S) / (1 + S) + 0.59;
248 OS << llvm::formatv(
"=== Symbol quality: {0}\n", S.evaluateHeuristics());
249 OS << llvm::formatv(
"\tReferences: {0}\n", S.References);
250 OS << llvm::formatv(
"\tDeprecated: {0}\n", S.Deprecated);
251 OS << llvm::formatv(
"\tReserved name: {0}\n", S.ReservedName);
252 OS << llvm::formatv(
"\tImplementation detail: {0}\n", S.ImplementationDetail);
253 OS << llvm::formatv(
"\tCategory: {0}\n",
static_cast<int>(S.Category));
260 const DeclContext *DC = D->getDeclContext();
261 if (
auto *R = dyn_cast_or_null<RecordDecl>(D))
262 if (R->isInjectedClassName())
263 DC = DC->getParent();
265 if (isa<CXXConstructorDecl>(D))
266 DC = DC->getParent();
267 bool InClass =
false;
268 for (; !DC->isFileContext(); DC = DC->getParent()) {
269 if (DC->isFunctionOrMethod())
271 InClass = InClass || DC->isRecord();
278 llvm::to_underlying(Linkage::External))
304 if ((
SemaResult.Kind != CodeCompletionResult::RK_Declaration) &&
305 (
SemaResult.Kind != CodeCompletionResult::RK_Pattern))
307 if (
const NamedDecl *ND =
SemaResult.getDeclaration()) {
315 if (
const auto *NSD = dyn_cast<NamespaceDecl>(ND->getDeclContext())) {
316 if (NSD->isAnonymousNamespace())
327 if (SemaCCResult.Availability == CXAvailability_NotAvailable ||
328 SemaCCResult.Availability == CXAvailability_NotAccessible)
331 if (SemaCCResult.Declaration) {
347 if (SemaCCResult.Declaration)
369 return std::max(0.65, 2.0 * std::pow(0.6,
ScopeDistance / 2.0));
372static std::optional<llvm::StringRef>
375 for (
const auto &
Word : ContextWords->keys())
376 if (
Name.contains_insensitive(
Word))
381SymbolRelevanceSignals::DerivedSignals
460 (
Context == CodeCompletionContext::CCC_DotMemberAccess ||
461 Context == CodeCompletionContext::CCC_ArrowMemberAccess)) {
479 Score *= 11.0 * (1 - S) / (1 + S) + 0.7;
484 Score *= 10.0 * (1 - S) / (1 + S) + 0.7;
492 OS << llvm::formatv(
"=== Symbol relevance: {0}\n", S.evaluateHeuristics());
493 OS << llvm::formatv(
"\tName: {0}\n", S.Name);
494 OS << llvm::formatv(
"\tName match: {0}\n", S.NameMatch);
497 "\tMatching context word: {0}\n",
498 wordMatching(S.Name, S.ContextWords).value_or(
"<none>"));
499 OS << llvm::formatv(
"\tForbidden: {0}\n", S.Forbidden);
500 OS << llvm::formatv(
"\tNeedsFixIts: {0}\n", S.NeedsFixIts);
501 OS << llvm::formatv(
"\tIsInstanceMember: {0}\n", S.IsInstanceMember);
502 OS << llvm::formatv(
"\tInBaseClass: {0}\n", S.InBaseClass);
503 OS << llvm::formatv(
"\tContext: {0}\n", getCompletionKindString(S.Context));
504 OS << llvm::formatv(
"\tQuery type: {0}\n",
static_cast<int>(S.Query));
505 OS << llvm::formatv(
"\tScope: {0}\n",
static_cast<int>(S.Scope));
507 OS << llvm::formatv(
"\tSymbol URI: {0}\n", S.SymbolURI);
508 OS << llvm::formatv(
"\tSymbol scope: {0}\n",
509 S.SymbolScope ? *S.SymbolScope :
"<None>");
512 if (S.FileProximityMatch) {
514 OS << llvm::formatv(
"\tIndex URI proximity: {0} (distance={1})\n",
Score,
517 OS << llvm::formatv(
"\tSema file proximity: {0}\n", S.SemaFileProximityScore);
519 OS << llvm::formatv(
"\tSema says in scope: {0}\n", S.SemaSaysInScope);
520 if (S.ScopeProximityMatch)
521 OS << llvm::formatv(
"\tIndex scope boost: {0}\n",
525 "\tType matched preferred: {0} (Context type: {1}, Symbol type: {2}\n",
526 S.TypeMatchesPreferred, S.HadContextType, S.HadSymbolType);
532 return SymbolQuality * SymbolRelevance;
538 static_assert(std::numeric_limits<float>::is_iec559);
539 constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
542 uint32_t U = llvm::bit_cast<uint32_t>(F);
553 llvm::raw_string_ostream
OS(S);
563 OS << llvm::formatv(
"=== Signature Quality:\n");
564 OS << llvm::formatv(
"\tNumber of parameters: {0}\n", S.NumberOfParameters);
565 OS << llvm::formatv(
"\tNumber of optional parameters: {0}\n",
566 S.NumberOfOptionalParameters);
567 OS << llvm::formatv(
"\tKind: {0}\n", S.Kind);
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
const Symbol * IndexResult
const CodeCompletionResult * SemaResult
std::optional< float > Score
#define MAP(DeclType, Category)
llvm::raw_string_ostream OS
A context is an immutable container for per-request data that must be propagated through layers that ...
static constexpr unsigned Unreachable
Support lookups like FileDistance, but the lookup keys are symbol scopes.
unsigned distance(llvm::StringRef SymbolScope)
unsigned distance(llvm::StringRef URI)
static bool hasUsingDeclInMainFile(const CodeCompletionResult &R)
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
static float fileProximityScore(unsigned FileDistance)
static bool isInstanceMember(const NamedDecl *ND)
std::string sortText(float Score, llvm::StringRef Name)
Returns a string that sorts in the same order as (-Score, Tiebreak), for LSP.
static std::optional< llvm::StringRef > wordMatching(llvm::StringRef Name, const llvm::StringSet<> *ContextWords)
bool isReservedName(llvm::StringRef Name)
Returns true if Name is reserved, like _Foo or __Vector_base.
static float scopeProximityScore(unsigned ScopeDistance)
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
static SymbolRelevanceSignals::AccessibleScope computeScope(const NamedDecl *D)
static SymbolQualitySignals::SymbolCategory categorize(const NamedDecl &ND)
bool hasUnstableLinkage(const Decl *D)
Whether we must avoid computing linkage for D during code completion.
static uint32_t encodeFloat(float F)
static bool hasDeclInMainFile(const Decl &D)
bool isImplementationDetail(const Decl *D)
Returns true if the declaration is considered implementation detail based on heuristics.
float evaluateSymbolAndRelevance(float SymbolQuality, float SymbolRelevance)
Combine symbol quality and relevance into a single score.
std::string printNamespaceScope(const DeclContext &DC)
Returns the first enclosing namespace scope starting from DC.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::DenseMap< SymbolID, unsigned > ReferencedSymbols
Number of occurrences of each symbol present in the file.
llvm::StringMap< unsigned > RelatedNamespaces
Namespaces whose symbols are used in the file, and the number of such distinct symbols.
Attributes of a symbol that affect how much we like it.
float evaluateHeuristics() const
enum clang::clangd::SymbolQualitySignals::SymbolCategory Category
void merge(const CodeCompletionResult &SemaCCResult)
bool ImplementationDetail
Set of derived signals computed by calculateDerivedSignals().
bool NameMatchesContext
Whether Name contains some word from context.
unsigned FileProximityDistance
Min distance between SymbolURI and all the headers included by the TU.
unsigned ScopeProximityDistance
Min distance between SymbolScope and all the available scopes.
Attributes of a symbol-query pair that affect how much we like it.
float NameMatch
0-1+ fuzzy-match score for unqualified name. Must be explicitly assigned.
bool NeedsFixIts
Whether fixits needs to be applied for that completion or not.
DerivedSignals calculateDerivedSignals() const
ScopeDistance * ScopeProximityMatch
unsigned MainFileRefs
Number of references to the candidate in the main file.
llvm::StringRef Name
The name of the symbol (for ContextWords). Must be explicitly assigned.
void computeASTSignals(const CodeCompletionResult &SemaResult)
llvm::StringRef SymbolURI
These are used to calculate proximity between the index symbol and the query.
unsigned ScopeRefsInFile
Number of unique symbols in the main file which belongs to candidate's namespace.
const ASTSignals * MainFileSignals
llvm::StringSet * ContextWords
Lowercase words relevant to the context (e.g. near the completion point).
URIDistance * FileProximityMatch
void merge(const CodeCompletionResult &SemaResult)
float SemaFileProximityScore
FIXME: unify with index proximity score - signals should be source-independent.
enum clang::clangd::SymbolRelevanceSignals::QueryType Query
enum clang::clangd::SymbolRelevanceSignals::AccessibleScope Scope
bool TypeMatchesPreferred
float evaluateHeuristics() const
std::optional< llvm::StringRef > SymbolScope
The class presents a C++ symbol, e.g.
@ Deprecated
Indicates if the symbol is deprecated.
@ ImplementationDetail
Symbol is an implementation detail.
@ VisibleOutsideFile
Symbol is visible to other files (not e.g. a static helper function).