summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ClangdUnit.cpp
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2017-12-04 13:49:59 +0000
committerSam McCall <sam.mccall@gmail.com>2017-12-04 13:49:59 +0000
commitffdee9bdaca9868c27af76e0ec6ee7f10a37df67 (patch)
treec509f9ffa949c08f5a2613805ea73a637b5ca9ad /clang-tools-extra/clangd/ClangdUnit.cpp
parent341496466458b7f160370fcc043fa251b1c37aee (diff)
[clangd] Split CodeComplete into a separate file. NFC
Summary: Shared details of ClangdUnit and CodeComplete moved to a new Compiler file. Reviewers: ilya-biryukov Subscribers: klimek, mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D40719
Diffstat (limited to 'clang-tools-extra/clangd/ClangdUnit.cpp')
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.cpp761
1 files changed, 8 insertions, 753 deletions
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
index 02b73002087..0a8661c301a 100644
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ b/clang-tools-extra/clangd/ClangdUnit.cpp
@@ -9,6 +9,7 @@
#include "ClangdUnit.h"
+#include "Compiler.h"
#include "Logger.h"
#include "Trace.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -20,7 +21,6 @@
#include "clang/Lex/Lexer.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
-#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/Sema.h"
#include "clang/Serialization/ASTWriter.h"
#include "clang/Tooling/CompilationDatabase.h"
@@ -120,44 +120,6 @@ static int getSeverity(DiagnosticsEngine::Level L) {
llvm_unreachable("Unknown diagnostic level!");
}
-/// Get the optional chunk as a string. This function is possibly recursive.
-///
-/// The parameter info for each parameter is appended to the Parameters.
-std::string
-getOptionalParameters(const CodeCompletionString &CCS,
- std::vector<ParameterInformation> &Parameters) {
- std::string Result;
- 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.");
- Result += getOptionalParameters(*Chunk.Optional, Parameters);
- break;
- case CodeCompletionString::CK_VerticalSpace:
- break;
- case CodeCompletionString::CK_Placeholder:
- // A string that acts as a placeholder for, e.g., a function call
- // argument.
- // Intentional fallthrough here.
- case CodeCompletionString::CK_CurrentParameter: {
- // A piece of text that describes the parameter that corresponds to
- // the code-completion location within a function call, message send,
- // macro invocation, etc.
- Result += Chunk.Text;
- ParameterInformation Info;
- Info.label = Chunk.Text;
- Parameters.push_back(std::move(Info));
- break;
- }
- default:
- Result += Chunk.Text;
- break;
- }
- }
- return Result;
-}
-
llvm::Optional<DiagWithFixIts> toClangdDiag(const StoredDiagnostic &D) {
auto Location = D.getLocation();
if (!Location.isValid() || !Location.getManager().isInMainFile(Location))
@@ -193,722 +155,12 @@ private:
std::vector<DiagWithFixIts> &Output;
};
-class EmptyDiagsConsumer : public DiagnosticConsumer {
-public:
- void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
- const clang::Diagnostic &Info) override {}
-};
-
-std::unique_ptr<CompilerInvocation>
-createCompilerInvocation(ArrayRef<const char *> ArgList,
- IntrusiveRefCntPtr<DiagnosticsEngine> Diags,
- IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
- auto CI = createInvocationFromCommandLine(ArgList, std::move(Diags),
- std::move(VFS));
- // We rely on CompilerInstance to manage the resource (i.e. free them on
- // EndSourceFile), but that won't happen if DisableFree is set to true.
- // Since createInvocationFromCommandLine sets it to true, we have to override
- // it.
- CI->getFrontendOpts().DisableFree = false;
- return CI;
-}
-
-/// Creates a CompilerInstance from \p CI, with main buffer overriden to \p
-/// Buffer and arguments to read the PCH from \p Preamble, if \p Preamble is not
-/// null. Note that vfs::FileSystem inside returned instance may differ from \p
-/// VFS if additional file remapping were set in command-line arguments.
-/// On some errors, returns null. When non-null value is returned, it's expected
-/// to be consumed by the FrontendAction as it will have a pointer to the \p
-/// Buffer that will only be deleted if BeginSourceFile is called.
-std::unique_ptr<CompilerInstance>
-prepareCompilerInstance(std::unique_ptr<clang::CompilerInvocation> CI,
- const PrecompiledPreamble *Preamble,
- std::unique_ptr<llvm::MemoryBuffer> Buffer,
- std::shared_ptr<PCHContainerOperations> PCHs,
- IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- DiagnosticConsumer &DiagsClient) {
- assert(VFS && "VFS is null");
- assert(!CI->getPreprocessorOpts().RetainRemappedFileBuffers &&
- "Setting RetainRemappedFileBuffers to true will cause a memory leak "
- "of ContentsBuffer");
-
- // NOTE: we use Buffer.get() when adding remapped files, so we have to make
- // sure it will be released if no error is emitted.
- if (Preamble) {
- Preamble->AddImplicitPreamble(*CI, VFS, Buffer.get());
- } else {
- CI->getPreprocessorOpts().addRemappedFile(
- CI->getFrontendOpts().Inputs[0].getFile(), Buffer.get());
- }
-
- auto Clang = llvm::make_unique<CompilerInstance>(PCHs);
- Clang->setInvocation(std::move(CI));
- Clang->createDiagnostics(&DiagsClient, false);
-
- if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
- Clang->getInvocation(), Clang->getDiagnostics(), VFS))
- VFS = VFSWithRemapping;
- Clang->setVirtualFileSystem(VFS);
-
- Clang->setTarget(TargetInfo::CreateTargetInfo(
- Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
- if (!Clang->hasTarget())
- return nullptr;
-
- // RemappedFileBuffers will handle the lifetime of the Buffer pointer,
- // release it.
- Buffer.release();
- return Clang;
-}
-
template <class T> bool futureIsReady(std::shared_future<T> const &Future) {
return Future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}
} // namespace
-namespace {
-
-CompletionItemKind getKindOfDecl(CXCursorKind CursorKind) {
- switch (CursorKind) {
- case CXCursor_MacroInstantiation:
- case CXCursor_MacroDefinition:
- return CompletionItemKind::Text;
- case CXCursor_CXXMethod:
- return CompletionItemKind::Method;
- case CXCursor_FunctionDecl:
- case CXCursor_FunctionTemplate:
- return CompletionItemKind::Function;
- case CXCursor_Constructor:
- case CXCursor_Destructor:
- return CompletionItemKind::Constructor;
- case CXCursor_FieldDecl:
- return CompletionItemKind::Field;
- case CXCursor_VarDecl:
- case CXCursor_ParmDecl:
- return CompletionItemKind::Variable;
- case CXCursor_ClassDecl:
- case CXCursor_StructDecl:
- case CXCursor_UnionDecl:
- case CXCursor_ClassTemplate:
- case CXCursor_ClassTemplatePartialSpecialization:
- return CompletionItemKind::Class;
- case CXCursor_Namespace:
- case CXCursor_NamespaceAlias:
- case CXCursor_NamespaceRef:
- return CompletionItemKind::Module;
- case CXCursor_EnumConstantDecl:
- return CompletionItemKind::Value;
- case CXCursor_EnumDecl:
- return CompletionItemKind::Enum;
- case CXCursor_TypeAliasDecl:
- case CXCursor_TypeAliasTemplateDecl:
- case CXCursor_TypedefDecl:
- case CXCursor_MemberRef:
- case CXCursor_TypeRef:
- return CompletionItemKind::Reference;
- default:
- return CompletionItemKind::Missing;
- }
-}
-
-CompletionItemKind getKind(CodeCompletionResult::ResultKind ResKind,
- CXCursorKind CursorKind) {
- switch (ResKind) {
- case CodeCompletionResult::RK_Declaration:
- return getKindOfDecl(CursorKind);
- 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.");
-}
-
-std::string escapeSnippet(const llvm::StringRef Text) {
- std::string Result;
- Result.reserve(Text.size()); // Assume '$', '}' and '\\' are rare.
- for (const auto Character : Text) {
- if (Character == '$' || Character == '}' || Character == '\\')
- Result.push_back('\\');
- Result.push_back(Character);
- }
- return Result;
-}
-
-std::string getDocumentation(const CodeCompletionString &CCS) {
- // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
- // information in the documentation field.
- std::string Result;
- const unsigned AnnotationCount = CCS.getAnnotationCount();
- if (AnnotationCount > 0) {
- Result += "Annotation";
- if (AnnotationCount == 1) {
- Result += ": ";
- } else /* AnnotationCount > 1 */ {
- Result += "s: ";
- }
- for (unsigned I = 0; I < AnnotationCount; ++I) {
- Result += CCS.getAnnotation(I);
- Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
- }
- }
- // Add brief documentation (if there is any).
- if (CCS.getBriefComment() != nullptr) {
- if (!Result.empty()) {
- // This means we previously added annotations. Add an extra newline
- // character to make the annotations stand out.
- Result.push_back('\n');
- }
- Result += CCS.getBriefComment();
- }
- return Result;
-}
-
-/// A scored code completion result.
-/// It may be promoted to a CompletionItem if it's among the top-ranked results.
-struct CompletionCandidate {
- CompletionCandidate(CodeCompletionResult &Result)
- : Result(&Result), Score(score(Result)) {}
-
- CodeCompletionResult *Result;
- float Score; // 0 to 1, higher is better.
-
- // Comparison reflects rank: better candidates are smaller.
- bool operator<(const CompletionCandidate &C) const {
- if (Score != C.Score)
- return Score > C.Score;
- return *Result < *C.Result;
- }
-
- // Returns a string that sorts in the same order as operator<, for LSP.
- // Conceptually, this is [-Score, Name]. We convert -Score to an integer, and
- // hex-encode it for readability. Example: [0.5, "foo"] -> "41000000foo"
- std::string sortText() const {
- std::string S, NameStorage;
- llvm::raw_string_ostream OS(S);
- write_hex(OS, encodeFloat(-Score), llvm::HexPrintStyle::Lower,
- /*Width=*/2 * sizeof(Score));
- OS << Result->getOrderedName(NameStorage);
- return OS.str();
- }
-
-private:
- static float score(const CodeCompletionResult &Result) {
- // Priority 80 is a really bad score.
- float Score = 1 - std::min<float>(80, Result.Priority) / 80;
-
- switch (static_cast<CXAvailabilityKind>(Result.Availability)) {
- case CXAvailability_Available:
- // No penalty.
- break;
- case CXAvailability_Deprecated:
- Score *= 0.1f;
- break;
- case CXAvailability_NotAccessible:
- case CXAvailability_NotAvailable:
- Score = 0;
- break;
- }
- return Score;
- }
-
- // Produces an integer that sorts in the same order as F.
- // That is: a < b <==> encodeFloat(a) < encodeFloat(b).
- static uint32_t encodeFloat(float F) {
- static_assert(std::numeric_limits<float>::is_iec559, "");
- static_assert(sizeof(float) == sizeof(uint32_t), "");
- constexpr uint32_t TopBit = ~(~uint32_t{0} >> 1);
-
- // Get the bits of the float. Endianness is the same as for integers.
- uint32_t U;
- memcpy(&U, &F, sizeof(float));
- // IEEE 754 floats compare like sign-magnitude integers.
- if (U & TopBit) // Negative float.
- return 0 - U; // Map onto the low half of integers, order reversed.
- return U + TopBit; // Positive floats map onto the high half of integers.
- }
-};
-
-class CompletionItemsCollector : public CodeCompleteConsumer {
-public:
- CompletionItemsCollector(const clangd::CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
- : CodeCompleteConsumer(CodeCompleteOpts.getClangCompleteOpts(),
- /*OutputIsBinary=*/false),
- ClangdOpts(CodeCompleteOpts), Items(Items),
- Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
- CCTUInfo(Allocator) {}
-
- void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
- CodeCompletionResult *Results,
- unsigned NumResults) override final {
- StringRef Filter = S.getPreprocessor().getCodeCompletionFilter();
- std::priority_queue<CompletionCandidate> Candidates;
- for (unsigned I = 0; I < NumResults; ++I) {
- auto &Result = Results[I];
- if (!ClangdOpts.IncludeIneligibleResults &&
- (Result.Availability == CXAvailability_NotAvailable ||
- Result.Availability == CXAvailability_NotAccessible))
- continue;
- if (!Filter.empty() && !fuzzyMatch(S, Context, Filter, Result))
- continue;
- Candidates.emplace(Result);
- if (ClangdOpts.Limit && Candidates.size() > ClangdOpts.Limit) {
- Candidates.pop();
- Items.isIncomplete = true;
- }
- }
- while (!Candidates.empty()) {
- auto &Candidate = Candidates.top();
- const auto *CCS = Candidate.Result->CreateCodeCompletionString(
- S, Context, *Allocator, CCTUInfo,
- CodeCompleteOpts.IncludeBriefComments);
- assert(CCS && "Expected the CodeCompletionString to be non-null");
- Items.items.push_back(ProcessCodeCompleteResult(Candidate, *CCS));
- Candidates.pop();
- }
- std::reverse(Items.items.begin(), Items.items.end());
- }
-
- GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
-
- CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
-
-private:
- bool fuzzyMatch(Sema &S, const CodeCompletionContext &CCCtx, StringRef Filter,
- CodeCompletionResult Result) {
- switch (Result.Kind) {
- case CodeCompletionResult::RK_Declaration:
- if (auto *ID = Result.Declaration->getIdentifier())
- return fuzzyMatch(Filter, ID->getName());
- break;
- case CodeCompletionResult::RK_Keyword:
- return fuzzyMatch(Filter, Result.Keyword);
- case CodeCompletionResult::RK_Macro:
- return fuzzyMatch(Filter, Result.Macro->getName());
- case CodeCompletionResult::RK_Pattern:
- return fuzzyMatch(Filter, Result.Pattern->getTypedText());
- }
- auto *CCS = Result.CreateCodeCompletionString(
- S, CCCtx, *Allocator, CCTUInfo, /*IncludeBriefComments=*/false);
- return fuzzyMatch(Filter, CCS->getTypedText());
- }
-
- // Checks whether Target matches the Filter.
- // Currently just requires a case-insensitive subsequence match.
- // FIXME: make stricter and word-based: 'unique_ptr' should not match 'que'.
- // FIXME: return a score to be incorporated into ranking.
- static bool fuzzyMatch(StringRef Filter, StringRef Target) {
- size_t TPos = 0;
- for (char C : Filter) {
- TPos = Target.find_lower(C, TPos);
- if (TPos == StringRef::npos)
- return false;
- }
- return true;
- }
-
- CompletionItem
- ProcessCodeCompleteResult(const CompletionCandidate &Candidate,
- const CodeCompletionString &CCS) const {
-
- // Adjust this to InsertTextFormat::Snippet iff we encounter a
- // CK_Placeholder chunk in SnippetCompletionItemsCollector.
- CompletionItem Item;
- Item.insertTextFormat = InsertTextFormat::PlainText;
-
- Item.documentation = getDocumentation(CCS);
- Item.sortText = Candidate.sortText();
-
- // Fill in the label, detail, insertText and filterText fields of the
- // CompletionItem.
- ProcessChunks(CCS, Item);
-
- // Fill in the kind field of the CompletionItem.
- Item.kind = getKind(Candidate.Result->Kind, Candidate.Result->CursorKind);
-
- return Item;
- }
-
- virtual void ProcessChunks(const CodeCompletionString &CCS,
- CompletionItem &Item) const = 0;
-
- clangd::CodeCompleteOptions ClangdOpts;
- CompletionList &Items;
- std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
- CodeCompletionTUInfo CCTUInfo;
-
-}; // CompletionItemsCollector
-
-bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
- return Chunk.Kind == CodeCompletionString::CK_Informative &&
- StringRef(Chunk.Text).endswith("::");
-}
-
-class PlainTextCompletionItemsCollector final
- : public CompletionItemsCollector {
-
-public:
- PlainTextCompletionItemsCollector(
- const clangd::CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
- : CompletionItemsCollector(CodeCompleteOpts, Items) {}
-
-private:
- void ProcessChunks(const CodeCompletionString &CCS,
- CompletionItem &Item) const override {
- for (const auto &Chunk : CCS) {
- // Informative qualifier chunks only clutter completion results, skip
- // them.
- if (isInformativeQualifierChunk(Chunk))
- continue;
-
- switch (Chunk.Kind) {
- case CodeCompletionString::CK_TypedText:
- // There's always exactly one CK_TypedText chunk.
- Item.insertText = Item.filterText = Chunk.Text;
- Item.label += Chunk.Text;
- break;
- case CodeCompletionString::CK_ResultType:
- assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
- Item.detail = Chunk.Text;
- break;
- case CodeCompletionString::CK_Optional:
- break;
- default:
- Item.label += Chunk.Text;
- break;
- }
- }
- }
-}; // PlainTextCompletionItemsCollector
-
-class SnippetCompletionItemsCollector final : public CompletionItemsCollector {
-
-public:
- SnippetCompletionItemsCollector(
- const clangd::CodeCompleteOptions &CodeCompleteOpts,
- CompletionList &Items)
- : CompletionItemsCollector(CodeCompleteOpts, Items) {}
-
-private:
- void ProcessChunks(const CodeCompletionString &CCS,
- CompletionItem &Item) const override {
- unsigned ArgCount = 0;
- for (const auto &Chunk : CCS) {
- // Informative qualifier chunks only clutter completion results, skip
- // them.
- if (isInformativeQualifierChunk(Chunk))
- continue;
-
- switch (Chunk.Kind) {
- case CodeCompletionString::CK_TypedText:
- // The piece of text that the user is expected to type to match
- // the code-completion string, typically a keyword or the name of
- // a declarator or macro.
- Item.filterText = Chunk.Text;
- LLVM_FALLTHROUGH;
- case CodeCompletionString::CK_Text:
- // A piece of text that should be placed in the buffer,
- // e.g., parentheses or a comma in a function call.
- Item.label += Chunk.Text;
- Item.insertText += Chunk.Text;
- break;
- case CodeCompletionString::CK_Optional:
- // A code completion string that is entirely optional.
- // For example, an optional code completion string that
- // describes the default arguments in a function call.
-
- // FIXME: Maybe add an option to allow presenting the optional chunks?
- break;
- case CodeCompletionString::CK_Placeholder:
- // A string that acts as a placeholder for, e.g., a function call
- // argument.
- ++ArgCount;
- Item.insertText += "${" + std::to_string(ArgCount) + ':' +
- escapeSnippet(Chunk.Text) + '}';
- Item.label += Chunk.Text;
- Item.insertTextFormat = InsertTextFormat::Snippet;
- break;
- case CodeCompletionString::CK_Informative:
- // A piece of text that describes something about the result
- // but should not be inserted into the buffer.
- // For example, the word "const" for a const method, or the name of
- // the base class for methods that are part of the base class.
- Item.label += Chunk.Text;
- // Don't put the informative chunks in the insertText.
- break;
- case CodeCompletionString::CK_ResultType:
- // A piece of text that describes the type of an entity or,
- // for functions and methods, the return type.
- assert(Item.detail.empty() && "Unexpected extraneous CK_ResultType");
- Item.detail = Chunk.Text;
- break;
- case CodeCompletionString::CK_CurrentParameter:
- // A piece of text that describes the parameter that corresponds to
- // the code-completion location within a function call, message send,
- // macro invocation, etc.
- //
- // This should never be present while collecting completion items,
- // only while collecting overload candidates.
- llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
- "CompletionItems");
- break;
- case CodeCompletionString::CK_LeftParen:
- // A left parenthesis ('(').
- case CodeCompletionString::CK_RightParen:
- // A right parenthesis (')').
- case CodeCompletionString::CK_LeftBracket:
- // A left bracket ('[').
- case CodeCompletionString::CK_RightBracket:
- // A right bracket (']').
- case CodeCompletionString::CK_LeftBrace:
- // A left brace ('{').
- case CodeCompletionString::CK_RightBrace:
- // A right brace ('}').
- case CodeCompletionString::CK_LeftAngle:
- // A left angle bracket ('<').
- case CodeCompletionString::CK_RightAngle:
- // A right angle bracket ('>').
- case CodeCompletionString::CK_Comma:
- // A comma separator (',').
- case CodeCompletionString::CK_Colon:
- // A colon (':').
- case CodeCompletionString::CK_SemiColon:
- // A semicolon (';').
- case CodeCompletionString::CK_Equal:
- // An '=' sign.
- case CodeCompletionString::CK_HorizontalSpace:
- // Horizontal whitespace (' ').
- Item.insertText += Chunk.Text;
- Item.label += Chunk.Text;
- break;
- case CodeCompletionString::CK_VerticalSpace:
- // Vertical whitespace ('\n' or '\r\n', depending on the
- // platform).
- Item.insertText += Chunk.Text;
- // Don't even add a space to the label.
- break;
- }
- }
- }
-}; // SnippetCompletionItemsCollector
-
-class SignatureHelpCollector final : public CodeCompleteConsumer {
-
-public:
- SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts,
- SignatureHelp &SigHelp)
- : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
- SigHelp(SigHelp),
- Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
- CCTUInfo(Allocator) {}
-
- void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
- OverloadCandidate *Candidates,
- unsigned NumCandidates) override {
- SigHelp.signatures.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) {
- const auto &Candidate = Candidates[I];
- const auto *CCS = Candidate.CreateSignatureString(
- CurrentArg, S, *Allocator, CCTUInfo, true);
- assert(CCS && "Expected the CodeCompletionString to be non-null");
- SigHelp.signatures.push_back(ProcessOverloadCandidate(Candidate, *CCS));
- }
- }
-
- GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
-
- CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
-
-private:
- SignatureInformation
- ProcessOverloadCandidate(const OverloadCandidate &Candidate,
- const CodeCompletionString &CCS) const {
- SignatureInformation Result;
- const char *ReturnType = nullptr;
-
- Result.documentation = getDocumentation(CCS);
-
- 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_Placeholder:
- // A string that acts as a placeholder for, e.g., a function call
- // argument.
- // Intentional fallthrough here.
- case CodeCompletionString::CK_CurrentParameter: {
- // A piece of text that describes the parameter that corresponds to
- // the code-completion location within a function call, message send,
- // macro invocation, etc.
- Result.label += Chunk.Text;
- ParameterInformation Info;
- Info.label = Chunk.Text;
- Result.parameters.push_back(std::move(Info));
- 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.");
- Result.label +=
- getOptionalParameters(*Chunk.Optional, Result.parameters);
- break;
- }
- case CodeCompletionString::CK_VerticalSpace:
- break;
- default:
- Result.label += Chunk.Text;
- break;
- }
- }
- if (ReturnType) {
- Result.label += " -> ";
- Result.label += ReturnType;
- }
- return Result;
- }
-
- SignatureHelp &SigHelp;
- std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
- CodeCompletionTUInfo CCTUInfo;
-
-}; // SignatureHelpCollector
-
-bool invokeCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
- const clang::CodeCompleteOptions &Options,
- PathRef FileName,
- const tooling::CompileCommand &Command,
- PrecompiledPreamble const *Preamble, StringRef Contents,
- Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::Logger &Logger) {
- std::vector<const char *> ArgStrs;
- for (const auto &S : Command.CommandLine)
- ArgStrs.push_back(S.c_str());
-
- VFS->setCurrentWorkingDirectory(Command.Directory);
-
- std::unique_ptr<CompilerInvocation> CI;
- EmptyDiagsConsumer DummyDiagsConsumer;
- {
- IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
- CompilerInstance::createDiagnostics(new DiagnosticOptions,
- &DummyDiagsConsumer, false);
- CI = createCompilerInvocation(ArgStrs, CommandLineDiagsEngine, VFS);
- }
- assert(CI && "Couldn't create CompilerInvocation");
-
- std::unique_ptr<llvm::MemoryBuffer> ContentsBuffer =
- llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName);
-
- // Attempt to reuse the PCH from precompiled preamble, if it was built.
- if (Preamble) {
- auto Bounds =
- ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
- if (!Preamble->CanReuse(*CI, ContentsBuffer.get(), Bounds, VFS.get()))
- Preamble = nullptr;
- }
-
- auto Clang = prepareCompilerInstance(
- std::move(CI), Preamble, std::move(ContentsBuffer), std::move(PCHs),
- std::move(VFS), DummyDiagsConsumer);
- auto &DiagOpts = Clang->getDiagnosticOpts();
- DiagOpts.IgnoreWarnings = true;
-
- auto &FrontendOpts = Clang->getFrontendOpts();
- FrontendOpts.SkipFunctionBodies = true;
- FrontendOpts.CodeCompleteOpts = Options;
- FrontendOpts.CodeCompletionAt.FileName = FileName;
- FrontendOpts.CodeCompletionAt.Line = Pos.line + 1;
- FrontendOpts.CodeCompletionAt.Column = Pos.character + 1;
-
- Clang->setCodeCompletionConsumer(Consumer.release());
-
- SyntaxOnlyAction Action;
- if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
- Logger.log("BeginSourceFile() failed when running codeComplete for " +
- FileName);
- return false;
- }
- if (!Action.Execute()) {
- Logger.log("Execute() failed when running codeComplete for " + FileName);
- return false;
- }
-
- Action.EndSourceFile();
-
- return true;
-}
-
-} // namespace
-
-clang::CodeCompleteOptions
-clangd::CodeCompleteOptions::getClangCompleteOpts() const {
- clang::CodeCompleteOptions Result;
- Result.IncludeCodePatterns = EnableSnippets && IncludeCodePatterns;
- Result.IncludeMacros = IncludeMacros;
- Result.IncludeGlobals = IncludeGlobals;
- Result.IncludeBriefComments = IncludeBriefComments;
-
- return Result;
-}
-
-CompletionList
-clangd::codeComplete(PathRef FileName, const tooling::CompileCommand &Command,
- PrecompiledPreamble const *Preamble, StringRef Contents,
- Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::CodeCompleteOptions Opts, clangd::Logger &Logger) {
- CompletionList Results;
- std::unique_ptr<CodeCompleteConsumer> Consumer;
- if (Opts.EnableSnippets) {
- Consumer =
- llvm::make_unique<SnippetCompletionItemsCollector>(Opts, Results);
- } else {
- Consumer =
- llvm::make_unique<PlainTextCompletionItemsCollector>(Opts, Results);
- }
- invokeCodeComplete(std::move(Consumer), Opts.getClangCompleteOpts(), FileName,
- Command, Preamble, Contents, Pos, std::move(VFS),
- std::move(PCHs), Logger);
- return Results;
-}
-
-SignatureHelp
-clangd::signatureHelp(PathRef FileName, const tooling::CompileCommand &Command,
- PrecompiledPreamble const *Preamble, StringRef Contents,
- Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS,
- std::shared_ptr<PCHContainerOperations> PCHs,
- clangd::Logger &Logger) {
- SignatureHelp Result;
- clang::CodeCompleteOptions Options;
- Options.IncludeGlobals = false;
- Options.IncludeMacros = false;
- Options.IncludeCodePatterns = false;
- Options.IncludeBriefComments = true;
- invokeCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, Result),
- Options, FileName, Command, Preamble, Contents, Pos,
- std::move(VFS), std::move(PCHs), Logger);
- return Result;
-}
-
void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {
AST.getASTContext().getTranslationUnitDecl()->dump(OS, true);
}
@@ -946,7 +198,7 @@ ParsedAST::Build(std::unique_ptr<clang::CompilerInvocation> CI,
// UnitDiagsConsumer is local, we can not store it in CompilerInstance that
// has a longer lifetime.
- Clang->getDiagnostics().setClient(new EmptyDiagsConsumer);
+ Clang->getDiagnostics().setClient(new IgnoreDiagnostics);
std::vector<const Decl *> ParsedDecls = Action->takeTopLevelDecls();
return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
@@ -1302,11 +554,14 @@ CppFile::deferRebuild(StringRef NewContents,
{
// FIXME(ibiryukov): store diagnostics from CommandLine when we start
// reporting them.
- EmptyDiagsConsumer CommandLineDiagsConsumer;
+ IgnoreDiagnostics IgnoreDiagnostics;
IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
CompilerInstance::createDiagnostics(new DiagnosticOptions,
- &CommandLineDiagsConsumer, false);
- CI = createCompilerInvocation(ArgStrs, CommandLineDiagsEngine, VFS);
+ &IgnoreDiagnostics, false);
+ CI =
+ createInvocationFromCommandLine(ArgStrs, CommandLineDiagsEngine, VFS);
+ // createInvocationFromCommandLine sets DisableFree.
+ CI->getFrontendOpts().DisableFree = false;
}
assert(CI && "Couldn't create CompilerInvocation");