blob: e2fc5019708fa69667a5bccd9e7de1764e45e1c1 [file] [log] [blame]
Sam McCall98775c52017-12-04 13:49:59 +00001//===--- CodeComplete.cpp ---------------------------------------*- C++-*-===//
2//
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//
8//===---------------------------------------------------------------------===//
9//
10// AST-based completions are provided using the completion hooks in Sema.
11//
12// Signature help works in a similar way as code completion, but it is simpler
13// as there are typically fewer candidates.
14//
15//===---------------------------------------------------------------------===//
16
17#include "CodeComplete.h"
Eric Liu63696e12017-12-20 17:24:31 +000018#include "CodeCompletionStrings.h"
Sam McCall98775c52017-12-04 13:49:59 +000019#include "Compiler.h"
Sam McCall84652cc2018-01-12 16:16:09 +000020#include "FuzzyMatch.h"
Eric Liu6f648df2017-12-19 16:50:37 +000021#include "Logger.h"
22#include "index/Index.h"
Sam McCall98775c52017-12-04 13:49:59 +000023#include "clang/Frontend/CompilerInstance.h"
24#include "clang/Frontend/FrontendActions.h"
25#include "clang/Sema/CodeCompleteConsumer.h"
26#include "clang/Sema/Sema.h"
Haojian Wuba28e9a2018-01-10 14:44:34 +000027#include "llvm/Support/Format.h"
Sam McCall98775c52017-12-04 13:49:59 +000028#include <queue>
29
30namespace clang {
31namespace clangd {
32namespace {
33
Eric Liu6f648df2017-12-19 16:50:37 +000034CompletionItemKind toCompletionItemKind(CXCursorKind CursorKind) {
Sam McCall98775c52017-12-04 13:49:59 +000035 switch (CursorKind) {
36 case CXCursor_MacroInstantiation:
37 case CXCursor_MacroDefinition:
38 return CompletionItemKind::Text;
39 case CXCursor_CXXMethod:
Eric Liu6f648df2017-12-19 16:50:37 +000040 case CXCursor_Destructor:
Sam McCall98775c52017-12-04 13:49:59 +000041 return CompletionItemKind::Method;
42 case CXCursor_FunctionDecl:
43 case CXCursor_FunctionTemplate:
44 return CompletionItemKind::Function;
45 case CXCursor_Constructor:
Sam McCall98775c52017-12-04 13:49:59 +000046 return CompletionItemKind::Constructor;
47 case CXCursor_FieldDecl:
48 return CompletionItemKind::Field;
49 case CXCursor_VarDecl:
50 case CXCursor_ParmDecl:
51 return CompletionItemKind::Variable;
Eric Liu6f648df2017-12-19 16:50:37 +000052 // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
53 // protocol.
Sam McCall98775c52017-12-04 13:49:59 +000054 case CXCursor_StructDecl:
Eric Liu6f648df2017-12-19 16:50:37 +000055 case CXCursor_ClassDecl:
Sam McCall98775c52017-12-04 13:49:59 +000056 case CXCursor_UnionDecl:
57 case CXCursor_ClassTemplate:
58 case CXCursor_ClassTemplatePartialSpecialization:
59 return CompletionItemKind::Class;
60 case CXCursor_Namespace:
61 case CXCursor_NamespaceAlias:
62 case CXCursor_NamespaceRef:
63 return CompletionItemKind::Module;
64 case CXCursor_EnumConstantDecl:
65 return CompletionItemKind::Value;
66 case CXCursor_EnumDecl:
67 return CompletionItemKind::Enum;
Eric Liu6f648df2017-12-19 16:50:37 +000068 // FIXME(ioeric): figure out whether reference is the right type for aliases.
Sam McCall98775c52017-12-04 13:49:59 +000069 case CXCursor_TypeAliasDecl:
70 case CXCursor_TypeAliasTemplateDecl:
71 case CXCursor_TypedefDecl:
72 case CXCursor_MemberRef:
73 case CXCursor_TypeRef:
74 return CompletionItemKind::Reference;
75 default:
76 return CompletionItemKind::Missing;
77 }
78}
79
Eric Liu6f648df2017-12-19 16:50:37 +000080CompletionItemKind
81toCompletionItemKind(CodeCompletionResult::ResultKind ResKind,
82 CXCursorKind CursorKind) {
Sam McCall98775c52017-12-04 13:49:59 +000083 switch (ResKind) {
84 case CodeCompletionResult::RK_Declaration:
Eric Liu6f648df2017-12-19 16:50:37 +000085 return toCompletionItemKind(CursorKind);
Sam McCall98775c52017-12-04 13:49:59 +000086 case CodeCompletionResult::RK_Keyword:
87 return CompletionItemKind::Keyword;
88 case CodeCompletionResult::RK_Macro:
89 return CompletionItemKind::Text; // unfortunately, there's no 'Macro'
90 // completion items in LSP.
91 case CodeCompletionResult::RK_Pattern:
92 return CompletionItemKind::Snippet;
93 }
94 llvm_unreachable("Unhandled CodeCompletionResult::ResultKind.");
95}
96
Eric Liu6f648df2017-12-19 16:50:37 +000097CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
98 using SK = index::SymbolKind;
99 switch (Kind) {
100 case SK::Unknown:
101 return CompletionItemKind::Missing;
102 case SK::Module:
103 case SK::Namespace:
104 case SK::NamespaceAlias:
105 return CompletionItemKind::Module;
106 case SK::Macro:
107 return CompletionItemKind::Text;
108 case SK::Enum:
109 return CompletionItemKind::Enum;
110 // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
111 // protocol.
112 case SK::Struct:
113 case SK::Class:
114 case SK::Protocol:
115 case SK::Extension:
116 case SK::Union:
117 return CompletionItemKind::Class;
118 // FIXME(ioeric): figure out whether reference is the right type for aliases.
119 case SK::TypeAlias:
120 case SK::Using:
121 return CompletionItemKind::Reference;
122 case SK::Function:
123 // FIXME(ioeric): this should probably be an operator. This should be fixed
124 // when `Operator` is support type in the protocol.
125 case SK::ConversionFunction:
126 return CompletionItemKind::Function;
127 case SK::Variable:
128 case SK::Parameter:
129 return CompletionItemKind::Variable;
130 case SK::Field:
131 return CompletionItemKind::Field;
132 // FIXME(ioeric): use LSP enum constant when it is supported in the protocol.
133 case SK::EnumConstant:
134 return CompletionItemKind::Value;
135 case SK::InstanceMethod:
136 case SK::ClassMethod:
137 case SK::StaticMethod:
138 case SK::Destructor:
139 return CompletionItemKind::Method;
140 case SK::InstanceProperty:
141 case SK::ClassProperty:
142 case SK::StaticProperty:
143 return CompletionItemKind::Property;
144 case SK::Constructor:
145 return CompletionItemKind::Constructor;
146 }
147 llvm_unreachable("Unhandled clang::index::SymbolKind.");
148}
149
Sam McCall98775c52017-12-04 13:49:59 +0000150/// Get the optional chunk as a string. This function is possibly recursive.
151///
152/// The parameter info for each parameter is appended to the Parameters.
153std::string
154getOptionalParameters(const CodeCompletionString &CCS,
155 std::vector<ParameterInformation> &Parameters) {
156 std::string Result;
157 for (const auto &Chunk : CCS) {
158 switch (Chunk.Kind) {
159 case CodeCompletionString::CK_Optional:
160 assert(Chunk.Optional &&
161 "Expected the optional code completion string to be non-null.");
162 Result += getOptionalParameters(*Chunk.Optional, Parameters);
163 break;
164 case CodeCompletionString::CK_VerticalSpace:
165 break;
166 case CodeCompletionString::CK_Placeholder:
167 // A string that acts as a placeholder for, e.g., a function call
168 // argument.
169 // Intentional fallthrough here.
170 case CodeCompletionString::CK_CurrentParameter: {
171 // A piece of text that describes the parameter that corresponds to
172 // the code-completion location within a function call, message send,
173 // macro invocation, etc.
174 Result += Chunk.Text;
175 ParameterInformation Info;
176 Info.label = Chunk.Text;
177 Parameters.push_back(std::move(Info));
178 break;
179 }
180 default:
181 Result += Chunk.Text;
182 break;
183 }
184 }
185 return Result;
186}
187
Sam McCall98775c52017-12-04 13:49:59 +0000188/// A scored code completion result.
189/// It may be promoted to a CompletionItem if it's among the top-ranked results.
Sam McCall84652cc2018-01-12 16:16:09 +0000190///
191/// We score candidates by multiplying the symbolScore ("quality" of the result)
192/// with the filterScore (how well it matched the query).
193/// This is sensitive to the distribution of both component scores!
Sam McCall98775c52017-12-04 13:49:59 +0000194struct CompletionCandidate {
Sam McCall84652cc2018-01-12 16:16:09 +0000195 CompletionCandidate(CodeCompletionResult &Result, float FilterScore)
196 : Result(&Result) {
197 Scores.symbolScore = score(Result); // Higher is better.
198 Scores.filterScore = FilterScore; // 0-1, higher is better.
199 Scores.finalScore = Scores.symbolScore * Scores.filterScore;
200 }
Sam McCall98775c52017-12-04 13:49:59 +0000201
202 CodeCompletionResult *Result;
Sam McCall84652cc2018-01-12 16:16:09 +0000203 CompletionItemScores Scores;
Sam McCall98775c52017-12-04 13:49:59 +0000204
205 // Comparison reflects rank: better candidates are smaller.
206 bool operator<(const CompletionCandidate &C) const {
Sam McCall84652cc2018-01-12 16:16:09 +0000207 if (Scores.finalScore != C.Scores.finalScore)
208 return Scores.finalScore > C.Scores.finalScore;
Sam McCall98775c52017-12-04 13:49:59 +0000209 return *Result < *C.Result;
210 }
211
212 // Returns a string that sorts in the same order as operator<, for LSP.
213 // Conceptually, this is [-Score, Name]. We convert -Score to an integer, and
214 // hex-encode it for readability. Example: [0.5, "foo"] -> "41000000foo"
215 std::string sortText() const {
216 std::string S, NameStorage;
217 llvm::raw_string_ostream OS(S);
Sam McCall84652cc2018-01-12 16:16:09 +0000218 write_hex(OS, encodeFloat(-Scores.finalScore), llvm::HexPrintStyle::Lower,
219 /*Width=*/2 * sizeof(Scores.finalScore));
Sam McCall98775c52017-12-04 13:49:59 +0000220 OS << Result->getOrderedName(NameStorage);
221 return OS.str();
222 }
223
224private:
225 static float score(const CodeCompletionResult &Result) {
226 // Priority 80 is a really bad score.
227 float Score = 1 - std::min<float>(80, Result.Priority) / 80;
228
229 switch (static_cast<CXAvailabilityKind>(Result.Availability)) {
230 case CXAvailability_Available:
231 // No penalty.
232 break;
233 case CXAvailability_Deprecated:
234 Score *= 0.1f;
235 break;
236 case CXAvailability_NotAccessible:
237 case CXAvailability_NotAvailable:
238 Score = 0;
239 break;
240 }
241 return Score;
242 }
243
244 // Produces an integer that sorts in the same order as F.
245 // That is: a < b <==> encodeFloat(a) < encodeFloat(b).
246 static uint32_t encodeFloat(float F) {
247 static_assert(std::numeric_limits<float>::is_iec559, "");
248 static_assert(sizeof(float) == sizeof(uint32_t), "");
249 constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
250
251 // Get the bits of the float. Endianness is the same as for integers.
252 uint32_t U;
253 memcpy(&U, &F, sizeof(float));
254 // IEEE 754 floats compare like sign-magnitude integers.
255 if (U & TopBit) // Negative float.
256 return 0 - U; // Map onto the low half of integers, order reversed.
257 return U + TopBit; // Positive floats map onto the high half of integers.
258 }
259};
260
Eric Liu6f648df2017-12-19 16:50:37 +0000261/// \brief Information about the scope specifier in the qualified-id code
262/// completion (e.g. "ns::ab?").
263struct SpecifiedScope {
264 /// The scope specifier as written. For example, for completion "ns::ab?", the
265 /// written scope specifier is "ns".
266 std::string Written;
267 // If this scope specifier is recognized in Sema (e.g. as a namespace
268 // context), this will be set to the fully qualfied name of the corresponding
269 // context.
270 std::string Resolved;
271};
272
273/// \brief Information from sema about (parital) symbol names to be completed.
274/// For example, for completion "ns::ab^", this stores the scope specifier
275/// "ns::" and the completion filter text "ab".
276struct NameToComplete {
277 // The partial identifier being completed, without qualifier.
278 std::string Filter;
279
280 /// This is set if the completion is for qualified IDs, e.g. "abc::x^".
281 llvm::Optional<SpecifiedScope> SSInfo;
282};
283
284SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS);
285
Sam McCall98775c52017-12-04 13:49:59 +0000286class CompletionItemsCollector : public CodeCompleteConsumer {
287public:
288 CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
Eric Liu6f648df2017-12-19 16:50:37 +0000289 CompletionList &Items, NameToComplete &CompletedName)
Sam McCall98775c52017-12-04 13:49:59 +0000290 : CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
291 /*OutputIsBinary=*/false),
292 ClangdOpts(CodeCompleteOpts), Items(Items),
293 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
Eric Liu63696e12017-12-20 17:24:31 +0000294 CCTUInfo(Allocator), CompletedName(CompletedName),
295 EnableSnippets(CodeCompleteOpts.EnableSnippets) {}
Sam McCall98775c52017-12-04 13:49:59 +0000296
297 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
298 CodeCompletionResult *Results,
299 unsigned NumResults) override final {
Sam McCall84652cc2018-01-12 16:16:09 +0000300 FuzzyMatcher Filter(S.getPreprocessor().getCodeCompletionFilter());
Eric Liu6f648df2017-12-19 16:50:37 +0000301 if (auto SS = Context.getCXXScopeSpecifier())
302 CompletedName.SSInfo = extraCompletionScope(S, **SS);
303
304 CompletedName.Filter = S.getPreprocessor().getCodeCompletionFilter();
Sam McCall98775c52017-12-04 13:49:59 +0000305 std::priority_queue<CompletionCandidate> Candidates;
306 for (unsigned I = 0; I < NumResults; ++I) {
307 auto &Result = Results[I];
Ilya Biryukovf60bf342018-01-10 13:51:09 +0000308 // We drop hidden items, as they cannot be found by the lookup after
309 // inserting the corresponding completion item and only produce noise and
310 // duplicates in the completion list. However, there is one exception. If
311 // Result has a Qualifier which is non-informative, we can refer to an
312 // item by adding that qualifier, so we don't filter out this item.
313 if (Result.Hidden && (!Result.Qualifier || Result.QualifierIsInformative))
314 continue;
Sam McCall98775c52017-12-04 13:49:59 +0000315 if (!ClangdOpts.IncludeIneligibleResults &&
316 (Result.Availability == CXAvailability_NotAvailable ||
317 Result.Availability == CXAvailability_NotAccessible))
318 continue;
Sam McCall84652cc2018-01-12 16:16:09 +0000319 auto FilterScore = fuzzyMatch(S, Context, Filter, Result);
320 if (!FilterScore)
Sam McCall98775c52017-12-04 13:49:59 +0000321 continue;
Sam McCall84652cc2018-01-12 16:16:09 +0000322 Candidates.emplace(Result, *FilterScore);
Sam McCall98775c52017-12-04 13:49:59 +0000323 if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
324 Candidates.pop();
325 Items.isIncomplete = true;
326 }
327 }
328 while (!Candidates.empty()) {
329 auto &Candidate = Candidates.top();
330 const auto *CCS = Candidate.Result->CreateCodeCompletionString(
331 S, Context, *Allocator, CCTUInfo,
332 CodeCompleteOpts.IncludeBriefComments);
333 assert(CCS && "Expected the CodeCompletionString to be non-null");
334 Items.items.push_back(ProcessCodeCompleteResult(Candidate, *CCS));
335 Candidates.pop();
336 }
337 std::reverse(Items.items.begin(), Items.items.end());
338 }
339
340 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
341
342 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
343
344private:
Sam McCall84652cc2018-01-12 16:16:09 +0000345 llvm::Optional<float> fuzzyMatch(Sema &S, const CodeCompletionContext &CCCtx,
346 FuzzyMatcher &Filter,
347 CodeCompletionResult Result) {
Sam McCall98775c52017-12-04 13:49:59 +0000348 switch (Result.Kind) {
349 case CodeCompletionResult::RK_Declaration:
350 if (auto *ID = Result.Declaration->getIdentifier())
Sam McCall84652cc2018-01-12 16:16:09 +0000351 return Filter.match(ID->getName());
Sam McCall98775c52017-12-04 13:49:59 +0000352 break;
353 case CodeCompletionResult::RK_Keyword:
Sam McCall84652cc2018-01-12 16:16:09 +0000354 return Filter.match(Result.Keyword);
Sam McCall98775c52017-12-04 13:49:59 +0000355 case CodeCompletionResult::RK_Macro:
Sam McCall84652cc2018-01-12 16:16:09 +0000356 return Filter.match(Result.Macro->getName());
Sam McCall98775c52017-12-04 13:49:59 +0000357 case CodeCompletionResult::RK_Pattern:
Sam McCall84652cc2018-01-12 16:16:09 +0000358 return Filter.match(Result.Pattern->getTypedText());
Sam McCall98775c52017-12-04 13:49:59 +0000359 }
360 auto *CCS = Result.CreateCodeCompletionString(
361 S, CCCtx, *Allocator, CCTUInfo, /*IncludeBriefComments=*/false);
Sam McCall84652cc2018-01-12 16:16:09 +0000362 return Filter.match(CCS->getTypedText());
Sam McCall98775c52017-12-04 13:49:59 +0000363 }
364
365 CompletionItem
366 ProcessCodeCompleteResult(const CompletionCandidate &Candidate,
367 const CodeCompletionString &CCS) const {
368
369 // Adjust this to InsertTextFormat::Snippet iff we encounter a
370 // CK_Placeholder chunk in SnippetCompletionItemsCollector.
371 CompletionItem Item;
Sam McCall98775c52017-12-04 13:49:59 +0000372
373 Item.documentation = getDocumentation(CCS);
374 Item.sortText = Candidate.sortText();
Sam McCall84652cc2018-01-12 16:16:09 +0000375 Item.scoreInfo = Candidate.Scores;
Sam McCall98775c52017-12-04 13:49:59 +0000376
Eric Liu63696e12017-12-20 17:24:31 +0000377 Item.detail = getDetail(CCS);
378 Item.filterText = getFilterText(CCS);
379 getLabelAndInsertText(CCS, &Item.label, &Item.insertText, EnableSnippets);
380
381 Item.insertTextFormat = EnableSnippets ? InsertTextFormat::Snippet
382 : InsertTextFormat::PlainText;
Sam McCall98775c52017-12-04 13:49:59 +0000383
384 // Fill in the kind field of the CompletionItem.
Eric Liu6f648df2017-12-19 16:50:37 +0000385 Item.kind = toCompletionItemKind(Candidate.Result->Kind,
386 Candidate.Result->CursorKind);
Sam McCall98775c52017-12-04 13:49:59 +0000387
388 return Item;
389 }
390
Sam McCall98775c52017-12-04 13:49:59 +0000391 CodeCompleteOptions ClangdOpts;
392 CompletionList &Items;
393 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
394 CodeCompletionTUInfo CCTUInfo;
Eric Liu6f648df2017-12-19 16:50:37 +0000395 NameToComplete &CompletedName;
Eric Liu63696e12017-12-20 17:24:31 +0000396 bool EnableSnippets;
Sam McCall98775c52017-12-04 13:49:59 +0000397}; // CompletionItemsCollector
398
Sam McCall98775c52017-12-04 13:49:59 +0000399class SignatureHelpCollector final : public CodeCompleteConsumer {
400
401public:
402 SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts,
403 SignatureHelp &SigHelp)
404 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
405 SigHelp(SigHelp),
406 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
407 CCTUInfo(Allocator) {}
408
409 void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
410 OverloadCandidate *Candidates,
411 unsigned NumCandidates) override {
412 SigHelp.signatures.reserve(NumCandidates);
413 // FIXME(rwols): How can we determine the "active overload candidate"?
414 // Right now the overloaded candidates seem to be provided in a "best fit"
415 // order, so I'm not too worried about this.
416 SigHelp.activeSignature = 0;
417 assert(CurrentArg <= (unsigned)std::numeric_limits<int>::max() &&
418 "too many arguments");
419 SigHelp.activeParameter = static_cast<int>(CurrentArg);
420 for (unsigned I = 0; I < NumCandidates; ++I) {
421 const auto &Candidate = Candidates[I];
422 const auto *CCS = Candidate.CreateSignatureString(
423 CurrentArg, S, *Allocator, CCTUInfo, true);
424 assert(CCS && "Expected the CodeCompletionString to be non-null");
425 SigHelp.signatures.push_back(ProcessOverloadCandidate(Candidate, *CCS));
426 }
427 }
428
429 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
430
431 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
432
433private:
Eric Liu63696e12017-12-20 17:24:31 +0000434 // FIXME(ioeric): consider moving CodeCompletionString logic here to
435 // CompletionString.h.
Sam McCall98775c52017-12-04 13:49:59 +0000436 SignatureInformation
437 ProcessOverloadCandidate(const OverloadCandidate &Candidate,
438 const CodeCompletionString &CCS) const {
439 SignatureInformation Result;
440 const char *ReturnType = nullptr;
441
442 Result.documentation = getDocumentation(CCS);
443
444 for (const auto &Chunk : CCS) {
445 switch (Chunk.Kind) {
446 case CodeCompletionString::CK_ResultType:
447 // A piece of text that describes the type of an entity or,
448 // for functions and methods, the return type.
449 assert(!ReturnType && "Unexpected CK_ResultType");
450 ReturnType = Chunk.Text;
451 break;
452 case CodeCompletionString::CK_Placeholder:
453 // A string that acts as a placeholder for, e.g., a function call
454 // argument.
455 // Intentional fallthrough here.
456 case CodeCompletionString::CK_CurrentParameter: {
457 // A piece of text that describes the parameter that corresponds to
458 // the code-completion location within a function call, message send,
459 // macro invocation, etc.
460 Result.label += Chunk.Text;
461 ParameterInformation Info;
462 Info.label = Chunk.Text;
463 Result.parameters.push_back(std::move(Info));
464 break;
465 }
466 case CodeCompletionString::CK_Optional: {
467 // The rest of the parameters are defaulted/optional.
468 assert(Chunk.Optional &&
469 "Expected the optional code completion string to be non-null.");
470 Result.label +=
471 getOptionalParameters(*Chunk.Optional, Result.parameters);
472 break;
473 }
474 case CodeCompletionString::CK_VerticalSpace:
475 break;
476 default:
477 Result.label += Chunk.Text;
478 break;
479 }
480 }
481 if (ReturnType) {
482 Result.label += " -> ";
483 Result.label += ReturnType;
484 }
485 return Result;
486 }
487
488 SignatureHelp &SigHelp;
489 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
490 CodeCompletionTUInfo CCTUInfo;
491
492}; // SignatureHelpCollector
493
Ilya Biryukov940901e2017-12-13 12:51:22 +0000494bool invokeCodeComplete(const Context &Ctx,
495 std::unique_ptr<CodeCompleteConsumer> Consumer,
Sam McCall98775c52017-12-04 13:49:59 +0000496 const clang::CodeCompleteOptions &Options,
497 PathRef FileName,
498 const tooling::CompileCommand &Command,
499 PrecompiledPreamble const *Preamble, StringRef Contents,
500 Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000501 std::shared_ptr<PCHContainerOperations> PCHs) {
Sam McCall98775c52017-12-04 13:49:59 +0000502 std::vector<const char *> ArgStrs;
503 for (const auto &S : Command.CommandLine)
504 ArgStrs.push_back(S.c_str());
505
506 VFS->setCurrentWorkingDirectory(Command.Directory);
507
508 IgnoreDiagnostics DummyDiagsConsumer;
509 auto CI = createInvocationFromCommandLine(
510 ArgStrs,
511 CompilerInstance::createDiagnostics(new DiagnosticOptions,
512 &DummyDiagsConsumer, false),
513 VFS);
514 assert(CI && "Couldn't create CompilerInvocation");
Ilya Biryukov71590652018-01-05 13:36:55 +0000515 CI->getFrontendOpts().DisableFree = false;
Sam McCall98775c52017-12-04 13:49:59 +0000516
517 std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
518 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName);
519
520 // Attempt to reuse the PCH from precompiled preamble, if it was built.
521 if (Preamble) {
522 auto Bounds =
523 ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
524 if (!Preamble->CanReuse(*CI, ContentsBuffer.get(), Bounds, VFS.get()))
525 Preamble = nullptr;
526 }
527
528 auto Clang = prepareCompilerInstance(
529 std::move(CI), Preamble, std::move(ContentsBuffer), std::move(PCHs),
530 std::move(VFS), DummyDiagsConsumer);
531 auto &DiagOpts = Clang->getDiagnosticOpts();
532 DiagOpts.IgnoreWarnings = true;
533
534 auto &FrontendOpts = Clang->getFrontendOpts();
535 FrontendOpts.SkipFunctionBodies = true;
536 FrontendOpts.CodeCompleteOpts = Options;
537 FrontendOpts.CodeCompletionAt.FileName = FileName;
538 FrontendOpts.CodeCompletionAt.Line = Pos.line + 1;
539 FrontendOpts.CodeCompletionAt.Column = Pos.character + 1;
540
541 Clang->setCodeCompletionConsumer(Consumer.release());
542
543 SyntaxOnlyAction Action;
544 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000545 log(Ctx,
546 "BeginSourceFile() failed when running codeComplete for " + FileName);
Sam McCall98775c52017-12-04 13:49:59 +0000547 return false;
548 }
549 if (!Action.Execute()) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000550 log(Ctx, "Execute() failed when running codeComplete for " + FileName);
Sam McCall98775c52017-12-04 13:49:59 +0000551 return false;
552 }
553
554 Action.EndSourceFile();
555
556 return true;
557}
558
Eric Liu6f648df2017-12-19 16:50:37 +0000559CompletionItem indexCompletionItem(const Symbol &Sym, llvm::StringRef Filter,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000560 const SpecifiedScope &SSInfo,
561 llvm::StringRef DebuggingLabel = "") {
Eric Liu6f648df2017-12-19 16:50:37 +0000562 CompletionItem Item;
563 Item.kind = toCompletionItemKind(Sym.SymInfo.Kind);
Haojian Wuba28e9a2018-01-10 14:44:34 +0000564 // Add DebuggingLabel to the completion results if DebuggingLabel is not
565 // empty.
566 //
567 // For symbols from static index, there are prefix "[G]" in the
568 // results (which is used for debugging purpose).
569 // So completion list will be like:
570 // clang::symbol_from_dynamic_index
571 // [G]clang::symbol_from_static_index
572 //
573 // FIXME: Find out a better way to show the index source.
574 if (!DebuggingLabel.empty()) {
575 llvm::raw_string_ostream Label(Item.label);
576 Label << llvm::format("[%s]%s", DebuggingLabel.str().c_str(),
577 Sym.Name.str().c_str());
578 } else {
579 Item.label = Sym.Name;
580 }
Eric Liu6f648df2017-12-19 16:50:37 +0000581 // FIXME(ioeric): support inserting/replacing scope qualifiers.
Eric Liu76f6b442018-01-09 17:32:00 +0000582
Eric Liu6f648df2017-12-19 16:50:37 +0000583 // FIXME(ioeric): support snippets.
Eric Liu76f6b442018-01-09 17:32:00 +0000584 Item.insertText = Sym.CompletionPlainInsertText;
Eric Liu6f648df2017-12-19 16:50:37 +0000585 Item.insertTextFormat = InsertTextFormat::PlainText;
Ilya Biryukov5a5e1ca2017-12-29 14:59:22 +0000586 Item.filterText = Sym.Name;
Eric Liu6f648df2017-12-19 16:50:37 +0000587
588 // FIXME(ioeric): sort symbols appropriately.
589 Item.sortText = "";
590
Eric Liu76f6b442018-01-09 17:32:00 +0000591 if (Sym.Detail) {
592 Item.documentation = Sym.Detail->Documentation;
593 Item.detail = Sym.Detail->CompletionDetail;
594 }
Eric Liu6f648df2017-12-19 16:50:37 +0000595
596 return Item;
597}
598
599void completeWithIndex(const Context &Ctx, const SymbolIndex &Index,
600 llvm::StringRef Code, const SpecifiedScope &SSInfo,
Haojian Wuba28e9a2018-01-10 14:44:34 +0000601 llvm::StringRef Filter, CompletionList *Items,
602 llvm::StringRef DebuggingLabel = "") {
Eric Liu6f648df2017-12-19 16:50:37 +0000603 FuzzyFindRequest Req;
604 Req.Query = Filter;
605 // FIXME(ioeric): add more possible scopes based on using namespaces and
606 // containing namespaces.
607 StringRef Scope = SSInfo.Resolved.empty() ? SSInfo.Written : SSInfo.Resolved;
608 Req.Scopes = {Scope.trim(':').str()};
609
Haojian Wuba28e9a2018-01-10 14:44:34 +0000610 Items->isIncomplete |= !Index.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
611 Items->items.push_back(
612 indexCompletionItem(Sym, Filter, SSInfo, DebuggingLabel));
Eric Liu6f648df2017-12-19 16:50:37 +0000613 });
614}
615
616SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS) {
617 SpecifiedScope Info;
618 auto &SM = S.getSourceManager();
619 auto SpecifierRange = SS.getRange();
620 Info.Written = Lexer::getSourceText(
621 CharSourceRange::getCharRange(SpecifierRange), SM, clang::LangOptions());
622 if (SS.isValid()) {
623 DeclContext *DC = S.computeDeclContext(SS);
624 if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
625 Info.Resolved = NS->getQualifiedNameAsString();
Sam McCalle3e15702017-12-19 17:05:00 +0000626 } else if (llvm::dyn_cast<TranslationUnitDecl>(DC) != nullptr) {
Eric Liu6f648df2017-12-19 16:50:37 +0000627 Info.Resolved = "::";
628 // Sema does not include the suffix "::" in the range of SS, so we add
629 // it back here.
630 Info.Written = "::";
631 }
632 }
633 return Info;
634}
635
Sam McCall98775c52017-12-04 13:49:59 +0000636} // namespace
637
638clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
639 clang::CodeCompleteOptions Result;
640 Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
641 Result.IncludeMacros = IncludeMacros;
642 Result.IncludeGlobals = IncludeGlobals;
643 Result.IncludeBriefComments = IncludeBriefComments;
644
Eric Liu6f648df2017-12-19 16:50:37 +0000645 // Enable index-based code completion when Index is provided.
646 Result.IncludeNamespaceLevelDecls = !Index;
647
Sam McCall98775c52017-12-04 13:49:59 +0000648 return Result;
649}
650
Ilya Biryukov940901e2017-12-13 12:51:22 +0000651CompletionList codeComplete(const Context &Ctx, PathRef FileName,
Sam McCall98775c52017-12-04 13:49:59 +0000652 const tooling::CompileCommand &Command,
653 PrecompiledPreamble const *Preamble,
654 StringRef Contents, Position Pos,
655 IntrusiveRefCntPtr<vfs::FileSystem> VFS,
656 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000657 CodeCompleteOptions Opts) {
Sam McCall98775c52017-12-04 13:49:59 +0000658 CompletionList Results;
Eric Liu6f648df2017-12-19 16:50:37 +0000659 NameToComplete CompletedName;
Eric Liu63696e12017-12-20 17:24:31 +0000660 auto Consumer =
661 llvm::make_unique<CompletionItemsCollector>(Opts, Results, CompletedName);
Ilya Biryukov940901e2017-12-13 12:51:22 +0000662 invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(),
663 FileName, Command, Preamble, Contents, Pos, std::move(VFS),
664 std::move(PCHs));
Haojian Wuba28e9a2018-01-10 14:44:34 +0000665
666 // Got scope specifier (ns::f^) for code completion from sema, try to query
667 // global symbols from indexes.
668 if (CompletedName.SSInfo) {
669 // FIXME: figure out a good algorithm to merge symbols from different
670 // sources (dynamic index, static index, AST symbols from clang's completion
671 // engine).
672 if (Opts.Index)
673 completeWithIndex(Ctx, *Opts.Index, Contents, *CompletedName.SSInfo,
Sam McCall93483e72018-01-12 17:09:49 +0000674 CompletedName.Filter, &Results, /*DebuggingLabel=*/"D");
Haojian Wuba28e9a2018-01-10 14:44:34 +0000675 if (Opts.StaticIndex)
676 completeWithIndex(Ctx, *Opts.StaticIndex, Contents, *CompletedName.SSInfo,
Sam McCall93483e72018-01-12 17:09:49 +0000677 CompletedName.Filter, &Results, /*DebuggingLabel=*/"S");
Eric Liu6f648df2017-12-19 16:50:37 +0000678 }
Sam McCall98775c52017-12-04 13:49:59 +0000679 return Results;
680}
681
Ilya Biryukov940901e2017-12-13 12:51:22 +0000682SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName,
683 const tooling::CompileCommand &Command,
684 PrecompiledPreamble const *Preamble,
685 StringRef Contents, Position Pos,
686 IntrusiveRefCntPtr<vfs::FileSystem> VFS,
687 std::shared_ptr<PCHContainerOperations> PCHs) {
Sam McCall98775c52017-12-04 13:49:59 +0000688 SignatureHelp Result;
689 clang::CodeCompleteOptions Options;
690 Options.IncludeGlobals = false;
691 Options.IncludeMacros = false;
692 Options.IncludeCodePatterns = false;
693 Options.IncludeBriefComments = true;
Ilya Biryukov940901e2017-12-13 12:51:22 +0000694 invokeCodeComplete(Ctx,
695 llvm::make_unique<SignatureHelpCollector>(Options, Result),
Sam McCall98775c52017-12-04 13:49:59 +0000696 Options, FileName, Command, Preamble, Contents, Pos,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000697 std::move(VFS), std::move(PCHs));
Sam McCall98775c52017-12-04 13:49:59 +0000698 return Result;
699}
700
701} // namespace clangd
702} // namespace clang