blob: cb56afe902e27446fe58829854e5a302eb34b717 [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"
18#include "Compiler.h"
Eric Liu6f648df2017-12-19 16:50:37 +000019#include "Logger.h"
20#include "index/Index.h"
Sam McCall98775c52017-12-04 13:49:59 +000021#include "clang/Frontend/CompilerInstance.h"
22#include "clang/Frontend/FrontendActions.h"
23#include "clang/Sema/CodeCompleteConsumer.h"
24#include "clang/Sema/Sema.h"
25#include <queue>
26
27namespace clang {
28namespace clangd {
29namespace {
30
Eric Liu6f648df2017-12-19 16:50:37 +000031CompletionItemKind toCompletionItemKind(CXCursorKind CursorKind) {
Sam McCall98775c52017-12-04 13:49:59 +000032 switch (CursorKind) {
33 case CXCursor_MacroInstantiation:
34 case CXCursor_MacroDefinition:
35 return CompletionItemKind::Text;
36 case CXCursor_CXXMethod:
Eric Liu6f648df2017-12-19 16:50:37 +000037 case CXCursor_Destructor:
Sam McCall98775c52017-12-04 13:49:59 +000038 return CompletionItemKind::Method;
39 case CXCursor_FunctionDecl:
40 case CXCursor_FunctionTemplate:
41 return CompletionItemKind::Function;
42 case CXCursor_Constructor:
Sam McCall98775c52017-12-04 13:49:59 +000043 return CompletionItemKind::Constructor;
44 case CXCursor_FieldDecl:
45 return CompletionItemKind::Field;
46 case CXCursor_VarDecl:
47 case CXCursor_ParmDecl:
48 return CompletionItemKind::Variable;
Eric Liu6f648df2017-12-19 16:50:37 +000049 // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
50 // protocol.
Sam McCall98775c52017-12-04 13:49:59 +000051 case CXCursor_StructDecl:
Eric Liu6f648df2017-12-19 16:50:37 +000052 case CXCursor_ClassDecl:
Sam McCall98775c52017-12-04 13:49:59 +000053 case CXCursor_UnionDecl:
54 case CXCursor_ClassTemplate:
55 case CXCursor_ClassTemplatePartialSpecialization:
56 return CompletionItemKind::Class;
57 case CXCursor_Namespace:
58 case CXCursor_NamespaceAlias:
59 case CXCursor_NamespaceRef:
60 return CompletionItemKind::Module;
61 case CXCursor_EnumConstantDecl:
62 return CompletionItemKind::Value;
63 case CXCursor_EnumDecl:
64 return CompletionItemKind::Enum;
Eric Liu6f648df2017-12-19 16:50:37 +000065 // FIXME(ioeric): figure out whether reference is the right type for aliases.
Sam McCall98775c52017-12-04 13:49:59 +000066 case CXCursor_TypeAliasDecl:
67 case CXCursor_TypeAliasTemplateDecl:
68 case CXCursor_TypedefDecl:
69 case CXCursor_MemberRef:
70 case CXCursor_TypeRef:
71 return CompletionItemKind::Reference;
72 default:
73 return CompletionItemKind::Missing;
74 }
75}
76
Eric Liu6f648df2017-12-19 16:50:37 +000077CompletionItemKind
78toCompletionItemKind(CodeCompletionResult::ResultKind ResKind,
79 CXCursorKind CursorKind) {
Sam McCall98775c52017-12-04 13:49:59 +000080 switch (ResKind) {
81 case CodeCompletionResult::RK_Declaration:
Eric Liu6f648df2017-12-19 16:50:37 +000082 return toCompletionItemKind(CursorKind);
Sam McCall98775c52017-12-04 13:49:59 +000083 case CodeCompletionResult::RK_Keyword:
84 return CompletionItemKind::Keyword;
85 case CodeCompletionResult::RK_Macro:
86 return CompletionItemKind::Text; // unfortunately, there's no 'Macro'
87 // completion items in LSP.
88 case CodeCompletionResult::RK_Pattern:
89 return CompletionItemKind::Snippet;
90 }
91 llvm_unreachable("Unhandled CodeCompletionResult::ResultKind.");
92}
93
Eric Liu6f648df2017-12-19 16:50:37 +000094CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) {
95 using SK = index::SymbolKind;
96 switch (Kind) {
97 case SK::Unknown:
98 return CompletionItemKind::Missing;
99 case SK::Module:
100 case SK::Namespace:
101 case SK::NamespaceAlias:
102 return CompletionItemKind::Module;
103 case SK::Macro:
104 return CompletionItemKind::Text;
105 case SK::Enum:
106 return CompletionItemKind::Enum;
107 // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the
108 // protocol.
109 case SK::Struct:
110 case SK::Class:
111 case SK::Protocol:
112 case SK::Extension:
113 case SK::Union:
114 return CompletionItemKind::Class;
115 // FIXME(ioeric): figure out whether reference is the right type for aliases.
116 case SK::TypeAlias:
117 case SK::Using:
118 return CompletionItemKind::Reference;
119 case SK::Function:
120 // FIXME(ioeric): this should probably be an operator. This should be fixed
121 // when `Operator` is support type in the protocol.
122 case SK::ConversionFunction:
123 return CompletionItemKind::Function;
124 case SK::Variable:
125 case SK::Parameter:
126 return CompletionItemKind::Variable;
127 case SK::Field:
128 return CompletionItemKind::Field;
129 // FIXME(ioeric): use LSP enum constant when it is supported in the protocol.
130 case SK::EnumConstant:
131 return CompletionItemKind::Value;
132 case SK::InstanceMethod:
133 case SK::ClassMethod:
134 case SK::StaticMethod:
135 case SK::Destructor:
136 return CompletionItemKind::Method;
137 case SK::InstanceProperty:
138 case SK::ClassProperty:
139 case SK::StaticProperty:
140 return CompletionItemKind::Property;
141 case SK::Constructor:
142 return CompletionItemKind::Constructor;
143 }
144 llvm_unreachable("Unhandled clang::index::SymbolKind.");
145}
146
Sam McCall98775c52017-12-04 13:49:59 +0000147std::string escapeSnippet(const llvm::StringRef Text) {
148 std::string Result;
149 Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
150 for (const auto Character : Text) {
151 if (Character == '$' || Character == '}' || Character == '\\')
152 Result.push_back('\\');
153 Result.push_back(Character);
154 }
155 return Result;
156}
157
158std::string getDocumentation(const CodeCompletionString &CCS) {
159 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
160 // information in the documentation field.
161 std::string Result;
162 const unsigned AnnotationCount = CCS.getAnnotationCount();
163 if (AnnotationCount > 0) {
164 Result += "Annotation";
165 if (AnnotationCount == 1) {
166 Result += ": ";
167 } else /* AnnotationCount > 1 */ {
168 Result += "s: ";
169 }
170 for (unsigned I = 0; I < AnnotationCount; ++I) {
171 Result += CCS.getAnnotation(I);
172 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
173 }
174 }
175 // Add brief documentation (if there is any).
176 if (CCS.getBriefComment() != nullptr) {
177 if (!Result.empty()) {
178 // This means we previously added annotations. Add an extra newline
179 // character to make the annotations stand out.
180 Result.push_back('\n');
181 }
182 Result += CCS.getBriefComment();
183 }
184 return Result;
185}
186
187/// Get the optional chunk as a string. This function is possibly recursive.
188///
189/// The parameter info for each parameter is appended to the Parameters.
190std::string
191getOptionalParameters(const CodeCompletionString &CCS,
192 std::vector<ParameterInformation> &Parameters) {
193 std::string Result;
194 for (const auto &Chunk : CCS) {
195 switch (Chunk.Kind) {
196 case CodeCompletionString::CK_Optional:
197 assert(Chunk.Optional &&
198 "Expected the optional code completion string to be non-null.");
199 Result += getOptionalParameters(*Chunk.Optional, Parameters);
200 break;
201 case CodeCompletionString::CK_VerticalSpace:
202 break;
203 case CodeCompletionString::CK_Placeholder:
204 // A string that acts as a placeholder for, e.g., a function call
205 // argument.
206 // Intentional fallthrough here.
207 case CodeCompletionString::CK_CurrentParameter: {
208 // A piece of text that describes the parameter that corresponds to
209 // the code-completion location within a function call, message send,
210 // macro invocation, etc.
211 Result += Chunk.Text;
212 ParameterInformation Info;
213 Info.label = Chunk.Text;
214 Parameters.push_back(std::move(Info));
215 break;
216 }
217 default:
218 Result += Chunk.Text;
219 break;
220 }
221 }
222 return Result;
223}
224
Sam McCall98775c52017-12-04 13:49:59 +0000225/// A scored code completion result.
226/// It may be promoted to a CompletionItem if it's among the top-ranked results.
227struct CompletionCandidate {
228 CompletionCandidate(CodeCompletionResult &Result)
229 : Result(&Result), Score(score(Result)) {}
230
231 CodeCompletionResult *Result;
232 float Score; // 0 to 1, higher is better.
233
234 // Comparison reflects rank: better candidates are smaller.
235 bool operator<(const CompletionCandidate &C) const {
236 if (Score != C.Score)
237 return Score > C.Score;
238 return *Result < *C.Result;
239 }
240
241 // Returns a string that sorts in the same order as operator<, for LSP.
242 // Conceptually, this is [-Score, Name]. We convert -Score to an integer, and
243 // hex-encode it for readability. Example: [0.5, "foo"] -> "41000000foo"
244 std::string sortText() const {
245 std::string S, NameStorage;
246 llvm::raw_string_ostream OS(S);
247 write_hex(OS, encodeFloat(-Score), llvm::HexPrintStyle::Lower,
248 /*Width=*/2 * sizeof(Score));
249 OS << Result->getOrderedName(NameStorage);
250 return OS.str();
251 }
252
253private:
254 static float score(const CodeCompletionResult &Result) {
255 // Priority 80 is a really bad score.
256 float Score = 1 - std::min<float>(80, Result.Priority) / 80;
257
258 switch (static_cast<CXAvailabilityKind>(Result.Availability)) {
259 case CXAvailability_Available:
260 // No penalty.
261 break;
262 case CXAvailability_Deprecated:
263 Score *= 0.1f;
264 break;
265 case CXAvailability_NotAccessible:
266 case CXAvailability_NotAvailable:
267 Score = 0;
268 break;
269 }
270 return Score;
271 }
272
273 // Produces an integer that sorts in the same order as F.
274 // That is: a < b <==> encodeFloat(a) < encodeFloat(b).
275 static uint32_t encodeFloat(float F) {
276 static_assert(std::numeric_limits<float>::is_iec559, "");
277 static_assert(sizeof(float) == sizeof(uint32_t), "");
278 constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
279
280 // Get the bits of the float. Endianness is the same as for integers.
281 uint32_t U;
282 memcpy(&U, &F, sizeof(float));
283 // IEEE 754 floats compare like sign-magnitude integers.
284 if (U & TopBit) // Negative float.
285 return 0 - U; // Map onto the low half of integers, order reversed.
286 return U + TopBit; // Positive floats map onto the high half of integers.
287 }
288};
289
Eric Liu6f648df2017-12-19 16:50:37 +0000290/// \brief Information about the scope specifier in the qualified-id code
291/// completion (e.g. "ns::ab?").
292struct SpecifiedScope {
293 /// The scope specifier as written. For example, for completion "ns::ab?", the
294 /// written scope specifier is "ns".
295 std::string Written;
296 // If this scope specifier is recognized in Sema (e.g. as a namespace
297 // context), this will be set to the fully qualfied name of the corresponding
298 // context.
299 std::string Resolved;
300};
301
302/// \brief Information from sema about (parital) symbol names to be completed.
303/// For example, for completion "ns::ab^", this stores the scope specifier
304/// "ns::" and the completion filter text "ab".
305struct NameToComplete {
306 // The partial identifier being completed, without qualifier.
307 std::string Filter;
308
309 /// This is set if the completion is for qualified IDs, e.g. "abc::x^".
310 llvm::Optional<SpecifiedScope> SSInfo;
311};
312
313SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS);
314
Sam McCall98775c52017-12-04 13:49:59 +0000315class CompletionItemsCollector : public CodeCompleteConsumer {
316public:
317 CompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
Eric Liu6f648df2017-12-19 16:50:37 +0000318 CompletionList &Items, NameToComplete &CompletedName)
Sam McCall98775c52017-12-04 13:49:59 +0000319 : CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
320 /*OutputIsBinary=*/false),
321 ClangdOpts(CodeCompleteOpts), Items(Items),
322 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
Eric Liu6f648df2017-12-19 16:50:37 +0000323 CCTUInfo(Allocator), CompletedName(CompletedName) {}
Sam McCall98775c52017-12-04 13:49:59 +0000324
325 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
326 CodeCompletionResult *Results,
327 unsigned NumResults) override final {
Eric Liu6f648df2017-12-19 16:50:37 +0000328 if (auto SS = Context.getCXXScopeSpecifier())
329 CompletedName.SSInfo = extraCompletionScope(S, **SS);
330
331 CompletedName.Filter = S.getPreprocessor().getCodeCompletionFilter();
Sam McCall98775c52017-12-04 13:49:59 +0000332 std::priority_queue<CompletionCandidate> Candidates;
333 for (unsigned I = 0; I < NumResults; ++I) {
334 auto &Result = Results[I];
335 if (!ClangdOpts.IncludeIneligibleResults &&
336 (Result.Availability == CXAvailability_NotAvailable ||
337 Result.Availability == CXAvailability_NotAccessible))
338 continue;
Eric Liu6f648df2017-12-19 16:50:37 +0000339 if (!CompletedName.Filter.empty() &&
340 !fuzzyMatch(S, Context, CompletedName.Filter, Result))
Sam McCall98775c52017-12-04 13:49:59 +0000341 continue;
342 Candidates.emplace(Result);
343 if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
344 Candidates.pop();
345 Items.isIncomplete = true;
346 }
347 }
348 while (!Candidates.empty()) {
349 auto &Candidate = Candidates.top();
350 const auto *CCS = Candidate.Result->CreateCodeCompletionString(
351 S, Context, *Allocator, CCTUInfo,
352 CodeCompleteOpts.IncludeBriefComments);
353 assert(CCS && "Expected the CodeCompletionString to be non-null");
354 Items.items.push_back(ProcessCodeCompleteResult(Candidate, *CCS));
355 Candidates.pop();
356 }
357 std::reverse(Items.items.begin(), Items.items.end());
358 }
359
360 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
361
362 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
363
364private:
365 bool fuzzyMatch(Sema &S, const CodeCompletionContext &CCCtx, StringRef Filter,
366 CodeCompletionResult Result) {
367 switch (Result.Kind) {
368 case CodeCompletionResult::RK_Declaration:
369 if (auto *ID = Result.Declaration->getIdentifier())
370 return fuzzyMatch(Filter, ID->getName());
371 break;
372 case CodeCompletionResult::RK_Keyword:
373 return fuzzyMatch(Filter, Result.Keyword);
374 case CodeCompletionResult::RK_Macro:
375 return fuzzyMatch(Filter, Result.Macro->getName());
376 case CodeCompletionResult::RK_Pattern:
377 return fuzzyMatch(Filter, Result.Pattern->getTypedText());
378 }
379 auto *CCS = Result.CreateCodeCompletionString(
380 S, CCCtx, *Allocator, CCTUInfo, /*IncludeBriefComments=*/false);
381 return fuzzyMatch(Filter, CCS->getTypedText());
382 }
383
384 // Checks whether Target matches the Filter.
385 // Currently just requires a case-insensitive subsequence match.
386 // FIXME: make stricter and word-based: 'unique_ptr' should not match 'que'.
387 // FIXME: return a score to be incorporated into ranking.
388 static bool fuzzyMatch(StringRef Filter, StringRef Target) {
389 size_t TPos = 0;
390 for (char C : Filter) {
391 TPos = Target.find_lower(C, TPos);
392 if (TPos == StringRef::npos)
393 return false;
394 }
395 return true;
396 }
397
398 CompletionItem
399 ProcessCodeCompleteResult(const CompletionCandidate &Candidate,
400 const CodeCompletionString &CCS) const {
401
402 // Adjust this to InsertTextFormat::Snippet iff we encounter a
403 // CK_Placeholder chunk in SnippetCompletionItemsCollector.
404 CompletionItem Item;
405 Item.insertTextFormat = InsertTextFormat::PlainText;
406
407 Item.documentation = getDocumentation(CCS);
408 Item.sortText = Candidate.sortText();
409
410 // Fill in the label, detail, insertText and filterText fields of the
411 // CompletionItem.
412 ProcessChunks(CCS, Item);
413
414 // Fill in the kind field of the CompletionItem.
Eric Liu6f648df2017-12-19 16:50:37 +0000415 Item.kind = toCompletionItemKind(Candidate.Result->Kind,
416 Candidate.Result->CursorKind);
Sam McCall98775c52017-12-04 13:49:59 +0000417
418 return Item;
419 }
420
421 virtual void ProcessChunks(const CodeCompletionString &CCS,
422 CompletionItem &Item) const = 0;
423
424 CodeCompleteOptions ClangdOpts;
425 CompletionList &Items;
426 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
427 CodeCompletionTUInfo CCTUInfo;
Eric Liu6f648df2017-12-19 16:50:37 +0000428 NameToComplete &CompletedName;
Sam McCall98775c52017-12-04 13:49:59 +0000429}; // CompletionItemsCollector
430
431bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
432 return Chunk.Kind == CodeCompletionString::CK_Informative &&
433 StringRef(Chunk.Text).endswith("::");
434}
435
436class PlainTextCompletionItemsCollector final
437 : public CompletionItemsCollector {
438
439public:
Sam McCall9aad25f2017-12-05 07:20:26 +0000440 PlainTextCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
Eric Liu6f648df2017-12-19 16:50:37 +0000441 CompletionList &Items,
442 NameToComplete &CompletedName)
443 : CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
Sam McCall98775c52017-12-04 13:49:59 +0000444
445private:
446 void ProcessChunks(const CodeCompletionString &CCS,
447 CompletionItem &Item) const override {
448 for (const auto &Chunk : CCS) {
449 // Informative qualifier chunks only clutter completion results, skip
450 // them.
451 if (isInformativeQualifierChunk(Chunk))
452 continue;
453
454 switch (Chunk.Kind) {
455 case CodeCompletionString::CK_TypedText:
456 // There's always exactly one CK_TypedText chunk.
457 Item.insertText = Item.filterText = Chunk.Text;
458 Item.label += Chunk.Text;
459 break;
460 case CodeCompletionString::CK_ResultType:
461 assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
462 Item.detail = Chunk.Text;
463 break;
464 case CodeCompletionString::CK_Optional:
465 break;
466 default:
467 Item.label += Chunk.Text;
468 break;
469 }
470 }
471 }
472}; // PlainTextCompletionItemsCollector
473
474class SnippetCompletionItemsCollector final : public CompletionItemsCollector {
475
476public:
Sam McCall9aad25f2017-12-05 07:20:26 +0000477 SnippetCompletionItemsCollector(const CodeCompleteOptions &CodeCompleteOpts,
Eric Liu6f648df2017-12-19 16:50:37 +0000478 CompletionList &Items,
479 NameToComplete &CompletedName)
480 : CompletionItemsCollector(CodeCompleteOpts, Items, CompletedName) {}
Sam McCall98775c52017-12-04 13:49:59 +0000481
482private:
483 void ProcessChunks(const CodeCompletionString &CCS,
484 CompletionItem &Item) const override {
485 unsigned ArgCount = 0;
486 for (const auto &Chunk : CCS) {
487 // Informative qualifier chunks only clutter completion results, skip
488 // them.
489 if (isInformativeQualifierChunk(Chunk))
490 continue;
491
492 switch (Chunk.Kind) {
493 case CodeCompletionString::CK_TypedText:
494 // The piece of text that the user is expected to type to match
495 // the code-completion string, typically a keyword or the name of
496 // a declarator or macro.
497 Item.filterText = Chunk.Text;
498 LLVM_FALLTHROUGH;
499 case CodeCompletionString::CK_Text:
500 // A piece of text that should be placed in the buffer,
501 // e.g., parentheses or a comma in a function call.
502 Item.label += Chunk.Text;
503 Item.insertText += Chunk.Text;
504 break;
505 case CodeCompletionString::CK_Optional:
506 // A code completion string that is entirely optional.
507 // For example, an optional code completion string that
508 // describes the default arguments in a function call.
509
510 // FIXME: Maybe add an option to allow presenting the optional chunks?
511 break;
512 case CodeCompletionString::CK_Placeholder:
513 // A string that acts as a placeholder for, e.g., a function call
514 // argument.
515 ++ArgCount;
516 Item.insertText += "${" + std::to_string(ArgCount) + ':' +
517 escapeSnippet(Chunk.Text) + '}';
518 Item.label += Chunk.Text;
519 Item.insertTextFormat = InsertTextFormat::Snippet;
520 break;
521 case CodeCompletionString::CK_Informative:
522 // A piece of text that describes something about the result
523 // but should not be inserted into the buffer.
524 // For example, the word "const" for a const method, or the name of
525 // the base class for methods that are part of the base class.
526 Item.label += Chunk.Text;
527 // Don't put the informative chunks in the insertText.
528 break;
529 case CodeCompletionString::CK_ResultType:
530 // A piece of text that describes the type of an entity or,
531 // for functions and methods, the return type.
532 assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
533 Item.detail = Chunk.Text;
534 break;
535 case CodeCompletionString::CK_CurrentParameter:
536 // A piece of text that describes the parameter that corresponds to
537 // the code-completion location within a function call, message send,
538 // macro invocation, etc.
539 //
540 // This should never be present while collecting completion items,
541 // only while collecting overload candidates.
542 llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
543 "CompletionItems");
544 break;
545 case CodeCompletionString::CK_LeftParen:
546 // A left parenthesis ('(').
547 case CodeCompletionString::CK_RightParen:
548 // A right parenthesis (')').
549 case CodeCompletionString::CK_LeftBracket:
550 // A left bracket ('[').
551 case CodeCompletionString::CK_RightBracket:
552 // A right bracket (']').
553 case CodeCompletionString::CK_LeftBrace:
554 // A left brace ('{').
555 case CodeCompletionString::CK_RightBrace:
556 // A right brace ('}').
557 case CodeCompletionString::CK_LeftAngle:
558 // A left angle bracket ('<').
559 case CodeCompletionString::CK_RightAngle:
560 // A right angle bracket ('>').
561 case CodeCompletionString::CK_Comma:
562 // A comma separator (',').
563 case CodeCompletionString::CK_Colon:
564 // A colon (':').
565 case CodeCompletionString::CK_SemiColon:
566 // A semicolon (';').
567 case CodeCompletionString::CK_Equal:
568 // An '=' sign.
569 case CodeCompletionString::CK_HorizontalSpace:
570 // Horizontal whitespace (' ').
571 Item.insertText += Chunk.Text;
572 Item.label += Chunk.Text;
573 break;
574 case CodeCompletionString::CK_VerticalSpace:
575 // Vertical whitespace ('\n' or '\r\n', depending on the
576 // platform).
577 Item.insertText += Chunk.Text;
578 // Don't even add a space to the label.
579 break;
580 }
581 }
582 }
583}; // SnippetCompletionItemsCollector
584
585class SignatureHelpCollector final : public CodeCompleteConsumer {
586
587public:
588 SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts,
589 SignatureHelp &SigHelp)
590 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
591 SigHelp(SigHelp),
592 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
593 CCTUInfo(Allocator) {}
594
595 void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
596 OverloadCandidate *Candidates,
597 unsigned NumCandidates) override {
598 SigHelp.signatures.reserve(NumCandidates);
599 // FIXME(rwols): How can we determine the "active overload candidate"?
600 // Right now the overloaded candidates seem to be provided in a "best fit"
601 // order, so I'm not too worried about this.
602 SigHelp.activeSignature = 0;
603 assert(CurrentArg <= (unsigned)std::numeric_limits<int>::max() &&
604 "too many arguments");
605 SigHelp.activeParameter = static_cast<int>(CurrentArg);
606 for (unsigned I = 0; I < NumCandidates; ++I) {
607 const auto &Candidate = Candidates[I];
608 const auto *CCS = Candidate.CreateSignatureString(
609 CurrentArg, S, *Allocator, CCTUInfo, true);
610 assert(CCS && "Expected the CodeCompletionString to be non-null");
611 SigHelp.signatures.push_back(ProcessOverloadCandidate(Candidate, *CCS));
612 }
613 }
614
615 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
616
617 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
618
619private:
620 SignatureInformation
621 ProcessOverloadCandidate(const OverloadCandidate &Candidate,
622 const CodeCompletionString &CCS) const {
623 SignatureInformation Result;
624 const char *ReturnType = nullptr;
625
626 Result.documentation = getDocumentation(CCS);
627
628 for (const auto &Chunk : CCS) {
629 switch (Chunk.Kind) {
630 case CodeCompletionString::CK_ResultType:
631 // A piece of text that describes the type of an entity or,
632 // for functions and methods, the return type.
633 assert(!ReturnType && "Unexpected CK_ResultType");
634 ReturnType = Chunk.Text;
635 break;
636 case CodeCompletionString::CK_Placeholder:
637 // A string that acts as a placeholder for, e.g., a function call
638 // argument.
639 // Intentional fallthrough here.
640 case CodeCompletionString::CK_CurrentParameter: {
641 // A piece of text that describes the parameter that corresponds to
642 // the code-completion location within a function call, message send,
643 // macro invocation, etc.
644 Result.label += Chunk.Text;
645 ParameterInformation Info;
646 Info.label = Chunk.Text;
647 Result.parameters.push_back(std::move(Info));
648 break;
649 }
650 case CodeCompletionString::CK_Optional: {
651 // The rest of the parameters are defaulted/optional.
652 assert(Chunk.Optional &&
653 "Expected the optional code completion string to be non-null.");
654 Result.label +=
655 getOptionalParameters(*Chunk.Optional, Result.parameters);
656 break;
657 }
658 case CodeCompletionString::CK_VerticalSpace:
659 break;
660 default:
661 Result.label += Chunk.Text;
662 break;
663 }
664 }
665 if (ReturnType) {
666 Result.label += " -> ";
667 Result.label += ReturnType;
668 }
669 return Result;
670 }
671
672 SignatureHelp &SigHelp;
673 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
674 CodeCompletionTUInfo CCTUInfo;
675
676}; // SignatureHelpCollector
677
Ilya Biryukov940901e2017-12-13 12:51:22 +0000678bool invokeCodeComplete(const Context &Ctx,
679 std::unique_ptr<CodeCompleteConsumer> Consumer,
Sam McCall98775c52017-12-04 13:49:59 +0000680 const clang::CodeCompleteOptions &Options,
681 PathRef FileName,
682 const tooling::CompileCommand &Command,
683 PrecompiledPreamble const *Preamble, StringRef Contents,
684 Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000685 std::shared_ptr<PCHContainerOperations> PCHs) {
Sam McCall98775c52017-12-04 13:49:59 +0000686 std::vector<const char *> ArgStrs;
687 for (const auto &S : Command.CommandLine)
688 ArgStrs.push_back(S.c_str());
689
690 VFS->setCurrentWorkingDirectory(Command.Directory);
691
692 IgnoreDiagnostics DummyDiagsConsumer;
693 auto CI = createInvocationFromCommandLine(
694 ArgStrs,
695 CompilerInstance::createDiagnostics(new DiagnosticOptions,
696 &DummyDiagsConsumer, false),
697 VFS);
698 assert(CI && "Couldn't create CompilerInvocation");
699
700 std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
701 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName);
702
703 // Attempt to reuse the PCH from precompiled preamble, if it was built.
704 if (Preamble) {
705 auto Bounds =
706 ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
707 if (!Preamble->CanReuse(*CI, ContentsBuffer.get(), Bounds, VFS.get()))
708 Preamble = nullptr;
709 }
710
711 auto Clang = prepareCompilerInstance(
712 std::move(CI), Preamble, std::move(ContentsBuffer), std::move(PCHs),
713 std::move(VFS), DummyDiagsConsumer);
714 auto &DiagOpts = Clang->getDiagnosticOpts();
715 DiagOpts.IgnoreWarnings = true;
716
717 auto &FrontendOpts = Clang->getFrontendOpts();
718 FrontendOpts.SkipFunctionBodies = true;
719 FrontendOpts.CodeCompleteOpts = Options;
720 FrontendOpts.CodeCompletionAt.FileName = FileName;
721 FrontendOpts.CodeCompletionAt.Line = Pos.line + 1;
722 FrontendOpts.CodeCompletionAt.Column = Pos.character + 1;
723
724 Clang->setCodeCompletionConsumer(Consumer.release());
725
726 SyntaxOnlyAction Action;
727 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000728 log(Ctx,
729 "BeginSourceFile() failed when running codeComplete for " + FileName);
Sam McCall98775c52017-12-04 13:49:59 +0000730 return false;
731 }
732 if (!Action.Execute()) {
Ilya Biryukov940901e2017-12-13 12:51:22 +0000733 log(Ctx, "Execute() failed when running codeComplete for " + FileName);
Sam McCall98775c52017-12-04 13:49:59 +0000734 return false;
735 }
736
737 Action.EndSourceFile();
738
739 return true;
740}
741
Eric Liu6f648df2017-12-19 16:50:37 +0000742CompletionItem indexCompletionItem(const Symbol &Sym, llvm::StringRef Filter,
743 const SpecifiedScope &SSInfo) {
744 CompletionItem Item;
745 Item.kind = toCompletionItemKind(Sym.SymInfo.Kind);
746 Item.label = Sym.Name;
747 // FIXME(ioeric): support inserting/replacing scope qualifiers.
748 Item.insertText = Sym.Name;
749 // FIXME(ioeric): support snippets.
750 Item.insertTextFormat = InsertTextFormat::PlainText;
751 Item.filterText = Filter;
752
753 // FIXME(ioeric): sort symbols appropriately.
754 Item.sortText = "";
755
756 // FIXME(ioeric): use more symbol information (e.g. documentation, label) to
757 // populate the completion item.
758
759 return Item;
760}
761
762void completeWithIndex(const Context &Ctx, const SymbolIndex &Index,
763 llvm::StringRef Code, const SpecifiedScope &SSInfo,
764 llvm::StringRef Filter, CompletionList *Items) {
765 FuzzyFindRequest Req;
766 Req.Query = Filter;
767 // FIXME(ioeric): add more possible scopes based on using namespaces and
768 // containing namespaces.
769 StringRef Scope = SSInfo.Resolved.empty() ? SSInfo.Written : SSInfo.Resolved;
770 Req.Scopes = {Scope.trim(':').str()};
771
772 Items->isIncomplete = !Index.fuzzyFind(Ctx, Req, [&](const Symbol &Sym) {
773 Items->items.push_back(indexCompletionItem(Sym, Filter, SSInfo));
774 });
775}
776
777SpecifiedScope extraCompletionScope(Sema &S, const CXXScopeSpec &SS) {
778 SpecifiedScope Info;
779 auto &SM = S.getSourceManager();
780 auto SpecifierRange = SS.getRange();
781 Info.Written = Lexer::getSourceText(
782 CharSourceRange::getCharRange(SpecifierRange), SM, clang::LangOptions());
783 if (SS.isValid()) {
784 DeclContext *DC = S.computeDeclContext(SS);
785 if (auto *NS = llvm::dyn_cast<NamespaceDecl>(DC)) {
786 Info.Resolved = NS->getQualifiedNameAsString();
Sam McCalle3e15702017-12-19 17:05:00 +0000787 } else if (llvm::dyn_cast<TranslationUnitDecl>(DC) != nullptr) {
Eric Liu6f648df2017-12-19 16:50:37 +0000788 Info.Resolved = "::";
789 // Sema does not include the suffix "::" in the range of SS, so we add
790 // it back here.
791 Info.Written = "::";
792 }
793 }
794 return Info;
795}
796
Sam McCall98775c52017-12-04 13:49:59 +0000797} // namespace
798
799clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
800 clang::CodeCompleteOptions Result;
801 Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
802 Result.IncludeMacros = IncludeMacros;
803 Result.IncludeGlobals = IncludeGlobals;
804 Result.IncludeBriefComments = IncludeBriefComments;
805
Eric Liu6f648df2017-12-19 16:50:37 +0000806 // Enable index-based code completion when Index is provided.
807 Result.IncludeNamespaceLevelDecls = !Index;
808
Sam McCall98775c52017-12-04 13:49:59 +0000809 return Result;
810}
811
Ilya Biryukov940901e2017-12-13 12:51:22 +0000812CompletionList codeComplete(const Context &Ctx, PathRef FileName,
Sam McCall98775c52017-12-04 13:49:59 +0000813 const tooling::CompileCommand &Command,
814 PrecompiledPreamble const *Preamble,
815 StringRef Contents, Position Pos,
816 IntrusiveRefCntPtr<vfs::FileSystem> VFS,
817 std::shared_ptr<PCHContainerOperations> PCHs,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000818 CodeCompleteOptions Opts) {
Sam McCall98775c52017-12-04 13:49:59 +0000819 CompletionList Results;
820 std::unique_ptr<CodeCompleteConsumer> Consumer;
Eric Liu6f648df2017-12-19 16:50:37 +0000821 NameToComplete CompletedName;
Sam McCall98775c52017-12-04 13:49:59 +0000822 if (Opts.EnableSnippets) {
Eric Liu6f648df2017-12-19 16:50:37 +0000823 Consumer = llvm::make_unique<SnippetCompletionItemsCollector>(
824 Opts, Results, CompletedName);
Sam McCall98775c52017-12-04 13:49:59 +0000825 } else {
Eric Liu6f648df2017-12-19 16:50:37 +0000826 Consumer = llvm::make_unique<PlainTextCompletionItemsCollector>(
827 Opts, Results, CompletedName);
Sam McCall98775c52017-12-04 13:49:59 +0000828 }
Ilya Biryukov940901e2017-12-13 12:51:22 +0000829 invokeCodeComplete(Ctx, std::move(Consumer), Opts.getClangCompleteOpts(),
830 FileName, Command, Preamble, Contents, Pos, std::move(VFS),
831 std::move(PCHs));
Eric Liu6f648df2017-12-19 16:50:37 +0000832 if (Opts.Index && CompletedName.SSInfo) {
Eric Liudc641eb2017-12-19 18:10:32 +0000833 if (!Results.items.empty())
834 log(Ctx, "WARNING: Got completion results from sema for completion on "
835 "qualified ID while symbol index is provided.");
Eric Liu6f648df2017-12-19 16:50:37 +0000836 Results.items.clear();
837 completeWithIndex(Ctx, *Opts.Index, Contents, *CompletedName.SSInfo,
838 CompletedName.Filter, &Results);
839 }
Sam McCall98775c52017-12-04 13:49:59 +0000840 return Results;
841}
842
Ilya Biryukov940901e2017-12-13 12:51:22 +0000843SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName,
844 const tooling::CompileCommand &Command,
845 PrecompiledPreamble const *Preamble,
846 StringRef Contents, Position Pos,
847 IntrusiveRefCntPtr<vfs::FileSystem> VFS,
848 std::shared_ptr<PCHContainerOperations> PCHs) {
Sam McCall98775c52017-12-04 13:49:59 +0000849 SignatureHelp Result;
850 clang::CodeCompleteOptions Options;
851 Options.IncludeGlobals = false;
852 Options.IncludeMacros = false;
853 Options.IncludeCodePatterns = false;
854 Options.IncludeBriefComments = true;
Ilya Biryukov940901e2017-12-13 12:51:22 +0000855 invokeCodeComplete(Ctx,
856 llvm::make_unique<SignatureHelpCollector>(Options, Result),
Sam McCall98775c52017-12-04 13:49:59 +0000857 Options, FileName, Command, Preamble, Contents, Pos,
Ilya Biryukov940901e2017-12-13 12:51:22 +0000858 std::move(VFS), std::move(PCHs));
Sam McCall98775c52017-12-04 13:49:59 +0000859 return Result;
860}
861
862} // namespace clangd
863} // namespace clang