Mercurial > hg > CbC > CbC_llvm
diff clang-tools-extra/clangd/CodeComplete.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 0572611fdcc8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clang-tools-extra/clangd/CodeComplete.cpp Thu Feb 13 15:10:13 2020 +0900 @@ -0,0 +1,1872 @@ +//===--- CodeComplete.cpp ----------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Code completion has several moving parts: +// - AST-based completions are provided using the completion hooks in Sema. +// - external completions are retrieved from the index (using hints from Sema) +// - the two sources overlap, and must be merged and overloads bundled +// - results must be scored and ranked (see Quality.h) before rendering +// +// Signature help works in a similar way as code completion, but it is simpler: +// it's purely AST-based, and there are few candidates. +// +//===----------------------------------------------------------------------===// + +#include "CodeComplete.h" +#include "AST.h" +#include "CodeCompletionStrings.h" +#include "Compiler.h" +#include "Diagnostics.h" +#include "ExpectedTypes.h" +#include "FileDistance.h" +#include "FuzzyMatch.h" +#include "Headers.h" +#include "Logger.h" +#include "Preamble.h" +#include "Protocol.h" +#include "Quality.h" +#include "SourceCode.h" +#include "TUScheduler.h" +#include "Threading.h" +#include "Trace.h" +#include "URI.h" +#include "index/Index.h" +#include "index/Symbol.h" +#include "index/SymbolOrigin.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Format/Format.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/ExternalPreprocessorSource.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ScopedPrinter.h" +#include <algorithm> +#include <iterator> + +// We log detailed candidate here if you run with -debug-only=codecomplete. +#define DEBUG_TYPE "CodeComplete" + +namespace clang { +namespace clangd { +namespace { + +CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) { + using SK = index::SymbolKind; + switch (Kind) { + case SK::Unknown: + return CompletionItemKind::Missing; + case SK::Module: + case SK::Namespace: + case SK::NamespaceAlias: + return CompletionItemKind::Module; + case SK::Macro: + return CompletionItemKind::Text; + case SK::Enum: + return CompletionItemKind::Enum; + // FIXME(ioeric): use LSP struct instead of class when it is suppoted in the + // protocol. + case SK::Struct: + case SK::Class: + case SK::Protocol: + case SK::Extension: + case SK::Union: + return CompletionItemKind::Class; + case SK::TypeAlias: + // We use the same kind as the VSCode C++ extension. + // FIXME: pick a better option when we have one. + return CompletionItemKind::Interface; + case SK::Using: + return CompletionItemKind::Reference; + case SK::Function: + // FIXME(ioeric): this should probably be an operator. This should be fixed + // when `Operator` is support type in the protocol. + case SK::ConversionFunction: + return CompletionItemKind::Function; + case SK::Variable: + case SK::Parameter: + return CompletionItemKind::Variable; + case SK::Field: + return CompletionItemKind::Field; + // FIXME(ioeric): use LSP enum constant when it is supported in the protocol. + case SK::EnumConstant: + return CompletionItemKind::Value; + case SK::InstanceMethod: + case SK::ClassMethod: + case SK::StaticMethod: + case SK::Destructor: + return CompletionItemKind::Method; + case SK::InstanceProperty: + case SK::ClassProperty: + case SK::StaticProperty: + return CompletionItemKind::Property; + case SK::Constructor: + return CompletionItemKind::Constructor; + } + llvm_unreachable("Unhandled clang::index::SymbolKind."); +} + +CompletionItemKind +toCompletionItemKind(CodeCompletionResult::ResultKind ResKind, + const NamedDecl *Decl, + CodeCompletionContext::Kind CtxKind) { + if (Decl) + return toCompletionItemKind(index::getSymbolInfo(Decl).Kind); + if (CtxKind == CodeCompletionContext::CCC_IncludedFile) + return CompletionItemKind::File; + switch (ResKind) { + case CodeCompletionResult::RK_Declaration: + llvm_unreachable("RK_Declaration without Decl"); + case CodeCompletionResult::RK_Keyword: + return CompletionItemKind::Keyword; + case CodeCompletionResult::RK_Macro: + return CompletionItemKind::Text; // unfortunately, there's no 'Macro' + // completion items in LSP. + case CodeCompletionResult::RK_Pattern: + return CompletionItemKind::Snippet; + } + llvm_unreachable("Unhandled CodeCompletionResult::ResultKind."); +} + +// Identifier code completion result. +struct RawIdentifier { + llvm::StringRef Name; + unsigned References; // # of usages in file. +}; + +/// A code completion result, in clang-native form. +/// It may be promoted to a CompletionItem if it's among the top-ranked results. +struct CompletionCandidate { + llvm::StringRef Name; // Used for filtering and sorting. + // We may have a result from Sema, from the index, or both. + const CodeCompletionResult *SemaResult = nullptr; + const Symbol *IndexResult = nullptr; + const RawIdentifier *IdentifierResult = nullptr; + llvm::SmallVector<llvm::StringRef, 1> RankedIncludeHeaders; + + // Returns a token identifying the overload set this is part of. + // 0 indicates it's not part of any overload set. + size_t overloadSet(const CodeCompleteOptions &Opts) const { + if (!Opts.BundleOverloads.getValueOr(false)) + return 0; + llvm::SmallString<256> Scratch; + if (IndexResult) { + switch (IndexResult->SymInfo.Kind) { + case index::SymbolKind::ClassMethod: + case index::SymbolKind::InstanceMethod: + case index::SymbolKind::StaticMethod: +#ifndef NDEBUG + llvm_unreachable("Don't expect members from index in code completion"); +#else + LLVM_FALLTHROUGH; +#endif + case index::SymbolKind::Function: + // We can't group overloads together that need different #includes. + // This could break #include insertion. + return llvm::hash_combine( + (IndexResult->Scope + IndexResult->Name).toStringRef(Scratch), + headerToInsertIfAllowed(Opts).getValueOr("")); + default: + return 0; + } + } + if (SemaResult) { + // We need to make sure we're consistent with the IndexResult case! + const NamedDecl *D = SemaResult->Declaration; + if (!D || !D->isFunctionOrFunctionTemplate()) + return 0; + { + llvm::raw_svector_ostream OS(Scratch); + D->printQualifiedName(OS); + } + return llvm::hash_combine(Scratch, + headerToInsertIfAllowed(Opts).getValueOr("")); + } + assert(IdentifierResult); + return 0; + } + + // The best header to include if include insertion is allowed. + llvm::Optional<llvm::StringRef> + headerToInsertIfAllowed(const CodeCompleteOptions &Opts) const { + if (Opts.InsertIncludes == CodeCompleteOptions::NeverInsert || + RankedIncludeHeaders.empty()) + return None; + if (SemaResult && SemaResult->Declaration) { + // Avoid inserting new #include if the declaration is found in the current + // file e.g. the symbol is forward declared. + auto &SM = SemaResult->Declaration->getASTContext().getSourceManager(); + for (const Decl *RD : SemaResult->Declaration->redecls()) + if (SM.isInMainFile(SM.getExpansionLoc(RD->getBeginLoc()))) + return None; + } + return RankedIncludeHeaders[0]; + } + + using Bundle = llvm::SmallVector<CompletionCandidate, 4>; +}; +using ScoredBundle = + std::pair<CompletionCandidate::Bundle, CodeCompletion::Scores>; +struct ScoredBundleGreater { + bool operator()(const ScoredBundle &L, const ScoredBundle &R) { + if (L.second.Total != R.second.Total) + return L.second.Total > R.second.Total; + return L.first.front().Name < + R.first.front().Name; // Earlier name is better. + } +}; + +// Assembles a code completion out of a bundle of >=1 completion candidates. +// Many of the expensive strings are only computed at this point, once we know +// the candidate bundle is going to be returned. +// +// Many fields are the same for all candidates in a bundle (e.g. name), and are +// computed from the first candidate, in the constructor. +// Others vary per candidate, so add() must be called for remaining candidates. +struct CodeCompletionBuilder { + CodeCompletionBuilder(ASTContext *ASTCtx, const CompletionCandidate &C, + CodeCompletionString *SemaCCS, + llvm::ArrayRef<std::string> QueryScopes, + const IncludeInserter &Includes, + llvm::StringRef FileName, + CodeCompletionContext::Kind ContextKind, + const CodeCompleteOptions &Opts, bool GenerateSnippets) + : ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments), + EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets), + GenerateSnippets(GenerateSnippets) { + add(C, SemaCCS); + if (C.SemaResult) { + assert(ASTCtx); + Completion.Origin |= SymbolOrigin::AST; + Completion.Name = std::string(llvm::StringRef(SemaCCS->getTypedText())); + if (Completion.Scope.empty()) { + if ((C.SemaResult->Kind == CodeCompletionResult::RK_Declaration) || + (C.SemaResult->Kind == CodeCompletionResult::RK_Pattern)) + if (const auto *D = C.SemaResult->getDeclaration()) + if (const auto *ND = dyn_cast<NamedDecl>(D)) + Completion.Scope = std::string( + splitQualifiedName(printQualifiedName(*ND)).first); + } + Completion.Kind = toCompletionItemKind( + C.SemaResult->Kind, C.SemaResult->Declaration, ContextKind); + // Sema could provide more info on whether the completion was a file or + // folder. + if (Completion.Kind == CompletionItemKind::File && + Completion.Name.back() == '/') + Completion.Kind = CompletionItemKind::Folder; + for (const auto &FixIt : C.SemaResult->FixIts) { + Completion.FixIts.push_back(toTextEdit( + FixIt, ASTCtx->getSourceManager(), ASTCtx->getLangOpts())); + } + llvm::sort(Completion.FixIts, [](const TextEdit &X, const TextEdit &Y) { + return std::tie(X.range.start.line, X.range.start.character) < + std::tie(Y.range.start.line, Y.range.start.character); + }); + Completion.Deprecated |= + (C.SemaResult->Availability == CXAvailability_Deprecated); + } + if (C.IndexResult) { + Completion.Origin |= C.IndexResult->Origin; + if (Completion.Scope.empty()) + Completion.Scope = std::string(C.IndexResult->Scope); + if (Completion.Kind == CompletionItemKind::Missing) + Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind); + if (Completion.Name.empty()) + Completion.Name = std::string(C.IndexResult->Name); + // If the completion was visible to Sema, no qualifier is needed. This + // avoids unneeded qualifiers in cases like with `using ns::X`. + if (Completion.RequiredQualifier.empty() && !C.SemaResult) { + llvm::StringRef ShortestQualifier = C.IndexResult->Scope; + for (llvm::StringRef Scope : QueryScopes) { + llvm::StringRef Qualifier = C.IndexResult->Scope; + if (Qualifier.consume_front(Scope) && + Qualifier.size() < ShortestQualifier.size()) + ShortestQualifier = Qualifier; + } + Completion.RequiredQualifier = std::string(ShortestQualifier); + } + Completion.Deprecated |= (C.IndexResult->Flags & Symbol::Deprecated); + } + if (C.IdentifierResult) { + Completion.Origin |= SymbolOrigin::Identifier; + Completion.Kind = CompletionItemKind::Text; + Completion.Name = std::string(C.IdentifierResult->Name); + } + + // Turn absolute path into a literal string that can be #included. + auto Inserted = [&](llvm::StringRef Header) + -> llvm::Expected<std::pair<std::string, bool>> { + auto ResolvedDeclaring = + URI::resolve(C.IndexResult->CanonicalDeclaration.FileURI, FileName); + if (!ResolvedDeclaring) + return ResolvedDeclaring.takeError(); + auto ResolvedInserted = toHeaderFile(Header, FileName); + if (!ResolvedInserted) + return ResolvedInserted.takeError(); + auto Spelled = Includes.calculateIncludePath(*ResolvedInserted, FileName); + if (!Spelled) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Header not on include path"); + return std::make_pair( + std::move(*Spelled), + Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted)); + }; + bool ShouldInsert = C.headerToInsertIfAllowed(Opts).hasValue(); + // Calculate include paths and edits for all possible headers. + for (const auto &Inc : C.RankedIncludeHeaders) { + if (auto ToInclude = Inserted(Inc)) { + CodeCompletion::IncludeCandidate Include; + Include.Header = ToInclude->first; + if (ToInclude->second && ShouldInsert) + Include.Insertion = Includes.insert(ToInclude->first); + Completion.Includes.push_back(std::move(Include)); + } else + log("Failed to generate include insertion edits for adding header " + "(FileURI='{0}', IncludeHeader='{1}') into {2}: {3}", + C.IndexResult->CanonicalDeclaration.FileURI, Inc, FileName, + ToInclude.takeError()); + } + // Prefer includes that do not need edits (i.e. already exist). + std::stable_partition(Completion.Includes.begin(), + Completion.Includes.end(), + [](const CodeCompletion::IncludeCandidate &I) { + return !I.Insertion.hasValue(); + }); + } + + void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) { + assert(bool(C.SemaResult) == bool(SemaCCS)); + Bundled.emplace_back(); + BundledEntry &S = Bundled.back(); + if (C.SemaResult) { + bool IsPattern = C.SemaResult->Kind == CodeCompletionResult::RK_Pattern; + getSignature(*SemaCCS, &S.Signature, &S.SnippetSuffix, + &Completion.RequiredQualifier, IsPattern); + S.ReturnType = getReturnType(*SemaCCS); + } else if (C.IndexResult) { + S.Signature = std::string(C.IndexResult->Signature); + S.SnippetSuffix = std::string(C.IndexResult->CompletionSnippetSuffix); + S.ReturnType = std::string(C.IndexResult->ReturnType); + } + if (ExtractDocumentation && Completion.Documentation.empty()) { + if (C.IndexResult) + Completion.Documentation = std::string(C.IndexResult->Documentation); + else if (C.SemaResult) + Completion.Documentation = getDocComment(*ASTCtx, *C.SemaResult, + /*CommentsFromHeader=*/false); + } + } + + CodeCompletion build() { + Completion.ReturnType = summarizeReturnType(); + Completion.Signature = summarizeSignature(); + Completion.SnippetSuffix = summarizeSnippet(); + Completion.BundleSize = Bundled.size(); + return std::move(Completion); + } + +private: + struct BundledEntry { + std::string SnippetSuffix; + std::string Signature; + std::string ReturnType; + }; + + // If all BundledEntries have the same value for a property, return it. + template <std::string BundledEntry::*Member> + const std::string *onlyValue() const { + auto B = Bundled.begin(), E = Bundled.end(); + for (auto I = B + 1; I != E; ++I) + if (I->*Member != B->*Member) + return nullptr; + return &(B->*Member); + } + + template <bool BundledEntry::*Member> const bool *onlyValue() const { + auto B = Bundled.begin(), E = Bundled.end(); + for (auto I = B + 1; I != E; ++I) + if (I->*Member != B->*Member) + return nullptr; + return &(B->*Member); + } + + std::string summarizeReturnType() const { + if (auto *RT = onlyValue<&BundledEntry::ReturnType>()) + return *RT; + return ""; + } + + std::string summarizeSnippet() const { + if (!GenerateSnippets) + return ""; + auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>(); + if (!Snippet) + // All bundles are function calls. + // FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g. + // we need to complete 'forward<$1>($0)'. + return "($0)"; + if (EnableFunctionArgSnippets) + return *Snippet; + + // Replace argument snippets with a simplified pattern. + if (Snippet->empty()) + return ""; + if (Completion.Kind == CompletionItemKind::Function || + Completion.Kind == CompletionItemKind::Method) { + // Functions snippets can be of 2 types: + // - containing only function arguments, e.g. + // foo(${1:int p1}, ${2:int p2}); + // We transform this pattern to '($0)' or '()'. + // - template arguments and function arguments, e.g. + // foo<${1:class}>(${2:int p1}). + // We transform this pattern to '<$1>()$0' or '<$0>()'. + + bool EmptyArgs = llvm::StringRef(*Snippet).endswith("()"); + if (Snippet->front() == '<') + return EmptyArgs ? "<$1>()$0" : "<$1>($0)"; + if (Snippet->front() == '(') + return EmptyArgs ? "()" : "($0)"; + return *Snippet; // Not an arg snippet? + } + // 'CompletionItemKind::Interface' matches template type aliases. + if (Completion.Kind == CompletionItemKind::Interface || + Completion.Kind == CompletionItemKind::Class) { + if (Snippet->front() != '<') + return *Snippet; // Not an arg snippet? + + // Classes and template using aliases can only have template arguments, + // e.g. Foo<${1:class}>. + if (llvm::StringRef(*Snippet).endswith("<>")) + return "<>"; // can happen with defaulted template arguments. + return "<$0>"; + } + return *Snippet; + } + + std::string summarizeSignature() const { + if (auto *Signature = onlyValue<&BundledEntry::Signature>()) + return *Signature; + // All bundles are function calls. + return "(…)"; + } + + // ASTCtx can be nullptr if not run with sema. + ASTContext *ASTCtx; + CodeCompletion Completion; + llvm::SmallVector<BundledEntry, 1> Bundled; + bool ExtractDocumentation; + bool EnableFunctionArgSnippets; + /// When false, no snippets are generated argument lists. + bool GenerateSnippets; +}; + +// Determine the symbol ID for a Sema code completion result, if possible. +llvm::Optional<SymbolID> getSymbolID(const CodeCompletionResult &R, + const SourceManager &SM) { + switch (R.Kind) { + case CodeCompletionResult::RK_Declaration: + case CodeCompletionResult::RK_Pattern: { + // Computing USR caches linkage, which may change after code completion. + if (hasUnstableLinkage(R.Declaration)) + return llvm::None; + return clang::clangd::getSymbolID(R.Declaration); + } + case CodeCompletionResult::RK_Macro: + return clang::clangd::getSymbolID(R.Macro->getName(), R.MacroDefInfo, SM); + case CodeCompletionResult::RK_Keyword: + return None; + } + llvm_unreachable("unknown CodeCompletionResult kind"); +} + +// Scopes of the partial identifier we're trying to complete. +// It is used when we query the index for more completion results. +struct SpecifiedScope { + // The scopes we should look in, determined by Sema. + // + // If the qualifier was fully resolved, we look for completions in these + // scopes; if there is an unresolved part of the qualifier, it should be + // resolved within these scopes. + // + // Examples of qualified completion: + // + // "::vec" => {""} + // "using namespace std; ::vec^" => {"", "std::"} + // "namespace ns {using namespace std;} ns::^" => {"ns::", "std::"} + // "std::vec^" => {""} // "std" unresolved + // + // Examples of unqualified completion: + // + // "vec^" => {""} + // "using namespace std; vec^" => {"", "std::"} + // "using namespace std; namespace ns { vec^ }" => {"ns::", "std::", ""} + // + // "" for global namespace, "ns::" for normal namespace. + std::vector<std::string> AccessibleScopes; + // The full scope qualifier as typed by the user (without the leading "::"). + // Set if the qualifier is not fully resolved by Sema. + llvm::Optional<std::string> UnresolvedQualifier; + + // Construct scopes being queried in indexes. The results are deduplicated. + // This method format the scopes to match the index request representation. + std::vector<std::string> scopesForIndexQuery() { + std::set<std::string> Results; + for (llvm::StringRef AS : AccessibleScopes) + Results.insert( + (AS + (UnresolvedQualifier ? *UnresolvedQualifier : "")).str()); + return {Results.begin(), Results.end()}; + } +}; + +// Get all scopes that will be queried in indexes and whether symbols from +// any scope is allowed. The first scope in the list is the preferred scope +// (e.g. enclosing namespace). +std::pair<std::vector<std::string>, bool> +getQueryScopes(CodeCompletionContext &CCContext, const Sema &CCSema, + const CompletionPrefix &HeuristicPrefix, + const CodeCompleteOptions &Opts) { + SpecifiedScope Scopes; + for (auto *Context : CCContext.getVisitedContexts()) { + if (isa<TranslationUnitDecl>(Context)) + Scopes.AccessibleScopes.push_back(""); // global namespace + else if (isa<NamespaceDecl>(Context)) + Scopes.AccessibleScopes.push_back(printNamespaceScope(*Context)); + } + + const CXXScopeSpec *SemaSpecifier = + CCContext.getCXXScopeSpecifier().getValueOr(nullptr); + // Case 1: unqualified completion. + if (!SemaSpecifier) { + // Case 2 (exception): sema saw no qualifier, but there appears to be one! + // This can happen e.g. in incomplete macro expansions. Use heuristics. + if (!HeuristicPrefix.Qualifier.empty()) { + vlog("Sema said no scope specifier, but we saw {0} in the source code", + HeuristicPrefix.Qualifier); + StringRef SpelledSpecifier = HeuristicPrefix.Qualifier; + if (SpelledSpecifier.consume_front("::")) + Scopes.AccessibleScopes = {""}; + Scopes.UnresolvedQualifier = std::string(SpelledSpecifier); + return {Scopes.scopesForIndexQuery(), false}; + } + // The enclosing namespace must be first, it gets a quality boost. + std::vector<std::string> EnclosingAtFront; + std::string EnclosingScope = printNamespaceScope(*CCSema.CurContext); + EnclosingAtFront.push_back(EnclosingScope); + for (auto &S : Scopes.scopesForIndexQuery()) { + if (EnclosingScope != S) + EnclosingAtFront.push_back(std::move(S)); + } + // Allow AllScopes completion as there is no explicit scope qualifier. + return {EnclosingAtFront, Opts.AllScopes}; + } + // Case 3: sema saw and resolved a scope qualifier. + if (SemaSpecifier && SemaSpecifier->isValid()) + return {Scopes.scopesForIndexQuery(), false}; + + // Case 4: There was a qualifier, and Sema didn't resolve it. + Scopes.AccessibleScopes.push_back(""); // Make sure global scope is included. + llvm::StringRef SpelledSpecifier = Lexer::getSourceText( + CharSourceRange::getCharRange(SemaSpecifier->getRange()), + CCSema.SourceMgr, clang::LangOptions()); + if (SpelledSpecifier.consume_front("::")) + Scopes.AccessibleScopes = {""}; + Scopes.UnresolvedQualifier = std::string(SpelledSpecifier); + // Sema excludes the trailing "::". + if (!Scopes.UnresolvedQualifier->empty()) + *Scopes.UnresolvedQualifier += "::"; + + return {Scopes.scopesForIndexQuery(), false}; +} + +// Should we perform index-based completion in a context of the specified kind? +// FIXME: consider allowing completion, but restricting the result types. +bool contextAllowsIndex(enum CodeCompletionContext::Kind K) { + switch (K) { + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Statement: + case CodeCompletionContext::CCC_Expression: + case CodeCompletionContext::CCC_ObjCMessageReceiver: + case CodeCompletionContext::CCC_EnumTag: + case CodeCompletionContext::CCC_UnionTag: + case CodeCompletionContext::CCC_ClassOrStructTag: + case CodeCompletionContext::CCC_ObjCProtocolName: + case CodeCompletionContext::CCC_Namespace: + case CodeCompletionContext::CCC_Type: + case CodeCompletionContext::CCC_ParenthesizedExpression: + case CodeCompletionContext::CCC_ObjCInterfaceName: + case CodeCompletionContext::CCC_ObjCCategoryName: + case CodeCompletionContext::CCC_Symbol: + case CodeCompletionContext::CCC_SymbolOrNewName: + return true; + case CodeCompletionContext::CCC_OtherWithMacros: + case CodeCompletionContext::CCC_DotMemberAccess: + case CodeCompletionContext::CCC_ArrowMemberAccess: + case CodeCompletionContext::CCC_ObjCPropertyAccess: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_MacroNameUse: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_SelectorName: + case CodeCompletionContext::CCC_TypeQualifiers: + case CodeCompletionContext::CCC_ObjCInstanceMessage: + case CodeCompletionContext::CCC_ObjCClassMessage: + case CodeCompletionContext::CCC_IncludedFile: + // FIXME: Provide identifier based completions for the following contexts: + case CodeCompletionContext::CCC_Other: // Be conservative. + case CodeCompletionContext::CCC_NaturalLanguage: + case CodeCompletionContext::CCC_Recovery: + case CodeCompletionContext::CCC_NewName: + return false; + } + llvm_unreachable("unknown code completion context"); +} + +static bool isInjectedClass(const NamedDecl &D) { + if (auto *R = dyn_cast_or_null<RecordDecl>(&D)) + if (R->isInjectedClassName()) + return true; + return false; +} + +// Some member calls are blacklisted because they're so rarely useful. +static bool isBlacklistedMember(const NamedDecl &D) { + // Destructor completion is rarely useful, and works inconsistently. + // (s.^ completes ~string, but s.~st^ is an error). + if (D.getKind() == Decl::CXXDestructor) + return true; + // Injected name may be useful for A::foo(), but who writes A::A::foo()? + if (isInjectedClass(D)) + return true; + // Explicit calls to operators are also rare. + auto NameKind = D.getDeclName().getNameKind(); + if (NameKind == DeclarationName::CXXOperatorName || + NameKind == DeclarationName::CXXLiteralOperatorName || + NameKind == DeclarationName::CXXConversionFunctionName) + return true; + return false; +} + +// The CompletionRecorder captures Sema code-complete output, including context. +// It filters out ignored results (but doesn't apply fuzzy-filtering yet). +// It doesn't do scoring or conversion to CompletionItem yet, as we want to +// merge with index results first. +// Generally the fields and methods of this object should only be used from +// within the callback. +struct CompletionRecorder : public CodeCompleteConsumer { + CompletionRecorder(const CodeCompleteOptions &Opts, + llvm::unique_function<void()> ResultsCallback) + : CodeCompleteConsumer(Opts.getClangCompleteOpts()), + CCContext(CodeCompletionContext::CCC_Other), Opts(Opts), + CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()), + CCTUInfo(CCAllocator), ResultsCallback(std::move(ResultsCallback)) { + assert(this->ResultsCallback); + } + + std::vector<CodeCompletionResult> Results; + CodeCompletionContext CCContext; + Sema *CCSema = nullptr; // Sema that created the results. + // FIXME: Sema is scary. Can we store ASTContext and Preprocessor, instead? + + void ProcessCodeCompleteResults(class Sema &S, CodeCompletionContext Context, + CodeCompletionResult *InResults, + unsigned NumResults) override final { + // Results from recovery mode are generally useless, and the callback after + // recovery (if any) is usually more interesting. To make sure we handle the + // future callback from sema, we just ignore all callbacks in recovery mode, + // as taking only results from recovery mode results in poor completion + // results. + // FIXME: in case there is no future sema completion callback after the + // recovery mode, we might still want to provide some results (e.g. trivial + // identifier-based completion). + if (Context.getKind() == CodeCompletionContext::CCC_Recovery) { + log("Code complete: Ignoring sema code complete callback with Recovery " + "context."); + return; + } + // If a callback is called without any sema result and the context does not + // support index-based completion, we simply skip it to give way to + // potential future callbacks with results. + if (NumResults == 0 && !contextAllowsIndex(Context.getKind())) + return; + if (CCSema) { + log("Multiple code complete callbacks (parser backtracked?). " + "Dropping results from context {0}, keeping results from {1}.", + getCompletionKindString(Context.getKind()), + getCompletionKindString(this->CCContext.getKind())); + return; + } + // Record the completion context. + CCSema = &S; + CCContext = Context; + + // Retain the results we might want. + for (unsigned I = 0; I < NumResults; ++I) { + auto &Result = InResults[I]; + // Class members that are shadowed by subclasses are usually noise. + if (Result.Hidden && Result.Declaration && + Result.Declaration->isCXXClassMember()) + continue; + if (!Opts.IncludeIneligibleResults && + (Result.Availability == CXAvailability_NotAvailable || + Result.Availability == CXAvailability_NotAccessible)) + continue; + if (Result.Declaration && + !Context.getBaseType().isNull() // is this a member-access context? + && isBlacklistedMember(*Result.Declaration)) + continue; + // Skip injected class name when no class scope is not explicitly set. + // E.g. show injected A::A in `using A::A^` but not in "A^". + if (Result.Declaration && !Context.getCXXScopeSpecifier().hasValue() && + isInjectedClass(*Result.Declaration)) + continue; + // We choose to never append '::' to completion results in clangd. + Result.StartsNestedNameSpecifier = false; + Results.push_back(Result); + } + ResultsCallback(); + } + + CodeCompletionAllocator &getAllocator() override { return *CCAllocator; } + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } + + // Returns the filtering/sorting name for Result, which must be from Results. + // Returned string is owned by this recorder (or the AST). + llvm::StringRef getName(const CodeCompletionResult &Result) { + switch (Result.Kind) { + case CodeCompletionResult::RK_Declaration: + if (auto *ID = Result.Declaration->getIdentifier()) + return ID->getName(); + break; + case CodeCompletionResult::RK_Keyword: + return Result.Keyword; + case CodeCompletionResult::RK_Macro: + return Result.Macro->getName(); + case CodeCompletionResult::RK_Pattern: + return Result.Pattern->getTypedText(); + } + auto *CCS = codeCompletionString(Result); + return CCS->getTypedText(); + } + + // Build a CodeCompletion string for R, which must be from Results. + // The CCS will be owned by this recorder. + CodeCompletionString *codeCompletionString(const CodeCompletionResult &R) { + // CodeCompletionResult doesn't seem to be const-correct. We own it, anyway. + return const_cast<CodeCompletionResult &>(R).CreateCodeCompletionString( + *CCSema, CCContext, *CCAllocator, CCTUInfo, + /*IncludeBriefComments=*/false); + } + +private: + CodeCompleteOptions Opts; + std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator; + CodeCompletionTUInfo CCTUInfo; + llvm::unique_function<void()> ResultsCallback; +}; + +struct ScoredSignature { + // When set, requires documentation to be requested from the index with this + // ID. + llvm::Optional<SymbolID> IDForDoc; + SignatureInformation Signature; + SignatureQualitySignals Quality; +}; + +class SignatureHelpCollector final : public CodeCompleteConsumer { +public: + SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts, + const SymbolIndex *Index, SignatureHelp &SigHelp) + : CodeCompleteConsumer(CodeCompleteOpts), SigHelp(SigHelp), + Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()), + CCTUInfo(Allocator), Index(Index) {} + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates, + SourceLocation OpenParLoc) override { + assert(!OpenParLoc.isInvalid()); + SourceManager &SrcMgr = S.getSourceManager(); + OpenParLoc = SrcMgr.getFileLoc(OpenParLoc); + if (SrcMgr.isInMainFile(OpenParLoc)) + SigHelp.argListStart = sourceLocToPosition(SrcMgr, OpenParLoc); + else + elog("Location oustide main file in signature help: {0}", + OpenParLoc.printToString(SrcMgr)); + + std::vector<ScoredSignature> ScoredSignatures; + SigHelp.signatures.reserve(NumCandidates); + ScoredSignatures.reserve(NumCandidates); + // FIXME(rwols): How can we determine the "active overload candidate"? + // Right now the overloaded candidates seem to be provided in a "best fit" + // order, so I'm not too worried about this. + SigHelp.activeSignature = 0; + assert(CurrentArg <= (unsigned)std::numeric_limits<int>::max() && + "too many arguments"); + SigHelp.activeParameter = static_cast<int>(CurrentArg); + for (unsigned I = 0; I < NumCandidates; ++I) { + OverloadCandidate Candidate = Candidates[I]; + // We want to avoid showing instantiated signatures, because they may be + // long in some cases (e.g. when 'T' is substituted with 'std::string', we + // would get 'std::basic_string<char>'). + if (auto *Func = Candidate.getFunction()) { + if (auto *Pattern = Func->getTemplateInstantiationPattern()) + Candidate = OverloadCandidate(Pattern); + } + + const auto *CCS = Candidate.CreateSignatureString( + CurrentArg, S, *Allocator, CCTUInfo, true); + assert(CCS && "Expected the CodeCompletionString to be non-null"); + ScoredSignatures.push_back(processOverloadCandidate( + Candidate, *CCS, + Candidate.getFunction() + ? getDeclComment(S.getASTContext(), *Candidate.getFunction()) + : "")); + } + + // Sema does not load the docs from the preamble, so we need to fetch extra + // docs from the index instead. + llvm::DenseMap<SymbolID, std::string> FetchedDocs; + if (Index) { + LookupRequest IndexRequest; + for (const auto &S : ScoredSignatures) { + if (!S.IDForDoc) + continue; + IndexRequest.IDs.insert(*S.IDForDoc); + } + Index->lookup(IndexRequest, [&](const Symbol &S) { + if (!S.Documentation.empty()) + FetchedDocs[S.ID] = std::string(S.Documentation); + }); + log("SigHelp: requested docs for {0} symbols from the index, got {1} " + "symbols with non-empty docs in the response", + IndexRequest.IDs.size(), FetchedDocs.size()); + } + + llvm::sort(ScoredSignatures, [](const ScoredSignature &L, + const ScoredSignature &R) { + // Ordering follows: + // - Less number of parameters is better. + // - Function is better than FunctionType which is better than + // Function Template. + // - High score is better. + // - Shorter signature is better. + // - Alphabetically smaller is better. + if (L.Quality.NumberOfParameters != R.Quality.NumberOfParameters) + return L.Quality.NumberOfParameters < R.Quality.NumberOfParameters; + if (L.Quality.NumberOfOptionalParameters != + R.Quality.NumberOfOptionalParameters) + return L.Quality.NumberOfOptionalParameters < + R.Quality.NumberOfOptionalParameters; + if (L.Quality.Kind != R.Quality.Kind) { + using OC = CodeCompleteConsumer::OverloadCandidate; + switch (L.Quality.Kind) { + case OC::CK_Function: + return true; + case OC::CK_FunctionType: + return R.Quality.Kind != OC::CK_Function; + case OC::CK_FunctionTemplate: + return false; + } + llvm_unreachable("Unknown overload candidate type."); + } + if (L.Signature.label.size() != R.Signature.label.size()) + return L.Signature.label.size() < R.Signature.label.size(); + return L.Signature.label < R.Signature.label; + }); + + for (auto &SS : ScoredSignatures) { + auto IndexDocIt = + SS.IDForDoc ? FetchedDocs.find(*SS.IDForDoc) : FetchedDocs.end(); + if (IndexDocIt != FetchedDocs.end()) + SS.Signature.documentation = IndexDocIt->second; + + SigHelp.signatures.push_back(std::move(SS.Signature)); + } + } + + GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } + +private: + void processParameterChunk(llvm::StringRef ChunkText, + SignatureInformation &Signature) const { + // (!) this is O(n), should still be fast compared to building ASTs. + unsigned ParamStartOffset = lspLength(Signature.label); + unsigned ParamEndOffset = ParamStartOffset + lspLength(ChunkText); + // A piece of text that describes the parameter that corresponds to + // the code-completion location within a function call, message send, + // macro invocation, etc. + Signature.label += ChunkText; + ParameterInformation Info; + Info.labelOffsets.emplace(ParamStartOffset, ParamEndOffset); + // FIXME: only set 'labelOffsets' when all clients migrate out of it. + Info.labelString = std::string(ChunkText); + + Signature.parameters.push_back(std::move(Info)); + } + + void processOptionalChunk(const CodeCompletionString &CCS, + SignatureInformation &Signature, + SignatureQualitySignals &Signal) const { + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_Optional: + assert(Chunk.Optional && + "Expected the optional code completion string to be non-null."); + processOptionalChunk(*Chunk.Optional, Signature, Signal); + break; + case CodeCompletionString::CK_VerticalSpace: + break; + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Placeholder: + processParameterChunk(Chunk.Text, Signature); + Signal.NumberOfOptionalParameters++; + break; + default: + Signature.label += Chunk.Text; + break; + } + } + } + + // FIXME(ioeric): consider moving CodeCompletionString logic here to + // CompletionString.h. + ScoredSignature processOverloadCandidate(const OverloadCandidate &Candidate, + const CodeCompletionString &CCS, + llvm::StringRef DocComment) const { + SignatureInformation Signature; + SignatureQualitySignals Signal; + const char *ReturnType = nullptr; + + Signature.documentation = formatDocumentation(CCS, DocComment); + Signal.Kind = Candidate.getKind(); + + for (const auto &Chunk : CCS) { + switch (Chunk.Kind) { + case CodeCompletionString::CK_ResultType: + // A piece of text that describes the type of an entity or, + // for functions and methods, the return type. + assert(!ReturnType && "Unexpected CK_ResultType"); + ReturnType = Chunk.Text; + break; + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Placeholder: + processParameterChunk(Chunk.Text, Signature); + Signal.NumberOfParameters++; + break; + case CodeCompletionString::CK_Optional: { + // The rest of the parameters are defaulted/optional. + assert(Chunk.Optional && + "Expected the optional code completion string to be non-null."); + processOptionalChunk(*Chunk.Optional, Signature, Signal); + break; + } + case CodeCompletionString::CK_VerticalSpace: + break; + default: + Signature.label += Chunk.Text; + break; + } + } + if (ReturnType) { + Signature.label += " -> "; + Signature.label += ReturnType; + } + dlog("Signal for {0}: {1}", Signature, Signal); + ScoredSignature Result; + Result.Signature = std::move(Signature); + Result.Quality = Signal; + const FunctionDecl *Func = Candidate.getFunction(); + if (Func && Result.Signature.documentation.empty()) { + // Computing USR caches linkage, which may change after code completion. + if (!hasUnstableLinkage(Func)) + Result.IDForDoc = clangd::getSymbolID(Func); + } + return Result; + } + + SignatureHelp &SigHelp; + std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator; + CodeCompletionTUInfo CCTUInfo; + const SymbolIndex *Index; +}; // SignatureHelpCollector + +struct SemaCompleteInput { + PathRef FileName; + const tooling::CompileCommand &Command; + const PreambleData *Preamble; + llvm::StringRef Contents; + size_t Offset; + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS; +}; + +void loadMainFilePreambleMacros(const Preprocessor &PP, + const PreambleData &Preamble) { + // The ExternalPreprocessorSource has our macros, if we know where to look. + // We can read all the macros using PreambleMacros->ReadDefinedMacros(), + // but this includes transitively included files, so may deserialize a lot. + ExternalPreprocessorSource *PreambleMacros = PP.getExternalSource(); + // As we have the names of the macros, we can look up their IdentifierInfo + // and then use this to load just the macros we want. + IdentifierInfoLookup *PreambleIdentifiers = + PP.getIdentifierTable().getExternalIdentifierLookup(); + if (!PreambleIdentifiers || !PreambleMacros) + return; + for (const auto &MacroName : Preamble.Macros.Names) + if (auto *II = PreambleIdentifiers->get(MacroName.getKey())) + if (II->isOutOfDate()) + PreambleMacros->updateOutOfDateIdentifier(*II); +} + +// Invokes Sema code completion on a file. +// If \p Includes is set, it will be updated based on the compiler invocation. +bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer, + const clang::CodeCompleteOptions &Options, + const SemaCompleteInput &Input, + IncludeStructure *Includes = nullptr) { + trace::Span Tracer("Sema completion"); + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = Input.VFS; + if (Input.Preamble && Input.Preamble->StatCache) + VFS = Input.Preamble->StatCache->getConsumingFS(std::move(VFS)); + ParseInputs ParseInput; + ParseInput.CompileCommand = Input.Command; + ParseInput.FS = VFS; + ParseInput.Contents = std::string(Input.Contents); + ParseInput.Opts = ParseOptions(); + + IgnoreDiagnostics IgnoreDiags; + auto CI = buildCompilerInvocation(ParseInput, IgnoreDiags); + if (!CI) { + elog("Couldn't create CompilerInvocation"); + return false; + } + auto &FrontendOpts = CI->getFrontendOpts(); + FrontendOpts.SkipFunctionBodies = true; + // Disable typo correction in Sema. + CI->getLangOpts()->SpellChecking = false; + // Setup code completion. + FrontendOpts.CodeCompleteOpts = Options; + FrontendOpts.CodeCompletionAt.FileName = std::string(Input.FileName); + std::tie(FrontendOpts.CodeCompletionAt.Line, + FrontendOpts.CodeCompletionAt.Column) = + offsetToClangLineColumn(Input.Contents, Input.Offset); + + std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer = + llvm::MemoryBuffer::getMemBufferCopy(Input.Contents, Input.FileName); + // The diagnostic options must be set before creating a CompilerInstance. + CI->getDiagnosticOpts().IgnoreWarnings = true; + // We reuse the preamble whether it's valid or not. This is a + // correctness/performance tradeoff: building without a preamble is slow, and + // completion is latency-sensitive. + // However, if we're completing *inside* the preamble section of the draft, + // overriding the preamble will break sema completion. Fortunately we can just + // skip all includes in this case; these completions are really simple. + PreambleBounds PreambleRegion = + ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0); + bool CompletingInPreamble = PreambleRegion.Size > Input.Offset; + // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise + // the remapped buffers do not get freed. + auto Clang = prepareCompilerInstance( + std::move(CI), + (Input.Preamble && !CompletingInPreamble) ? &Input.Preamble->Preamble + : nullptr, + std::move(ContentsBuffer), std::move(VFS), IgnoreDiags); + Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble; + Clang->setCodeCompletionConsumer(Consumer.release()); + + SyntaxOnlyAction Action; + if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { + log("BeginSourceFile() failed when running codeComplete for {0}", + Input.FileName); + return false; + } + // Macros can be defined within the preamble region of the main file. + // They don't fall nicely into our index/Sema dichotomy: + // - they're not indexed for completion (they're not available across files) + // - but Sema code complete won't see them: as part of the preamble, they're + // deserialized only when mentioned. + // Force them to be deserialized so SemaCodeComplete sees them. + if (Input.Preamble) + loadMainFilePreambleMacros(Clang->getPreprocessor(), *Input.Preamble); + if (Includes) + Clang->getPreprocessor().addPPCallbacks( + collectIncludeStructureCallback(Clang->getSourceManager(), Includes)); + if (llvm::Error Err = Action.Execute()) { + log("Execute() failed when running codeComplete for {0}: {1}", + Input.FileName, toString(std::move(Err))); + return false; + } + Action.EndSourceFile(); + + return true; +} + +// Should we allow index completions in the specified context? +bool allowIndex(CodeCompletionContext &CC) { + if (!contextAllowsIndex(CC.getKind())) + return false; + // We also avoid ClassName::bar (but allow namespace::bar). + auto Scope = CC.getCXXScopeSpecifier(); + if (!Scope) + return true; + NestedNameSpecifier *NameSpec = (*Scope)->getScopeRep(); + if (!NameSpec) + return true; + // We only query the index when qualifier is a namespace. + // If it's a class, we rely solely on sema completions. + switch (NameSpec->getKind()) { + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Namespace: + case NestedNameSpecifier::NamespaceAlias: + return true; + case NestedNameSpecifier::Super: + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + // Unresolved inside a template. + case NestedNameSpecifier::Identifier: + return false; + } + llvm_unreachable("invalid NestedNameSpecifier kind"); +} + +std::future<SymbolSlab> startAsyncFuzzyFind(const SymbolIndex &Index, + const FuzzyFindRequest &Req) { + return runAsync<SymbolSlab>([&Index, Req]() { + trace::Span Tracer("Async fuzzyFind"); + SymbolSlab::Builder Syms; + Index.fuzzyFind(Req, [&Syms](const Symbol &Sym) { Syms.insert(Sym); }); + return std::move(Syms).build(); + }); +} + +// Creates a `FuzzyFindRequest` based on the cached index request from the +// last completion, if any, and the speculated completion filter text in the +// source code. +FuzzyFindRequest speculativeFuzzyFindRequestForCompletion( + FuzzyFindRequest CachedReq, const CompletionPrefix &HeuristicPrefix) { + CachedReq.Query = std::string(HeuristicPrefix.Name); + return CachedReq; +} + +// Runs Sema-based (AST) and Index-based completion, returns merged results. +// +// There are a few tricky considerations: +// - the AST provides information needed for the index query (e.g. which +// namespaces to search in). So Sema must start first. +// - we only want to return the top results (Opts.Limit). +// Building CompletionItems for everything else is wasteful, so we want to +// preserve the "native" format until we're done with scoring. +// - the data underlying Sema completion items is owned by the AST and various +// other arenas, which must stay alive for us to build CompletionItems. +// - we may get duplicate results from Sema and the Index, we need to merge. +// +// So we start Sema completion first, and do all our work in its callback. +// We use the Sema context information to query the index. +// Then we merge the two result sets, producing items that are Sema/Index/Both. +// These items are scored, and the top N are synthesized into the LSP response. +// Finally, we can clean up the data structures created by Sema completion. +// +// Main collaborators are: +// - semaCodeComplete sets up the compiler machinery to run code completion. +// - CompletionRecorder captures Sema completion results, including context. +// - SymbolIndex (Opts.Index) provides index completion results as Symbols +// - CompletionCandidates are the result of merging Sema and Index results. +// Each candidate points to an underlying CodeCompletionResult (Sema), a +// Symbol (Index), or both. It computes the result quality score. +// CompletionCandidate also does conversion to CompletionItem (at the end). +// - FuzzyMatcher scores how the candidate matches the partial identifier. +// This score is combined with the result quality score for the final score. +// - TopN determines the results with the best score. +class CodeCompleteFlow { + PathRef FileName; + IncludeStructure Includes; // Complete once the compiler runs. + SpeculativeFuzzyFind *SpecFuzzyFind; // Can be nullptr. + const CodeCompleteOptions &Opts; + + // Sema takes ownership of Recorder. Recorder is valid until Sema cleanup. + CompletionRecorder *Recorder = nullptr; + CodeCompletionContext::Kind CCContextKind = CodeCompletionContext::CCC_Other; + bool IsUsingDeclaration = false; + // Counters for logging. + int NSema = 0, NIndex = 0, NSemaAndIndex = 0, NIdent = 0; + bool Incomplete = false; // Would more be available with a higher limit? + CompletionPrefix HeuristicPrefix; + llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema runs. + Range ReplacedRange; + std::vector<std::string> QueryScopes; // Initialized once Sema runs. + // Initialized once QueryScopes is initialized, if there are scopes. + llvm::Optional<ScopeDistance> ScopeProximity; + llvm::Optional<OpaqueType> PreferredType; // Initialized once Sema runs. + // Whether to query symbols from any scope. Initialized once Sema runs. + bool AllScopes = false; + llvm::StringSet<> ContextWords; + // Include-insertion and proximity scoring rely on the include structure. + // This is available after Sema has run. + llvm::Optional<IncludeInserter> Inserter; // Available during runWithSema. + llvm::Optional<URIDistance> FileProximity; // Initialized once Sema runs. + /// Speculative request based on the cached request and the filter text before + /// the cursor. + /// Initialized right before sema run. This is only set if `SpecFuzzyFind` is + /// set and contains a cached request. + llvm::Optional<FuzzyFindRequest> SpecReq; + +public: + // A CodeCompleteFlow object is only useful for calling run() exactly once. + CodeCompleteFlow(PathRef FileName, const IncludeStructure &Includes, + SpeculativeFuzzyFind *SpecFuzzyFind, + const CodeCompleteOptions &Opts) + : FileName(FileName), Includes(Includes), SpecFuzzyFind(SpecFuzzyFind), + Opts(Opts) {} + + CodeCompleteResult run(const SemaCompleteInput &SemaCCInput) && { + trace::Span Tracer("CodeCompleteFlow"); + HeuristicPrefix = + guessCompletionPrefix(SemaCCInput.Contents, SemaCCInput.Offset); + populateContextWords(SemaCCInput.Contents); + if (Opts.Index && SpecFuzzyFind && SpecFuzzyFind->CachedReq.hasValue()) { + assert(!SpecFuzzyFind->Result.valid()); + SpecReq = speculativeFuzzyFindRequestForCompletion( + *SpecFuzzyFind->CachedReq, HeuristicPrefix); + SpecFuzzyFind->Result = startAsyncFuzzyFind(*Opts.Index, *SpecReq); + } + + // We run Sema code completion first. It builds an AST and calculates: + // - completion results based on the AST. + // - partial identifier and context. We need these for the index query. + CodeCompleteResult Output; + auto RecorderOwner = std::make_unique<CompletionRecorder>(Opts, [&]() { + assert(Recorder && "Recorder is not set"); + CCContextKind = Recorder->CCContext.getKind(); + IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration(); + auto Style = getFormatStyleForFile( + SemaCCInput.FileName, SemaCCInput.Contents, SemaCCInput.VFS.get()); + // If preprocessor was run, inclusions from preprocessor callback should + // already be added to Includes. + Inserter.emplace( + SemaCCInput.FileName, SemaCCInput.Contents, Style, + SemaCCInput.Command.Directory, + &Recorder->CCSema->getPreprocessor().getHeaderSearchInfo()); + for (const auto &Inc : Includes.MainFileIncludes) + Inserter->addExisting(Inc); + + // Most of the cost of file proximity is in initializing the FileDistance + // structures based on the observed includes, once per query. Conceptually + // that happens here (though the per-URI-scheme initialization is lazy). + // The per-result proximity scoring is (amortized) very cheap. + FileDistanceOptions ProxOpts{}; // Use defaults. + const auto &SM = Recorder->CCSema->getSourceManager(); + llvm::StringMap<SourceParams> ProxSources; + for (auto &Entry : Includes.includeDepth( + SM.getFileEntryForID(SM.getMainFileID())->getName())) { + auto &Source = ProxSources[Entry.getKey()]; + Source.Cost = Entry.getValue() * ProxOpts.IncludeCost; + // Symbols near our transitive includes are good, but only consider + // things in the same directory or below it. Otherwise there can be + // many false positives. + if (Entry.getValue() > 0) + Source.MaxUpTraversals = 1; + } + FileProximity.emplace(ProxSources, ProxOpts); + + Output = runWithSema(); + Inserter.reset(); // Make sure this doesn't out-live Clang. + SPAN_ATTACH(Tracer, "sema_completion_kind", + getCompletionKindString(CCContextKind)); + log("Code complete: sema context {0}, query scopes [{1}] (AnyScope={2}), " + "expected type {3}{4}", + getCompletionKindString(CCContextKind), + llvm::join(QueryScopes.begin(), QueryScopes.end(), ","), AllScopes, + PreferredType ? Recorder->CCContext.getPreferredType().getAsString() + : "<none>", + IsUsingDeclaration ? ", inside using declaration" : ""); + }); + + Recorder = RecorderOwner.get(); + + semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(), + SemaCCInput, &Includes); + logResults(Output, Tracer); + return Output; + } + + void logResults(const CodeCompleteResult &Output, const trace::Span &Tracer) { + SPAN_ATTACH(Tracer, "sema_results", NSema); + SPAN_ATTACH(Tracer, "index_results", NIndex); + SPAN_ATTACH(Tracer, "merged_results", NSemaAndIndex); + SPAN_ATTACH(Tracer, "identifier_results", NIdent); + SPAN_ATTACH(Tracer, "returned_results", int64_t(Output.Completions.size())); + SPAN_ATTACH(Tracer, "incomplete", Output.HasMore); + log("Code complete: {0} results from Sema, {1} from Index, " + "{2} matched, {3} from identifiers, {4} returned{5}.", + NSema, NIndex, NSemaAndIndex, NIdent, Output.Completions.size(), + Output.HasMore ? " (incomplete)" : ""); + assert(!Opts.Limit || Output.Completions.size() <= Opts.Limit); + // We don't assert that isIncomplete means we hit a limit. + // Indexes may choose to impose their own limits even if we don't have one. + } + + CodeCompleteResult + runWithoutSema(llvm::StringRef Content, size_t Offset, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) && { + trace::Span Tracer("CodeCompleteWithoutSema"); + // Fill in fields normally set by runWithSema() + HeuristicPrefix = guessCompletionPrefix(Content, Offset); + populateContextWords(Content); + CCContextKind = CodeCompletionContext::CCC_Recovery; + IsUsingDeclaration = false; + Filter = FuzzyMatcher(HeuristicPrefix.Name); + auto Pos = offsetToPosition(Content, Offset); + ReplacedRange.start = ReplacedRange.end = Pos; + ReplacedRange.start.character -= HeuristicPrefix.Name.size(); + + llvm::StringMap<SourceParams> ProxSources; + ProxSources[FileName].Cost = 0; + FileProximity.emplace(ProxSources); + + auto Style = getFormatStyleForFile(FileName, Content, VFS.get()); + // This will only insert verbatim headers. + Inserter.emplace(FileName, Content, Style, + /*BuildDir=*/"", /*HeaderSearchInfo=*/nullptr); + + auto Identifiers = collectIdentifiers(Content, Style); + std::vector<RawIdentifier> IdentifierResults; + for (const auto &IDAndCount : Identifiers) { + RawIdentifier ID; + ID.Name = IDAndCount.first(); + ID.References = IDAndCount.second; + // Avoid treating typed filter as an identifier. + if (ID.Name == HeuristicPrefix.Name) + --ID.References; + if (ID.References > 0) + IdentifierResults.push_back(std::move(ID)); + } + + // Simplified version of getQueryScopes(): + // - accessible scopes are determined heuristically. + // - all-scopes query if no qualifier was typed (and it's allowed). + SpecifiedScope Scopes; + Scopes.AccessibleScopes = + visibleNamespaces(Content.take_front(Offset), Style); + for (std::string &S : Scopes.AccessibleScopes) + if (!S.empty()) + S.append("::"); // visibleNamespaces doesn't include trailing ::. + if (HeuristicPrefix.Qualifier.empty()) + AllScopes = Opts.AllScopes; + else if (HeuristicPrefix.Qualifier.startswith("::")) { + Scopes.AccessibleScopes = {""}; + Scopes.UnresolvedQualifier = + std::string(HeuristicPrefix.Qualifier.drop_front(2)); + } else + Scopes.UnresolvedQualifier = std::string(HeuristicPrefix.Qualifier); + // First scope is the (modified) enclosing scope. + QueryScopes = Scopes.scopesForIndexQuery(); + ScopeProximity.emplace(QueryScopes); + + SymbolSlab IndexResults = Opts.Index ? queryIndex() : SymbolSlab(); + + CodeCompleteResult Output = toCodeCompleteResult(mergeResults( + /*SemaResults=*/{}, IndexResults, IdentifierResults)); + Output.RanParser = false; + logResults(Output, Tracer); + return Output; + } + +private: + void populateContextWords(llvm::StringRef Content) { + // Take last 3 lines before the completion point. + unsigned RangeEnd = HeuristicPrefix.Qualifier.begin() - Content.data(), + RangeBegin = RangeEnd; + for (size_t I = 0; I < 3 && RangeBegin > 0; ++I) { + auto PrevNL = Content.rfind('\n', RangeBegin); + if (PrevNL == StringRef::npos) { + RangeBegin = 0; + break; + } + RangeBegin = PrevNL; + } + + ContextWords = collectWords(Content.slice(RangeBegin, RangeEnd)); + dlog("Completion context words: {0}", + llvm::join(ContextWords.keys(), ", ")); + } + + // This is called by run() once Sema code completion is done, but before the + // Sema data structures are torn down. It does all the real work. + CodeCompleteResult runWithSema() { + const auto &CodeCompletionRange = CharSourceRange::getCharRange( + Recorder->CCSema->getPreprocessor().getCodeCompletionTokenRange()); + // When we are getting completions with an empty identifier, for example + // std::vector<int> asdf; + // asdf.^; + // Then the range will be invalid and we will be doing insertion, use + // current cursor position in such cases as range. + if (CodeCompletionRange.isValid()) { + ReplacedRange = halfOpenToRange(Recorder->CCSema->getSourceManager(), + CodeCompletionRange); + } else { + const auto &Pos = sourceLocToPosition( + Recorder->CCSema->getSourceManager(), + Recorder->CCSema->getPreprocessor().getCodeCompletionLoc()); + ReplacedRange.start = ReplacedRange.end = Pos; + } + Filter = FuzzyMatcher( + Recorder->CCSema->getPreprocessor().getCodeCompletionFilter()); + std::tie(QueryScopes, AllScopes) = getQueryScopes( + Recorder->CCContext, *Recorder->CCSema, HeuristicPrefix, Opts); + if (!QueryScopes.empty()) + ScopeProximity.emplace(QueryScopes); + PreferredType = + OpaqueType::fromType(Recorder->CCSema->getASTContext(), + Recorder->CCContext.getPreferredType()); + // Sema provides the needed context to query the index. + // FIXME: in addition to querying for extra/overlapping symbols, we should + // explicitly request symbols corresponding to Sema results. + // We can use their signals even if the index can't suggest them. + // We must copy index results to preserve them, but there are at most Limit. + auto IndexResults = (Opts.Index && allowIndex(Recorder->CCContext)) + ? queryIndex() + : SymbolSlab(); + trace::Span Tracer("Populate CodeCompleteResult"); + // Merge Sema and Index results, score them, and pick the winners. + auto Top = + mergeResults(Recorder->Results, IndexResults, /*Identifiers*/ {}); + return toCodeCompleteResult(Top); + } + + CodeCompleteResult + toCodeCompleteResult(const std::vector<ScoredBundle> &Scored) { + CodeCompleteResult Output; + + // Convert the results to final form, assembling the expensive strings. + for (auto &C : Scored) { + Output.Completions.push_back(toCodeCompletion(C.first)); + Output.Completions.back().Score = C.second; + Output.Completions.back().CompletionTokenRange = ReplacedRange; + } + Output.HasMore = Incomplete; + Output.Context = CCContextKind; + Output.CompletionRange = ReplacedRange; + return Output; + } + + SymbolSlab queryIndex() { + trace::Span Tracer("Query index"); + SPAN_ATTACH(Tracer, "limit", int64_t(Opts.Limit)); + + // Build the query. + FuzzyFindRequest Req; + if (Opts.Limit) + Req.Limit = Opts.Limit; + Req.Query = std::string(Filter->pattern()); + Req.RestrictForCodeCompletion = true; + Req.Scopes = QueryScopes; + Req.AnyScope = AllScopes; + // FIXME: we should send multiple weighted paths here. + Req.ProximityPaths.push_back(std::string(FileName)); + if (PreferredType) + Req.PreferredTypes.push_back(std::string(PreferredType->raw())); + vlog("Code complete: fuzzyFind({0:2})", toJSON(Req)); + + if (SpecFuzzyFind) + SpecFuzzyFind->NewReq = Req; + if (SpecFuzzyFind && SpecFuzzyFind->Result.valid() && (*SpecReq == Req)) { + vlog("Code complete: speculative fuzzy request matches the actual index " + "request. Waiting for the speculative index results."); + SPAN_ATTACH(Tracer, "Speculative results", true); + + trace::Span WaitSpec("Wait speculative results"); + return SpecFuzzyFind->Result.get(); + } + + SPAN_ATTACH(Tracer, "Speculative results", false); + + // Run the query against the index. + SymbolSlab::Builder ResultsBuilder; + if (Opts.Index->fuzzyFind( + Req, [&](const Symbol &Sym) { ResultsBuilder.insert(Sym); })) + Incomplete = true; + return std::move(ResultsBuilder).build(); + } + + // Merges Sema and Index results where possible, to form CompletionCandidates. + // \p Identifiers is raw identifiers that can also be completion candidates. + // Identifiers are not merged with results from index or sema. + // Groups overloads if desired, to form CompletionCandidate::Bundles. The + // bundles are scored and top results are returned, best to worst. + std::vector<ScoredBundle> + mergeResults(const std::vector<CodeCompletionResult> &SemaResults, + const SymbolSlab &IndexResults, + const std::vector<RawIdentifier> &IdentifierResults) { + trace::Span Tracer("Merge and score results"); + std::vector<CompletionCandidate::Bundle> Bundles; + llvm::DenseMap<size_t, size_t> BundleLookup; + auto AddToBundles = [&](const CodeCompletionResult *SemaResult, + const Symbol *IndexResult, + const RawIdentifier *IdentifierResult) { + CompletionCandidate C; + C.SemaResult = SemaResult; + C.IndexResult = IndexResult; + C.IdentifierResult = IdentifierResult; + if (C.IndexResult) { + C.Name = IndexResult->Name; + C.RankedIncludeHeaders = getRankedIncludes(*C.IndexResult); + } else if (C.SemaResult) { + C.Name = Recorder->getName(*SemaResult); + } else { + assert(IdentifierResult); + C.Name = IdentifierResult->Name; + } + if (auto OverloadSet = C.overloadSet(Opts)) { + auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size()); + if (Ret.second) + Bundles.emplace_back(); + Bundles[Ret.first->second].push_back(std::move(C)); + } else { + Bundles.emplace_back(); + Bundles.back().push_back(std::move(C)); + } + }; + llvm::DenseSet<const Symbol *> UsedIndexResults; + auto CorrespondingIndexResult = + [&](const CodeCompletionResult &SemaResult) -> const Symbol * { + if (auto SymID = + getSymbolID(SemaResult, Recorder->CCSema->getSourceManager())) { + auto I = IndexResults.find(*SymID); + if (I != IndexResults.end()) { + UsedIndexResults.insert(&*I); + return &*I; + } + } + return nullptr; + }; + // Emit all Sema results, merging them with Index results if possible. + for (auto &SemaResult : SemaResults) + AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult), nullptr); + // Now emit any Index-only results. + for (const auto &IndexResult : IndexResults) { + if (UsedIndexResults.count(&IndexResult)) + continue; + AddToBundles(/*SemaResult=*/nullptr, &IndexResult, nullptr); + } + // Emit identifier results. + for (const auto &Ident : IdentifierResults) + AddToBundles(/*SemaResult=*/nullptr, /*IndexResult=*/nullptr, &Ident); + // We only keep the best N results at any time, in "native" format. + TopN<ScoredBundle, ScoredBundleGreater> Top( + Opts.Limit == 0 ? std::numeric_limits<size_t>::max() : Opts.Limit); + for (auto &Bundle : Bundles) + addCandidate(Top, std::move(Bundle)); + return std::move(Top).items(); + } + + llvm::Optional<float> fuzzyScore(const CompletionCandidate &C) { + // Macros can be very spammy, so we only support prefix completion. + // We won't end up with underfull index results, as macros are sema-only. + if (C.SemaResult && C.SemaResult->Kind == CodeCompletionResult::RK_Macro && + !C.Name.startswith_lower(Filter->pattern())) + return None; + return Filter->match(C.Name); + } + + // Scores a candidate and adds it to the TopN structure. + void addCandidate(TopN<ScoredBundle, ScoredBundleGreater> &Candidates, + CompletionCandidate::Bundle Bundle) { + SymbolQualitySignals Quality; + SymbolRelevanceSignals Relevance; + Relevance.Context = CCContextKind; + Relevance.Name = Bundle.front().Name; + Relevance.Query = SymbolRelevanceSignals::CodeComplete; + Relevance.FileProximityMatch = FileProximity.getPointer(); + if (ScopeProximity) + Relevance.ScopeProximityMatch = ScopeProximity.getPointer(); + if (PreferredType) + Relevance.HadContextType = true; + Relevance.ContextWords = &ContextWords; + + auto &First = Bundle.front(); + if (auto FuzzyScore = fuzzyScore(First)) + Relevance.NameMatch = *FuzzyScore; + else + return; + SymbolOrigin Origin = SymbolOrigin::Unknown; + bool FromIndex = false; + for (const auto &Candidate : Bundle) { + if (Candidate.IndexResult) { + Quality.merge(*Candidate.IndexResult); + Relevance.merge(*Candidate.IndexResult); + Origin |= Candidate.IndexResult->Origin; + FromIndex = true; + if (!Candidate.IndexResult->Type.empty()) + Relevance.HadSymbolType |= true; + if (PreferredType && + PreferredType->raw() == Candidate.IndexResult->Type) { + Relevance.TypeMatchesPreferred = true; + } + } + if (Candidate.SemaResult) { + Quality.merge(*Candidate.SemaResult); + Relevance.merge(*Candidate.SemaResult); + if (PreferredType) { + if (auto CompletionType = OpaqueType::fromCompletionResult( + Recorder->CCSema->getASTContext(), *Candidate.SemaResult)) { + Relevance.HadSymbolType |= true; + if (PreferredType == CompletionType) + Relevance.TypeMatchesPreferred = true; + } + } + Origin |= SymbolOrigin::AST; + } + if (Candidate.IdentifierResult) { + Quality.References = Candidate.IdentifierResult->References; + Relevance.Scope = SymbolRelevanceSignals::FileScope; + Origin |= SymbolOrigin::Identifier; + } + } + + CodeCompletion::Scores Scores; + Scores.Quality = Quality.evaluate(); + Scores.Relevance = Relevance.evaluate(); + Scores.Total = evaluateSymbolAndRelevance(Scores.Quality, Scores.Relevance); + // NameMatch is in fact a multiplier on total score, so rescoring is sound. + Scores.ExcludingName = Relevance.NameMatch + ? Scores.Total / Relevance.NameMatch + : Scores.Quality; + + dlog("CodeComplete: {0} ({1}) = {2}\n{3}{4}\n", First.Name, + llvm::to_string(Origin), Scores.Total, llvm::to_string(Quality), + llvm::to_string(Relevance)); + + NSema += bool(Origin & SymbolOrigin::AST); + NIndex += FromIndex; + NSemaAndIndex += bool(Origin & SymbolOrigin::AST) && FromIndex; + NIdent += bool(Origin & SymbolOrigin::Identifier); + if (Candidates.push({std::move(Bundle), Scores})) + Incomplete = true; + } + + CodeCompletion toCodeCompletion(const CompletionCandidate::Bundle &Bundle) { + llvm::Optional<CodeCompletionBuilder> Builder; + for (const auto &Item : Bundle) { + CodeCompletionString *SemaCCS = + Item.SemaResult ? Recorder->codeCompletionString(*Item.SemaResult) + : nullptr; + if (!Builder) + Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr, + Item, SemaCCS, QueryScopes, *Inserter, FileName, + CCContextKind, Opts, + /*GenerateSnippets=*/!IsUsingDeclaration); + else + Builder->add(Item, SemaCCS); + } + return Builder->build(); + } +}; + +} // namespace + +clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { + clang::CodeCompleteOptions Result; + Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns; + Result.IncludeMacros = IncludeMacros; + Result.IncludeGlobals = true; + // We choose to include full comments and not do doxygen parsing in + // completion. + // FIXME: ideally, we should support doxygen in some form, e.g. do markdown + // formatting of the comments. + Result.IncludeBriefComments = false; + + // When an is used, Sema is responsible for completing the main file, + // the index can provide results from the preamble. + // Tell Sema not to deserialize the preamble to look for results. + Result.LoadExternal = !Index; + Result.IncludeFixIts = IncludeFixIts; + + return Result; +} + +CompletionPrefix guessCompletionPrefix(llvm::StringRef Content, + unsigned Offset) { + assert(Offset <= Content.size()); + StringRef Rest = Content.take_front(Offset); + CompletionPrefix Result; + + // Consume the unqualified name. We only handle ASCII characters. + // isIdentifierBody will let us match "0invalid", but we don't mind. + while (!Rest.empty() && isIdentifierBody(Rest.back())) + Rest = Rest.drop_back(); + Result.Name = Content.slice(Rest.size(), Offset); + + // Consume qualifiers. + while (Rest.consume_back("::") && !Rest.endswith(":")) // reject :::: + while (!Rest.empty() && isIdentifierBody(Rest.back())) + Rest = Rest.drop_back(); + Result.Qualifier = + Content.slice(Rest.size(), Result.Name.begin() - Content.begin()); + + return Result; +} + +CodeCompleteResult +codeComplete(PathRef FileName, const tooling::CompileCommand &Command, + const PreambleData *Preamble, llvm::StringRef Contents, + Position Pos, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind) { + auto Offset = positionToOffset(Contents, Pos); + if (!Offset) { + elog("Code completion position was invalid {0}", Offset.takeError()); + return CodeCompleteResult(); + } + auto Flow = CodeCompleteFlow( + FileName, Preamble ? Preamble->Includes : IncludeStructure(), + SpecFuzzyFind, Opts); + return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse) + ? std::move(Flow).runWithoutSema(Contents, *Offset, VFS) + : std::move(Flow).run( + {FileName, Command, Preamble, Contents, *Offset, VFS}); +} + +SignatureHelp signatureHelp(PathRef FileName, + const tooling::CompileCommand &Command, + const PreambleData *Preamble, + llvm::StringRef Contents, Position Pos, + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + const SymbolIndex *Index) { + auto Offset = positionToOffset(Contents, Pos); + if (!Offset) { + elog("Code completion position was invalid {0}", Offset.takeError()); + return SignatureHelp(); + } + SignatureHelp Result; + clang::CodeCompleteOptions Options; + Options.IncludeGlobals = false; + Options.IncludeMacros = false; + Options.IncludeCodePatterns = false; + Options.IncludeBriefComments = false; + IncludeStructure PreambleInclusions; // Unused for signatureHelp + semaCodeComplete( + std::make_unique<SignatureHelpCollector>(Options, Index, Result), + Options, + {FileName, Command, Preamble, Contents, *Offset, std::move(VFS)}); + return Result; +} + +bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx) { + auto InTopLevelScope = [](const NamedDecl &ND) { + switch (ND.getDeclContext()->getDeclKind()) { + case Decl::TranslationUnit: + case Decl::Namespace: + case Decl::LinkageSpec: + return true; + default: + break; + }; + return false; + }; + // We only complete symbol's name, which is the same as the name of the + // *primary* template in case of template specializations. + if (isExplicitTemplateSpecialization(&ND)) + return false; + + if (InTopLevelScope(ND)) + return true; + + if (const auto *EnumDecl = dyn_cast<clang::EnumDecl>(ND.getDeclContext())) + return InTopLevelScope(*EnumDecl) && !EnumDecl->isScoped(); + + return false; +} + +CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { + CompletionItem LSP; + const auto *InsertInclude = Includes.empty() ? nullptr : &Includes[0]; + LSP.label = ((InsertInclude && InsertInclude->Insertion) + ? Opts.IncludeIndicator.Insert + : Opts.IncludeIndicator.NoInsert) + + (Opts.ShowOrigins ? "[" + llvm::to_string(Origin) + "]" : "") + + RequiredQualifier + Name + Signature; + + LSP.kind = Kind; + LSP.detail = BundleSize > 1 + ? std::string(llvm::formatv("[{0} overloads]", BundleSize)) + : ReturnType; + LSP.deprecated = Deprecated; + if (InsertInclude) + LSP.detail += "\n" + InsertInclude->Header; + LSP.documentation = Documentation; + LSP.sortText = sortText(Score.Total, Name); + LSP.filterText = Name; + LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name}; + // Merge continuous additionalTextEdits into main edit. The main motivation + // behind this is to help LSP clients, it seems most of them are confused when + // they are provided with additionalTextEdits that are consecutive to main + // edit. + // Note that we store additional text edits from back to front in a line. That + // is mainly to help LSP clients again, so that changes do not effect each + // other. + for (const auto &FixIt : FixIts) { + if (isRangeConsecutive(FixIt.range, LSP.textEdit->range)) { + LSP.textEdit->newText = FixIt.newText + LSP.textEdit->newText; + LSP.textEdit->range.start = FixIt.range.start; + } else { + LSP.additionalTextEdits.push_back(FixIt); + } + } + if (Opts.EnableSnippets) + LSP.textEdit->newText += SnippetSuffix; + + // FIXME(kadircet): Do not even fill insertText after making sure textEdit is + // compatible with most of the editors. + LSP.insertText = LSP.textEdit->newText; + LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet + : InsertTextFormat::PlainText; + if (InsertInclude && InsertInclude->Insertion) + LSP.additionalTextEdits.push_back(*InsertInclude->Insertion); + + return LSP; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const CodeCompletion &C) { + // For now just lean on CompletionItem. + return OS << C.render(CodeCompleteOptions()); +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const CodeCompleteResult &R) { + OS << "CodeCompleteResult: " << R.Completions.size() << (R.HasMore ? "+" : "") + << " (" << getCompletionKindString(R.Context) << ")" + << " items:\n"; + for (const auto &C : R.Completions) + OS << C << "\n"; + return OS; +} + +} // namespace clangd +} // namespace clang