blob: ff1ce9b63046cc824ef6d0ce816d813c339fcacf [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===---------------------------------------------------------------------===//
9
10#include "ClangdUnit.h"
11#include "clang/Frontend/ASTUnit.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "clang/Frontend/CompilerInvocation.h"
14#include "clang/Tooling/CompilationDatabase.h"
15
16using namespace clang::clangd;
17using namespace clang;
18
19ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
20 std::shared_ptr<PCHContainerOperations> PCHs,
21 std::vector<tooling::CompileCommand> Commands)
22 : FileName(FileName), PCHs(PCHs) {
23 assert(!Commands.empty() && "No compile commands provided");
24
25 // Inject the resource dir.
26 // FIXME: Don't overwrite it if it's already there.
27 static int Dummy; // Just an address in this process.
28 std::string ResourceDir =
29 CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
30 Commands.front().CommandLine.push_back("-resource-dir=" + ResourceDir);
31
32 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
33 CompilerInstance::createDiagnostics(new DiagnosticOptions);
34
35 std::vector<const char *> ArgStrs;
36 for (const auto &S : Commands.front().CommandLine)
37 ArgStrs.push_back(S.c_str());
38
39 ASTUnit::RemappedFile RemappedSource(
40 FileName,
41 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
42
43 auto ArgP = &*ArgStrs.begin();
44 Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
45 ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
46 /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
47 /*RemappedFilesKeepOriginalName=*/true,
48 /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Complete,
49 /*CacheCodeCompletionResults=*/true,
50 /*IncludeBriefCommentsInCodeCompletion=*/true,
51 /*AllowPCHWithCompilerErrors=*/true));
52}
53
54void ClangdUnit::reparse(StringRef Contents) {
55 // Do a reparse if this wasn't the first parse.
56 // FIXME: This might have the wrong working directory if it changed in the
57 // meantime.
58 ASTUnit::RemappedFile RemappedSource(
59 FileName,
60 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
61
62 Unit->Reparse(PCHs, RemappedSource);
63}
64
65namespace {
66
67CompletionItemKind getKind(CXCursorKind K) {
68 switch (K) {
69 case CXCursor_MacroInstantiation:
70 case CXCursor_MacroDefinition:
71 return CompletionItemKind::Text;
72 case CXCursor_CXXMethod:
73 return CompletionItemKind::Method;
74 case CXCursor_FunctionDecl:
75 case CXCursor_FunctionTemplate:
76 return CompletionItemKind::Function;
77 case CXCursor_Constructor:
78 case CXCursor_Destructor:
79 return CompletionItemKind::Constructor;
80 case CXCursor_FieldDecl:
81 return CompletionItemKind::Field;
82 case CXCursor_VarDecl:
83 case CXCursor_ParmDecl:
84 return CompletionItemKind::Variable;
85 case CXCursor_ClassDecl:
86 case CXCursor_StructDecl:
87 case CXCursor_UnionDecl:
88 case CXCursor_ClassTemplate:
89 case CXCursor_ClassTemplatePartialSpecialization:
90 return CompletionItemKind::Class;
91 case CXCursor_Namespace:
92 case CXCursor_NamespaceAlias:
93 case CXCursor_NamespaceRef:
94 return CompletionItemKind::Module;
95 case CXCursor_EnumConstantDecl:
96 return CompletionItemKind::Value;
97 case CXCursor_EnumDecl:
98 return CompletionItemKind::Enum;
99 case CXCursor_TypeAliasDecl:
100 case CXCursor_TypeAliasTemplateDecl:
101 case CXCursor_TypedefDecl:
102 case CXCursor_MemberRef:
103 case CXCursor_TypeRef:
104 return CompletionItemKind::Reference;
105 default:
106 return CompletionItemKind::Missing;
107 }
108}
109
110class CompletionItemsCollector : public CodeCompleteConsumer {
111 std::vector<CompletionItem> *Items;
112 std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
113 CodeCompletionTUInfo CCTUInfo;
114
115public:
116 CompletionItemsCollector(std::vector<CompletionItem> *Items,
117 const CodeCompleteOptions &CodeCompleteOpts)
118 : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
119 Items(Items),
120 Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
121 CCTUInfo(Allocator) {}
122
123 void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
124 CodeCompletionResult *Results,
125 unsigned NumResults) override {
126 for (unsigned I = 0; I != NumResults; ++I) {
127 CodeCompletionResult &Result = Results[I];
128 CodeCompletionString *CCS = Result.CreateCodeCompletionString(
129 S, Context, *Allocator, CCTUInfo,
130 CodeCompleteOpts.IncludeBriefComments);
131 if (CCS) {
132 CompletionItem Item;
133 assert(CCS->getTypedText());
134 Item.label = CCS->getTypedText();
135 Item.kind = getKind(Result.CursorKind);
136 if (CCS->getBriefComment())
137 Item.documentation = CCS->getBriefComment();
138 Items->push_back(std::move(Item));
139 }
140 }
141 }
142
143 GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
144
145 CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
146};
147} // namespace
148
149std::vector<CompletionItem> ClangdUnit::codeComplete(StringRef Contents,
150 Position Pos) {
151 CodeCompleteOptions CCO;
152 CCO.IncludeBriefComments = 1;
153 // This is where code completion stores dirty buffers. Need to free after
154 // completion.
155 SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
156 SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
157 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
158 new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
159 std::vector<CompletionItem> Items;
160 CompletionItemsCollector Collector(&Items, CCO);
161
162 ASTUnit::RemappedFile RemappedSource(
163 FileName,
164 llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
165
166 IntrusiveRefCntPtr<SourceManager> SourceMgr(
167 new SourceManager(*DiagEngine, Unit->getFileManager()));
168 // CodeComplete seems to require fresh LangOptions.
169 LangOptions LangOpts = Unit->getLangOpts();
170 // The language server protocol uses zero-based line and column numbers.
171 // The clang code completion uses one-based numbers.
172 Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
173 CCO.IncludeMacros, CCO.IncludeCodePatterns,
174 CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
175 LangOpts, *SourceMgr, Unit->getFileManager(),
176 StoredDiagnostics, OwnedBuffers);
177 for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
178 delete Buffer;
179 return Items;
180}
181
182namespace {
183/// Convert from clang diagnostic level to LSP severity.
184static int getSeverity(DiagnosticsEngine::Level L) {
185 switch (L) {
186 case DiagnosticsEngine::Remark:
187 return 4;
188 case DiagnosticsEngine::Note:
189 return 3;
190 case DiagnosticsEngine::Warning:
191 return 2;
192 case DiagnosticsEngine::Fatal:
193 case DiagnosticsEngine::Error:
194 return 1;
195 case DiagnosticsEngine::Ignored:
196 return 0;
197 }
198 llvm_unreachable("Unknown diagnostic level!");
199}
200} // namespace
201
202std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
203 std::vector<DiagWithFixIts> Result;
204 for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
205 DEnd = Unit->stored_diag_end();
206 D != DEnd; ++D) {
207 if (!D->getLocation().isValid() ||
208 !D->getLocation().getManager().isInMainFile(D->getLocation()))
209 continue;
210 Position P;
211 P.line = D->getLocation().getSpellingLineNumber() - 1;
212 P.character = D->getLocation().getSpellingColumnNumber();
213 Range R = {P, P};
214 clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
215
216 llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
217 for (const FixItHint &Fix : D->getFixIts()) {
218 FixItsForDiagnostic.push_back(clang::tooling::Replacement(
219 Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
220 }
221 Result.push_back({Diag, std::move(FixItsForDiagnostic)});
222 }
223 return Result;
224}