blob: 640f543275da23e02af86d3f989dfcfe20e6ecff [file] [log] [blame]
Kirill Bobyrev8e35f1e2018-08-14 16:03:32 +00001//===--- Quality.cpp ---------------------------------------------*- C++-*-===//
Sam McCallc5707b62018-05-15 17:43:27 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
Kirill Bobyrev8e35f1e2018-08-14 16:03:32 +00008//===----------------------------------------------------------------------===//
Sam McCallc5707b62018-05-15 17:43:27 +00009#include "Quality.h"
Eric Liu48597382018-10-18 12:23:05 +000010#include "AST.h"
Sam McCall3f0243f2018-07-03 08:09:29 +000011#include "FileDistance.h"
Eric Liu09c3c372018-06-15 08:58:12 +000012#include "URI.h"
Sam McCallc5707b62018-05-15 17:43:27 +000013#include "index/Index.h"
Ilya Biryukovf0296462018-06-04 14:50:59 +000014#include "clang/AST/ASTContext.h"
Eric Liu5d2a8072018-07-23 10:56:37 +000015#include "clang/AST/Decl.h"
Eric Liu8944f0e2018-07-05 08:14:04 +000016#include "clang/AST/DeclCXX.h"
Eric Liu5d2a8072018-07-23 10:56:37 +000017#include "clang/AST/DeclTemplate.h"
Sam McCall4a3c69b2018-06-06 08:53:36 +000018#include "clang/AST/DeclVisitor.h"
Sam McCall3f0243f2018-07-03 08:09:29 +000019#include "clang/Basic/CharInfo.h"
Ilya Biryukovf0296462018-06-04 14:50:59 +000020#include "clang/Basic/SourceManager.h"
Sam McCallc5707b62018-05-15 17:43:27 +000021#include "clang/Sema/CodeCompleteConsumer.h"
Eric Liu3fac4ef2018-10-17 11:19:02 +000022#include "llvm/ADT/ArrayRef.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/ADT/StringRef.h"
Eric Liu8944f0e2018-07-05 08:14:04 +000027#include "llvm/Support/Casting.h"
Sam McCallc5707b62018-05-15 17:43:27 +000028#include "llvm/Support/FormatVariadic.h"
29#include "llvm/Support/MathExtras.h"
30#include "llvm/Support/raw_ostream.h"
Eric Liu3fac4ef2018-10-17 11:19:02 +000031#include <algorithm>
Sam McCall3f0243f2018-07-03 08:09:29 +000032#include <cmath>
Sam McCallc5707b62018-05-15 17:43:27 +000033
Sam McCallc008af62018-10-20 15:30:37 +000034using namespace llvm;
Sam McCallc5707b62018-05-15 17:43:27 +000035namespace clang {
36namespace clangd {
Ilya Biryukov74f26552018-07-26 12:05:31 +000037static bool isReserved(StringRef Name) {
Sam McCalle018b362018-06-08 09:36:34 +000038 // FIXME: Should we exclude _Bool and others recognized by the standard?
39 return Name.size() >= 2 && Name[0] == '_' &&
40 (isUppercase(Name[1]) || Name[1] == '_');
41}
Sam McCallc5707b62018-05-15 17:43:27 +000042
Ilya Biryukovf0296462018-06-04 14:50:59 +000043static bool hasDeclInMainFile(const Decl &D) {
44 auto &SourceMgr = D.getASTContext().getSourceManager();
45 for (auto *Redecl : D.redecls()) {
46 auto Loc = SourceMgr.getSpellingLoc(Redecl->getLocation());
47 if (SourceMgr.isWrittenInMainFile(Loc))
48 return true;
49 }
50 return false;
51}
52
Kirill Bobyrev47d7f522018-07-11 14:49:49 +000053static bool hasUsingDeclInMainFile(const CodeCompletionResult &R) {
54 const auto &Context = R.Declaration->getASTContext();
55 const auto &SourceMgr = Context.getSourceManager();
56 if (R.ShadowDecl) {
57 const auto Loc = SourceMgr.getExpansionLoc(R.ShadowDecl->getLocation());
58 if (SourceMgr.isWrittenInMainFile(Loc))
59 return true;
60 }
61 return false;
62}
63
Sam McCall4a3c69b2018-06-06 08:53:36 +000064static SymbolQualitySignals::SymbolCategory categorize(const NamedDecl &ND) {
65 class Switch
66 : public ConstDeclVisitor<Switch, SymbolQualitySignals::SymbolCategory> {
67 public:
68#define MAP(DeclType, Category) \
69 SymbolQualitySignals::SymbolCategory Visit##DeclType(const DeclType *) { \
70 return SymbolQualitySignals::Category; \
71 }
72 MAP(NamespaceDecl, Namespace);
73 MAP(NamespaceAliasDecl, Namespace);
74 MAP(TypeDecl, Type);
75 MAP(TypeAliasTemplateDecl, Type);
76 MAP(ClassTemplateDecl, Type);
Eric Liud7de8112018-07-24 08:51:52 +000077 MAP(CXXConstructorDecl, Constructor);
Sam McCall4a3c69b2018-06-06 08:53:36 +000078 MAP(ValueDecl, Variable);
79 MAP(VarTemplateDecl, Variable);
80 MAP(FunctionDecl, Function);
81 MAP(FunctionTemplateDecl, Function);
82 MAP(Decl, Unknown);
83#undef MAP
84 };
85 return Switch().Visit(&ND);
86}
87
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +000088static SymbolQualitySignals::SymbolCategory
89categorize(const CodeCompletionResult &R) {
Sam McCallc3b5bad2018-06-14 13:42:21 +000090 if (R.Declaration)
91 return categorize(*R.Declaration);
92 if (R.Kind == CodeCompletionResult::RK_Macro)
93 return SymbolQualitySignals::Macro;
94 // Everything else is a keyword or a pattern. Patterns are mostly keywords
95 // too, except a few which we recognize by cursor kind.
96 switch (R.CursorKind) {
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +000097 case CXCursor_CXXMethod:
98 return SymbolQualitySignals::Function;
99 case CXCursor_ModuleImportDecl:
100 return SymbolQualitySignals::Namespace;
101 case CXCursor_MacroDefinition:
102 return SymbolQualitySignals::Macro;
103 case CXCursor_TypeRef:
104 return SymbolQualitySignals::Type;
105 case CXCursor_MemberRef:
106 return SymbolQualitySignals::Variable;
Eric Liud7de8112018-07-24 08:51:52 +0000107 case CXCursor_Constructor:
108 return SymbolQualitySignals::Constructor;
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000109 default:
110 return SymbolQualitySignals::Keyword;
Sam McCallc3b5bad2018-06-14 13:42:21 +0000111 }
112}
113
Sam McCall4a3c69b2018-06-06 08:53:36 +0000114static SymbolQualitySignals::SymbolCategory
115categorize(const index::SymbolInfo &D) {
116 switch (D.Kind) {
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000117 case index::SymbolKind::Namespace:
118 case index::SymbolKind::NamespaceAlias:
119 return SymbolQualitySignals::Namespace;
120 case index::SymbolKind::Macro:
121 return SymbolQualitySignals::Macro;
122 case index::SymbolKind::Enum:
123 case index::SymbolKind::Struct:
124 case index::SymbolKind::Class:
125 case index::SymbolKind::Protocol:
126 case index::SymbolKind::Extension:
127 case index::SymbolKind::Union:
128 case index::SymbolKind::TypeAlias:
129 return SymbolQualitySignals::Type;
130 case index::SymbolKind::Function:
131 case index::SymbolKind::ClassMethod:
132 case index::SymbolKind::InstanceMethod:
133 case index::SymbolKind::StaticMethod:
134 case index::SymbolKind::InstanceProperty:
135 case index::SymbolKind::ClassProperty:
136 case index::SymbolKind::StaticProperty:
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000137 case index::SymbolKind::Destructor:
138 case index::SymbolKind::ConversionFunction:
139 return SymbolQualitySignals::Function;
Eric Liud7de8112018-07-24 08:51:52 +0000140 case index::SymbolKind::Constructor:
141 return SymbolQualitySignals::Constructor;
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000142 case index::SymbolKind::Variable:
143 case index::SymbolKind::Field:
144 case index::SymbolKind::EnumConstant:
145 case index::SymbolKind::Parameter:
146 return SymbolQualitySignals::Variable;
147 case index::SymbolKind::Using:
148 case index::SymbolKind::Module:
149 case index::SymbolKind::Unknown:
150 return SymbolQualitySignals::Unknown;
Sam McCall4a3c69b2018-06-06 08:53:36 +0000151 }
Tim Northover0698e962018-06-06 13:28:49 +0000152 llvm_unreachable("Unknown index::SymbolKind");
Sam McCall4a3c69b2018-06-06 08:53:36 +0000153}
154
Eric Liu5d2a8072018-07-23 10:56:37 +0000155static bool isInstanceMember(const NamedDecl *ND) {
156 if (!ND)
157 return false;
158 if (const auto *TP = dyn_cast<FunctionTemplateDecl>(ND))
159 ND = TP->TemplateDecl::getTemplatedDecl();
160 if (const auto *CM = dyn_cast<CXXMethodDecl>(ND))
161 return !CM->isStatic();
162 return isa<FieldDecl>(ND); // Note that static fields are VarDecl.
163}
164
165static bool isInstanceMember(const index::SymbolInfo &D) {
166 switch (D.Kind) {
167 case index::SymbolKind::InstanceMethod:
168 case index::SymbolKind::InstanceProperty:
169 case index::SymbolKind::Field:
170 return true;
171 default:
172 return false;
173 }
174}
175
Sam McCallc5707b62018-05-15 17:43:27 +0000176void SymbolQualitySignals::merge(const CodeCompletionResult &SemaCCResult) {
Eric Liu6df66002018-09-06 18:52:26 +0000177 Deprecated |= (SemaCCResult.Availability == CXAvailability_Deprecated);
Sam McCallc3b5bad2018-06-14 13:42:21 +0000178 Category = categorize(SemaCCResult);
Sam McCalle018b362018-06-08 09:36:34 +0000179
180 if (SemaCCResult.Declaration) {
Eric Liu48597382018-10-18 12:23:05 +0000181 ImplementationDetail |= isImplementationDetail(SemaCCResult.Declaration);
Sam McCalle018b362018-06-08 09:36:34 +0000182 if (auto *ID = SemaCCResult.Declaration->getIdentifier())
Ilya Biryukov74f26552018-07-26 12:05:31 +0000183 ReservedName = ReservedName || isReserved(ID->getName());
Sam McCalle018b362018-06-08 09:36:34 +0000184 } else if (SemaCCResult.Kind == CodeCompletionResult::RK_Macro)
Ilya Biryukov74f26552018-07-26 12:05:31 +0000185 ReservedName = ReservedName || isReserved(SemaCCResult.Macro->getName());
Sam McCallc5707b62018-05-15 17:43:27 +0000186}
187
188void SymbolQualitySignals::merge(const Symbol &IndexResult) {
Eric Liu6df66002018-09-06 18:52:26 +0000189 Deprecated |= (IndexResult.Flags & Symbol::Deprecated);
Eric Liu48597382018-10-18 12:23:05 +0000190 ImplementationDetail |= (IndexResult.Flags & Symbol::ImplementationDetail);
Sam McCallc5707b62018-05-15 17:43:27 +0000191 References = std::max(IndexResult.References, References);
Sam McCall4a3c69b2018-06-06 08:53:36 +0000192 Category = categorize(IndexResult.SymInfo);
Ilya Biryukov74f26552018-07-26 12:05:31 +0000193 ReservedName = ReservedName || isReserved(IndexResult.Name);
Sam McCallc5707b62018-05-15 17:43:27 +0000194}
195
196float SymbolQualitySignals::evaluate() const {
197 float Score = 1;
198
199 // This avoids a sharp gradient for tail symbols, and also neatly avoids the
200 // question of whether 0 references means a bad symbol or missing data.
Eric Liu84bd5db2018-07-25 11:26:35 +0000201 if (References >= 10) {
202 // Use a sigmoid style boosting function, which flats out nicely for large
203 // numbers (e.g. 2.58 for 1M refererences).
204 // The following boosting function is equivalent to:
205 // m = 0.06
206 // f = 12.0
207 // boost = f * sigmoid(m * std::log(References)) - 0.5 * f + 0.59
208 // Sample data points: (10, 1.00), (100, 1.41), (1000, 1.82),
209 // (10K, 2.21), (100K, 2.58), (1M, 2.94)
Ilya Biryukov74f26552018-07-26 12:05:31 +0000210 float S = std::pow(References, -0.06);
211 Score *= 6.0 * (1 - S) / (1 + S) + 0.59;
Eric Liu84bd5db2018-07-25 11:26:35 +0000212 }
Sam McCallc5707b62018-05-15 17:43:27 +0000213
Sam McCallc5707b62018-05-15 17:43:27 +0000214 if (Deprecated)
Aaron Ballman215e4712018-05-18 13:18:41 +0000215 Score *= 0.1f;
Sam McCalle018b362018-06-08 09:36:34 +0000216 if (ReservedName)
217 Score *= 0.1f;
Eric Liu48597382018-10-18 12:23:05 +0000218 if (ImplementationDetail)
219 Score *= 0.2f;
Sam McCallc5707b62018-05-15 17:43:27 +0000220
Sam McCall4a3c69b2018-06-06 08:53:36 +0000221 switch (Category) {
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000222 case Keyword: // Often relevant, but misses most signals.
223 Score *= 4; // FIXME: important keywords should have specific boosts.
224 break;
225 case Type:
226 case Function:
227 case Variable:
228 Score *= 1.1f;
229 break;
230 case Namespace:
231 Score *= 0.8f;
232 break;
233 case Macro:
Eric Liuf592d282018-09-05 07:40:38 +0000234 Score *= 0.5f;
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000235 break;
236 case Unknown:
Eric Liud7de8112018-07-24 08:51:52 +0000237 case Constructor: // No boost constructors so they are after class types.
Kirill Bobyrev7cf29bc2018-07-05 09:37:26 +0000238 break;
Sam McCall4a3c69b2018-06-06 08:53:36 +0000239 }
240
Sam McCallc5707b62018-05-15 17:43:27 +0000241 return Score;
242}
243
244raw_ostream &operator<<(raw_ostream &OS, const SymbolQualitySignals &S) {
245 OS << formatv("=== Symbol quality: {0}\n", S.evaluate());
Sam McCallc5707b62018-05-15 17:43:27 +0000246 OS << formatv("\tReferences: {0}\n", S.References);
247 OS << formatv("\tDeprecated: {0}\n", S.Deprecated);
Sam McCalle018b362018-06-08 09:36:34 +0000248 OS << formatv("\tReserved name: {0}\n", S.ReservedName);
Sam McCall4a3c69b2018-06-06 08:53:36 +0000249 OS << formatv("\tCategory: {0}\n", static_cast<int>(S.Category));
Sam McCallc5707b62018-05-15 17:43:27 +0000250 return OS;
251}
252
Sam McCalld9b54f02018-06-05 16:30:25 +0000253static SymbolRelevanceSignals::AccessibleScope
Ilya Biryukov74f26552018-07-26 12:05:31 +0000254computeScope(const NamedDecl *D) {
Sam McCallabe37372018-06-27 11:43:54 +0000255 // Injected "Foo" within the class "Foo" has file scope, not class scope.
256 const DeclContext *DC = D->getDeclContext();
257 if (auto *R = dyn_cast_or_null<RecordDecl>(D))
258 if (R->isInjectedClassName())
259 DC = DC->getParent();
Eric Liu8944f0e2018-07-05 08:14:04 +0000260 // Class constructor should have the same scope as the class.
Simon Pilgrim4a032012018-07-05 09:35:12 +0000261 if (isa<CXXConstructorDecl>(D))
Eric Liu8944f0e2018-07-05 08:14:04 +0000262 DC = DC->getParent();
Sam McCall89f52932018-06-05 18:00:48 +0000263 bool InClass = false;
Sam McCallabe37372018-06-27 11:43:54 +0000264 for (; !DC->isFileContext(); DC = DC->getParent()) {
Sam McCalld9b54f02018-06-05 16:30:25 +0000265 if (DC->isFunctionOrMethod())
266 return SymbolRelevanceSignals::FunctionScope;
267 InClass = InClass || DC->isRecord();
268 }
269 if (InClass)
270 return SymbolRelevanceSignals::ClassScope;
271 // This threshold could be tweaked, e.g. to treat module-visible as global.
Sam McCallabe37372018-06-27 11:43:54 +0000272 if (D->getLinkageInternal() < ExternalLinkage)
Sam McCalld9b54f02018-06-05 16:30:25 +0000273 return SymbolRelevanceSignals::FileScope;
274 return SymbolRelevanceSignals::GlobalScope;
275}
276
277void SymbolRelevanceSignals::merge(const Symbol &IndexResult) {
278 // FIXME: Index results always assumed to be at global scope. If Scope becomes
279 // relevant to non-completion requests, we should recognize class members etc.
Eric Liu09c3c372018-06-15 08:58:12 +0000280
281 SymbolURI = IndexResult.CanonicalDeclaration.FileURI;
Eric Liu3fac4ef2018-10-17 11:19:02 +0000282 SymbolScope = IndexResult.Scope;
Eric Liu5d2a8072018-07-23 10:56:37 +0000283 IsInstanceMember |= isInstanceMember(IndexResult.SymInfo);
Sam McCalld9b54f02018-06-05 16:30:25 +0000284}
285
Sam McCallc5707b62018-05-15 17:43:27 +0000286void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) {
287 if (SemaCCResult.Availability == CXAvailability_NotAvailable ||
288 SemaCCResult.Availability == CXAvailability_NotAccessible)
289 Forbidden = true;
Ilya Biryukovf0296462018-06-04 14:50:59 +0000290
291 if (SemaCCResult.Declaration) {
Eric Liu3fac4ef2018-10-17 11:19:02 +0000292 SemaSaysInScope = true;
Eric Liu09c3c372018-06-15 08:58:12 +0000293 // We boost things that have decls in the main file. We give a fixed score
294 // for all other declarations in sema as they are already included in the
295 // translation unit.
Kirill Bobyrev47d7f522018-07-11 14:49:49 +0000296 float DeclProximity = (hasDeclInMainFile(*SemaCCResult.Declaration) ||
297 hasUsingDeclInMainFile(SemaCCResult))
298 ? 1.0
299 : 0.6;
Eric Liu3fac4ef2018-10-17 11:19:02 +0000300 SemaFileProximityScore = std::max(DeclProximity, SemaFileProximityScore);
Eric Liu5d2a8072018-07-23 10:56:37 +0000301 IsInstanceMember |= isInstanceMember(SemaCCResult.Declaration);
Eric Liu52a11b52018-10-24 13:45:17 +0000302 InBaseClass |= SemaCCResult.InBaseClass;
Ilya Biryukovf0296462018-06-04 14:50:59 +0000303 }
Sam McCalld9b54f02018-06-05 16:30:25 +0000304
305 // Declarations are scoped, others (like macros) are assumed global.
Sam McCall661d89c2018-06-05 17:58:12 +0000306 if (SemaCCResult.Declaration)
Ilya Biryukov74f26552018-07-26 12:05:31 +0000307 Scope = std::min(Scope, computeScope(SemaCCResult.Declaration));
Kadir Cetinkaya2f84d912018-08-08 08:59:29 +0000308
309 NeedsFixIts = !SemaCCResult.FixIts.empty();
Sam McCallc5707b62018-05-15 17:43:27 +0000310}
311
Sam McCallc008af62018-10-20 15:30:37 +0000312static std::pair<float, unsigned> uriProximity(StringRef SymbolURI,
Eric Liu3fac4ef2018-10-17 11:19:02 +0000313 URIDistance *D) {
Sam McCall3f0243f2018-07-03 08:09:29 +0000314 if (!D || SymbolURI.empty())
315 return {0.f, 0u};
316 unsigned Distance = D->distance(SymbolURI);
317 // Assume approximately default options are used for sensible scoring.
318 return {std::exp(Distance * -0.4f / FileDistanceOptions().UpCost), Distance};
319}
320
Eric Liu3fac4ef2018-10-17 11:19:02 +0000321static float scopeBoost(ScopeDistance &Distance,
Sam McCallc008af62018-10-20 15:30:37 +0000322 Optional<StringRef> SymbolScope) {
Eric Liu3fac4ef2018-10-17 11:19:02 +0000323 if (!SymbolScope)
324 return 1;
325 auto D = Distance.distance(*SymbolScope);
326 if (D == FileDistance::Unreachable)
Simon Pilgrim15ee23f2018-10-20 13:20:26 +0000327 return 0.4f;
Eric Liu3fac4ef2018-10-17 11:19:02 +0000328 return std::max(0.5, 2.0 * std::pow(0.6, D / 2.0));
329}
330
Sam McCallc5707b62018-05-15 17:43:27 +0000331float SymbolRelevanceSignals::evaluate() const {
Sam McCalld9b54f02018-06-05 16:30:25 +0000332 float Score = 1;
333
Sam McCallc5707b62018-05-15 17:43:27 +0000334 if (Forbidden)
335 return 0;
Ilya Biryukovf0296462018-06-04 14:50:59 +0000336
Sam McCalld9b54f02018-06-05 16:30:25 +0000337 Score *= NameMatch;
338
Eric Liu3fac4ef2018-10-17 11:19:02 +0000339 // File proximity scores are [0,1] and we translate them into a multiplier in
340 // the range from 1 to 3.
341 Score *= 1 + 2 * std::max(uriProximity(SymbolURI, FileProximityMatch).first,
342 SemaFileProximityScore);
343
344 if (ScopeProximityMatch)
345 // Use a constant scope boost for sema results, as scopes of sema results
346 // can be tricky (e.g. class/function scope). Set to the max boost as we
347 // don't load top-level symbols from the preamble and sema results are
348 // always in the accessible scope.
349 Score *=
350 SemaSaysInScope ? 2.0 : scopeBoost(*ScopeProximityMatch, SymbolScope);
Sam McCalld9b54f02018-06-05 16:30:25 +0000351
352 // Symbols like local variables may only be referenced within their scope.
353 // Conversely if we're in that scope, it's likely we'll reference them.
354 if (Query == CodeComplete) {
355 // The narrower the scope where a symbol is visible, the more likely it is
356 // to be relevant when it is available.
357 switch (Scope) {
358 case GlobalScope:
359 break;
360 case FileScope:
361 Score *= 1.5;
Sam McCallc22c9aa2018-06-07 08:16:36 +0000362 break;
Sam McCalld9b54f02018-06-05 16:30:25 +0000363 case ClassScope:
364 Score *= 2;
Sam McCallc22c9aa2018-06-07 08:16:36 +0000365 break;
Sam McCalld9b54f02018-06-05 16:30:25 +0000366 case FunctionScope:
367 Score *= 4;
Sam McCallc22c9aa2018-06-07 08:16:36 +0000368 break;
Sam McCalld9b54f02018-06-05 16:30:25 +0000369 }
370 }
371
Eric Liu5d2a8072018-07-23 10:56:37 +0000372 // Penalize non-instance members when they are accessed via a class instance.
373 if (!IsInstanceMember &&
374 (Context == CodeCompletionContext::CCC_DotMemberAccess ||
375 Context == CodeCompletionContext::CCC_ArrowMemberAccess)) {
Simon Pilgrim53691682018-10-24 19:31:24 +0000376 Score *= 0.2f;
Eric Liu5d2a8072018-07-23 10:56:37 +0000377 }
378
Eric Liu52a11b52018-10-24 13:45:17 +0000379 if (InBaseClass)
Simon Pilgrim53691682018-10-24 19:31:24 +0000380 Score *= 0.5f;
Eric Liu52a11b52018-10-24 13:45:17 +0000381
Kadir Cetinkaya2f84d912018-08-08 08:59:29 +0000382 // Penalize for FixIts.
383 if (NeedsFixIts)
Simon Pilgrim53691682018-10-24 19:31:24 +0000384 Score *= 0.5f;
Kadir Cetinkaya2f84d912018-08-08 08:59:29 +0000385
Ilya Biryukovf0296462018-06-04 14:50:59 +0000386 return Score;
Sam McCallc5707b62018-05-15 17:43:27 +0000387}
Eric Liu09c3c372018-06-15 08:58:12 +0000388
Sam McCallc5707b62018-05-15 17:43:27 +0000389raw_ostream &operator<<(raw_ostream &OS, const SymbolRelevanceSignals &S) {
390 OS << formatv("=== Symbol relevance: {0}\n", S.evaluate());
391 OS << formatv("\tName match: {0}\n", S.NameMatch);
392 OS << formatv("\tForbidden: {0}\n", S.Forbidden);
Kadir Cetinkaya2f84d912018-08-08 08:59:29 +0000393 OS << formatv("\tNeedsFixIts: {0}\n", S.NeedsFixIts);
Eric Liu5d2a8072018-07-23 10:56:37 +0000394 OS << formatv("\tIsInstanceMember: {0}\n", S.IsInstanceMember);
395 OS << formatv("\tContext: {0}\n", getCompletionKindString(S.Context));
Sam McCall661d89c2018-06-05 17:58:12 +0000396 OS << formatv("\tQuery type: {0}\n", static_cast<int>(S.Query));
397 OS << formatv("\tScope: {0}\n", static_cast<int>(S.Scope));
Eric Liu3fac4ef2018-10-17 11:19:02 +0000398
399 OS << formatv("\tSymbol URI: {0}\n", S.SymbolURI);
400 OS << formatv("\tSymbol scope: {0}\n",
401 S.SymbolScope ? *S.SymbolScope : "<None>");
402
403 if (S.FileProximityMatch) {
404 auto Score = uriProximity(S.SymbolURI, S.FileProximityMatch);
405 OS << formatv("\tIndex URI proximity: {0} (distance={1})\n", Score.first,
406 Score.second);
407 }
408 OS << formatv("\tSema file proximity: {0}\n", S.SemaFileProximityScore);
409
410 OS << formatv("\tSema says in scope: {0}\n", S.SemaSaysInScope);
411 if (S.ScopeProximityMatch)
412 OS << formatv("\tIndex scope boost: {0}\n",
413 scopeBoost(*S.ScopeProximityMatch, S.SymbolScope));
414
Sam McCallc5707b62018-05-15 17:43:27 +0000415 return OS;
416}
417
418float evaluateSymbolAndRelevance(float SymbolQuality, float SymbolRelevance) {
419 return SymbolQuality * SymbolRelevance;
420}
421
422// Produces an integer that sorts in the same order as F.
423// That is: a < b <==> encodeFloat(a) < encodeFloat(b).
424static uint32_t encodeFloat(float F) {
425 static_assert(std::numeric_limits<float>::is_iec559, "");
426 constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
427
428 // Get the bits of the float. Endianness is the same as for integers.
429 uint32_t U = FloatToBits(F);
430 // IEEE 754 floats compare like sign-magnitude integers.
431 if (U & TopBit) // Negative float.
432 return 0 - U; // Map onto the low half of integers, order reversed.
433 return U + TopBit; // Positive floats map onto the high half of integers.
434}
435
Sam McCallc008af62018-10-20 15:30:37 +0000436std::string sortText(float Score, StringRef Name) {
Sam McCallc5707b62018-05-15 17:43:27 +0000437 // We convert -Score to an integer, and hex-encode for readability.
438 // Example: [0.5, "foo"] -> "41000000foo"
439 std::string S;
Sam McCallc008af62018-10-20 15:30:37 +0000440 raw_string_ostream OS(S);
441 write_hex(OS, encodeFloat(-Score), HexPrintStyle::Lower,
Sam McCallc5707b62018-05-15 17:43:27 +0000442 /*Width=*/2 * sizeof(Score));
443 OS << Name;
444 OS.flush();
445 return S;
446}
447
Sam McCallc008af62018-10-20 15:30:37 +0000448raw_ostream &operator<<(raw_ostream &OS, const SignatureQualitySignals &S) {
Kadir Cetinkayae486e372018-08-13 08:40:05 +0000449 OS << formatv("=== Signature Quality:\n");
450 OS << formatv("\tNumber of parameters: {0}\n", S.NumberOfParameters);
451 OS << formatv("\tNumber of optional parameters: {0}\n",
452 S.NumberOfOptionalParameters);
453 OS << formatv("\tContains active parameter: {0}\n",
454 S.ContainsActiveParameter);
455 OS << formatv("\tKind: {0}\n", S.Kind);
456 return OS;
457}
458
Sam McCallc5707b62018-05-15 17:43:27 +0000459} // namespace clangd
460} // namespace clang