File: | tools/clang/tools/extra/clangd/ClangdServer.cpp |
Warning: | line 365, column 38 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- ClangdServer.cpp - Main clangd server code --------------*- 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 "ClangdServer.h" | |||
11 | #include "CodeComplete.h" | |||
12 | #include "SourceCode.h" | |||
13 | #include "XRefs.h" | |||
14 | #include "index/Merge.h" | |||
15 | #include "clang/Format/Format.h" | |||
16 | #include "clang/Frontend/CompilerInstance.h" | |||
17 | #include "clang/Frontend/CompilerInvocation.h" | |||
18 | #include "clang/Tooling/CompilationDatabase.h" | |||
19 | #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" | |||
20 | #include "clang/Tooling/Refactoring/Rename/RenamingAction.h" | |||
21 | #include "llvm/ADT/ArrayRef.h" | |||
22 | #include "llvm/ADT/ScopeExit.h" | |||
23 | #include "llvm/Support/Errc.h" | |||
24 | #include "llvm/Support/FileSystem.h" | |||
25 | #include "llvm/Support/FormatProviders.h" | |||
26 | #include "llvm/Support/FormatVariadic.h" | |||
27 | #include "llvm/Support/Path.h" | |||
28 | #include "llvm/Support/raw_ostream.h" | |||
29 | #include <future> | |||
30 | ||||
31 | using namespace clang; | |||
32 | using namespace clang::clangd; | |||
33 | ||||
34 | namespace { | |||
35 | ||||
36 | std::string getStandardResourceDir() { | |||
37 | static int Dummy; // Just an address in this process. | |||
38 | return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy); | |||
39 | } | |||
40 | ||||
41 | class RefactoringResultCollector final | |||
42 | : public tooling::RefactoringResultConsumer { | |||
43 | public: | |||
44 | void handleError(llvm::Error Err) override { | |||
45 | assert(!Result.hasValue())(static_cast <bool> (!Result.hasValue()) ? void (0) : __assert_fail ("!Result.hasValue()", "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 45, __extension__ __PRETTY_FUNCTION__)); | |||
46 | // FIXME: figure out a way to return better message for DiagnosticError. | |||
47 | // clangd uses llvm::toString to convert the Err to string, however, for | |||
48 | // DiagnosticError, only "clang diagnostic" will be generated. | |||
49 | Result = std::move(Err); | |||
50 | } | |||
51 | ||||
52 | // Using the handle(SymbolOccurrences) from parent class. | |||
53 | using tooling::RefactoringResultConsumer::handle; | |||
54 | ||||
55 | void handle(tooling::AtomicChanges SourceReplacements) override { | |||
56 | assert(!Result.hasValue())(static_cast <bool> (!Result.hasValue()) ? void (0) : __assert_fail ("!Result.hasValue()", "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 56, __extension__ __PRETTY_FUNCTION__)); | |||
57 | Result = std::move(SourceReplacements); | |||
58 | } | |||
59 | ||||
60 | Optional<Expected<tooling::AtomicChanges>> Result; | |||
61 | }; | |||
62 | ||||
63 | } // namespace | |||
64 | ||||
65 | Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> | |||
66 | RealFileSystemProvider::getTaggedFileSystem(PathRef File) { | |||
67 | return make_tagged(vfs::getRealFileSystem(), VFSTag()); | |||
68 | } | |||
69 | ||||
70 | unsigned clangd::getDefaultAsyncThreadsCount() { | |||
71 | unsigned HardwareConcurrency = std::thread::hardware_concurrency(); | |||
72 | // C++ standard says that hardware_concurrency() | |||
73 | // may return 0, fallback to 1 worker thread in | |||
74 | // that case. | |||
75 | if (HardwareConcurrency == 0) | |||
76 | return 1; | |||
77 | return HardwareConcurrency; | |||
78 | } | |||
79 | ||||
80 | ClangdScheduler::ClangdScheduler(unsigned AsyncThreadsCount) | |||
81 | : RunSynchronously(AsyncThreadsCount == 0) { | |||
82 | if (RunSynchronously) { | |||
83 | // Don't start the worker thread if we're running synchronously | |||
84 | return; | |||
85 | } | |||
86 | ||||
87 | Workers.reserve(AsyncThreadsCount); | |||
88 | for (unsigned I = 0; I < AsyncThreadsCount; ++I) { | |||
89 | Workers.push_back(std::thread([this, I]() { | |||
90 | llvm::set_thread_name(llvm::formatv("scheduler/{0}", I)); | |||
91 | while (true) { | |||
92 | UniqueFunction<void()> Request; | |||
93 | ||||
94 | // Pick request from the queue | |||
95 | { | |||
96 | std::unique_lock<std::mutex> Lock(Mutex); | |||
97 | // Wait for more requests. | |||
98 | RequestCV.wait(Lock, | |||
99 | [this] { return !RequestQueue.empty() || Done; }); | |||
100 | if (Done) | |||
101 | return; | |||
102 | ||||
103 | assert(!RequestQueue.empty() && "RequestQueue was empty")(static_cast <bool> (!RequestQueue.empty() && "RequestQueue was empty" ) ? void (0) : __assert_fail ("!RequestQueue.empty() && \"RequestQueue was empty\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 103, __extension__ __PRETTY_FUNCTION__)); | |||
104 | ||||
105 | // We process requests starting from the front of the queue. Users of | |||
106 | // ClangdScheduler have a way to prioritise their requests by putting | |||
107 | // them to the either side of the queue (using either addToEnd or | |||
108 | // addToFront). | |||
109 | Request = std::move(RequestQueue.front()); | |||
110 | RequestQueue.pop_front(); | |||
111 | } // unlock Mutex | |||
112 | ||||
113 | Request(); | |||
114 | } | |||
115 | })); | |||
116 | } | |||
117 | } | |||
118 | ||||
119 | ClangdScheduler::~ClangdScheduler() { | |||
120 | if (RunSynchronously) | |||
121 | return; // no worker thread is running in that case | |||
122 | ||||
123 | { | |||
124 | std::lock_guard<std::mutex> Lock(Mutex); | |||
125 | // Wake up the worker thread | |||
126 | Done = true; | |||
127 | } // unlock Mutex | |||
128 | RequestCV.notify_all(); | |||
129 | ||||
130 | for (auto &Worker : Workers) | |||
131 | Worker.join(); | |||
132 | } | |||
133 | ||||
134 | ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, | |||
135 | DiagnosticsConsumer &DiagConsumer, | |||
136 | FileSystemProvider &FSProvider, | |||
137 | unsigned AsyncThreadsCount, | |||
138 | bool StorePreamblesInMemory, | |||
139 | bool BuildDynamicSymbolIndex, SymbolIndex *StaticIdx, | |||
140 | llvm::Optional<StringRef> ResourceDir) | |||
141 | : CompileArgs(CDB, | |||
142 | ResourceDir ? ResourceDir->str() : getStandardResourceDir()), | |||
143 | DiagConsumer(DiagConsumer), FSProvider(FSProvider), | |||
144 | FileIdx(BuildDynamicSymbolIndex ? new FileIndex() : nullptr), | |||
145 | // Pass a callback into `Units` to extract symbols from a newly parsed | |||
146 | // file and rebuild the file index synchronously each time an AST is | |||
147 | // parsed. | |||
148 | // FIXME(ioeric): this can be slow and we may be able to index on less | |||
149 | // critical paths. | |||
150 | Units(FileIdx | |||
151 | ? [this](const Context &Ctx, PathRef Path, | |||
152 | ParsedAST *AST) { FileIdx->update(Ctx, Path, AST); } | |||
153 | : ASTParsedCallback()), | |||
154 | PCHs(std::make_shared<PCHContainerOperations>()), | |||
155 | StorePreamblesInMemory(StorePreamblesInMemory), | |||
156 | WorkScheduler(AsyncThreadsCount) { | |||
157 | if (FileIdx && StaticIdx) { | |||
158 | MergedIndex = mergeIndex(FileIdx.get(), StaticIdx); | |||
159 | Index = MergedIndex.get(); | |||
160 | } else if (FileIdx) | |||
161 | Index = FileIdx.get(); | |||
162 | else if (StaticIdx) | |||
163 | Index = StaticIdx; | |||
164 | else | |||
165 | Index = nullptr; | |||
166 | } | |||
167 | ||||
168 | void ClangdServer::setRootPath(PathRef RootPath) { | |||
169 | std::string NewRootPath = llvm::sys::path::convert_to_slash( | |||
170 | RootPath, llvm::sys::path::Style::posix); | |||
171 | if (llvm::sys::fs::is_directory(NewRootPath)) | |||
172 | this->RootPath = NewRootPath; | |||
173 | } | |||
174 | ||||
175 | std::future<Context> ClangdServer::addDocument(Context Ctx, PathRef File, | |||
176 | StringRef Contents) { | |||
177 | DocVersion Version = DraftMgr.updateDraft(File, Contents); | |||
178 | ||||
179 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
180 | std::shared_ptr<CppFile> Resources = | |||
181 | Units.getOrCreateFile(File, StorePreamblesInMemory, PCHs); | |||
182 | return scheduleReparseAndDiags(std::move(Ctx), File, | |||
183 | VersionedDraft{Version, Contents.str()}, | |||
184 | std::move(Resources), std::move(TaggedFS)); | |||
185 | } | |||
186 | ||||
187 | std::future<Context> ClangdServer::removeDocument(Context Ctx, PathRef File) { | |||
188 | DraftMgr.removeDraft(File); | |||
189 | CompileArgs.invalidate(File); | |||
190 | ||||
191 | std::shared_ptr<CppFile> Resources = Units.removeIfPresent(File); | |||
192 | return scheduleCancelRebuild(std::move(Ctx), std::move(Resources)); | |||
193 | } | |||
194 | ||||
195 | std::future<Context> ClangdServer::forceReparse(Context Ctx, PathRef File) { | |||
196 | auto FileContents = DraftMgr.getDraft(File); | |||
197 | assert(FileContents.Draft &&(static_cast <bool> (FileContents.Draft && "forceReparse() was called for non-added document" ) ? void (0) : __assert_fail ("FileContents.Draft && \"forceReparse() was called for non-added document\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 198, __extension__ __PRETTY_FUNCTION__)) | |||
198 | "forceReparse() was called for non-added document")(static_cast <bool> (FileContents.Draft && "forceReparse() was called for non-added document" ) ? void (0) : __assert_fail ("FileContents.Draft && \"forceReparse() was called for non-added document\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 198, __extension__ __PRETTY_FUNCTION__)); | |||
199 | ||||
200 | // forceReparse promises to request new compilation flags from CDB, so we | |||
201 | // remove any cahced flags. | |||
202 | CompileArgs.invalidate(File); | |||
203 | ||||
204 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
205 | std::shared_ptr<CppFile> Resources = | |||
206 | Units.getOrCreateFile(File, StorePreamblesInMemory, PCHs); | |||
207 | return scheduleReparseAndDiags(std::move(Ctx), File, FileContents, | |||
208 | std::move(Resources), std::move(TaggedFS)); | |||
209 | } | |||
210 | ||||
211 | std::future<std::pair<Context, Tagged<CompletionList>>> | |||
212 | ClangdServer::codeComplete(Context Ctx, PathRef File, Position Pos, | |||
213 | const clangd::CodeCompleteOptions &Opts, | |||
214 | llvm::Optional<StringRef> OverridenContents, | |||
215 | IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) { | |||
216 | using ResultType = std::pair<Context, Tagged<CompletionList>>; | |||
217 | ||||
218 | std::promise<ResultType> ResultPromise; | |||
219 | ||||
220 | auto Callback = [](std::promise<ResultType> ResultPromise, Context Ctx, | |||
221 | Tagged<CompletionList> Result) -> void { | |||
222 | ResultPromise.set_value({std::move(Ctx), std::move(Result)}); | |||
223 | }; | |||
224 | ||||
225 | std::future<ResultType> ResultFuture = ResultPromise.get_future(); | |||
226 | codeComplete(std::move(Ctx), File, Pos, Opts, | |||
227 | BindWithForward(Callback, std::move(ResultPromise)), | |||
228 | OverridenContents, UsedFS); | |||
229 | return ResultFuture; | |||
230 | } | |||
231 | ||||
232 | void ClangdServer::codeComplete( | |||
233 | Context Ctx, PathRef File, Position Pos, | |||
234 | const clangd::CodeCompleteOptions &Opts, | |||
235 | UniqueFunction<void(Context, Tagged<CompletionList>)> Callback, | |||
236 | llvm::Optional<StringRef> OverridenContents, | |||
237 | IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) { | |||
238 | using CallbackType = UniqueFunction<void(Context, Tagged<CompletionList>)>; | |||
239 | ||||
240 | std::string Contents; | |||
241 | if (OverridenContents) { | |||
242 | Contents = *OverridenContents; | |||
243 | } else { | |||
244 | auto FileContents = DraftMgr.getDraft(File); | |||
245 | assert(FileContents.Draft &&(static_cast <bool> (FileContents.Draft && "codeComplete is called for non-added document" ) ? void (0) : __assert_fail ("FileContents.Draft && \"codeComplete is called for non-added document\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 246, __extension__ __PRETTY_FUNCTION__)) | |||
246 | "codeComplete is called for non-added document")(static_cast <bool> (FileContents.Draft && "codeComplete is called for non-added document" ) ? void (0) : __assert_fail ("FileContents.Draft && \"codeComplete is called for non-added document\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 246, __extension__ __PRETTY_FUNCTION__)); | |||
247 | ||||
248 | Contents = std::move(*FileContents.Draft); | |||
249 | } | |||
250 | ||||
251 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
252 | if (UsedFS) | |||
253 | *UsedFS = TaggedFS.Value; | |||
254 | ||||
255 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
256 | assert(Resources && "Calling completion on non-added file")(static_cast <bool> (Resources && "Calling completion on non-added file" ) ? void (0) : __assert_fail ("Resources && \"Calling completion on non-added file\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 256, __extension__ __PRETTY_FUNCTION__)); | |||
257 | ||||
258 | // Remember the current Preamble and use it when async task starts executing. | |||
259 | // At the point when async task starts executing, we may have a different | |||
260 | // Preamble in Resources. However, we assume the Preamble that we obtain here | |||
261 | // is reusable in completion more often. | |||
262 | std::shared_ptr<const PreambleData> Preamble = | |||
263 | Resources->getPossiblyStalePreamble(); | |||
264 | // Copy completion options for passing them to async task handler. | |||
265 | auto CodeCompleteOpts = Opts; | |||
266 | if (!CodeCompleteOpts.Index) // Respect overridden index. | |||
267 | CodeCompleteOpts.Index = Index; | |||
268 | ||||
269 | // Copy File, as it is a PathRef that will go out of scope before Task is | |||
270 | // executed. | |||
271 | Path FileStr = File; | |||
272 | // Copy PCHs to avoid accessing this->PCHs concurrently | |||
273 | std::shared_ptr<PCHContainerOperations> PCHs = this->PCHs; | |||
274 | tooling::CompileCommand CompileCommand = CompileArgs.getCompileCommand(File); | |||
275 | // A task that will be run asynchronously. | |||
276 | auto Task = | |||
277 | // 'mutable' to reassign Preamble variable. | |||
278 | [FileStr, Preamble, Resources, Contents, Pos, CodeCompleteOpts, TaggedFS, | |||
279 | PCHs, CompileCommand](Context Ctx, CallbackType Callback) mutable { | |||
280 | if (!Preamble) { | |||
281 | // Maybe we built some preamble before processing this request. | |||
282 | Preamble = Resources->getPossiblyStalePreamble(); | |||
283 | } | |||
284 | // FIXME(ibiryukov): even if Preamble is non-null, we may want to check | |||
285 | // both the old and the new version in case only one of them matches. | |||
286 | CompletionList Result = clangd::codeComplete( | |||
287 | Ctx, FileStr, CompileCommand, | |||
288 | Preamble ? &Preamble->Preamble : nullptr, Contents, Pos, | |||
289 | TaggedFS.Value, PCHs, CodeCompleteOpts); | |||
290 | ||||
291 | Callback(std::move(Ctx), | |||
292 | make_tagged(std::move(Result), std::move(TaggedFS.Tag))); | |||
293 | }; | |||
294 | ||||
295 | WorkScheduler.addToFront(std::move(Task), std::move(Ctx), | |||
296 | std::move(Callback)); | |||
297 | } | |||
298 | ||||
299 | llvm::Expected<Tagged<SignatureHelp>> | |||
300 | ClangdServer::signatureHelp(const Context &Ctx, PathRef File, Position Pos, | |||
301 | llvm::Optional<StringRef> OverridenContents, | |||
302 | IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS) { | |||
303 | std::string DraftStorage; | |||
304 | if (!OverridenContents) { | |||
305 | auto FileContents = DraftMgr.getDraft(File); | |||
306 | if (!FileContents.Draft) | |||
307 | return llvm::make_error<llvm::StringError>( | |||
308 | "signatureHelp is called for non-added document", | |||
309 | llvm::errc::invalid_argument); | |||
310 | ||||
311 | DraftStorage = std::move(*FileContents.Draft); | |||
312 | OverridenContents = DraftStorage; | |||
313 | } | |||
314 | ||||
315 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
316 | if (UsedFS) | |||
317 | *UsedFS = TaggedFS.Value; | |||
318 | ||||
319 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
320 | if (!Resources) | |||
321 | return llvm::make_error<llvm::StringError>( | |||
322 | "signatureHelp is called for non-added document", | |||
323 | llvm::errc::invalid_argument); | |||
324 | ||||
325 | auto Preamble = Resources->getPossiblyStalePreamble(); | |||
326 | auto Result = | |||
327 | clangd::signatureHelp(Ctx, File, CompileArgs.getCompileCommand(File), | |||
328 | Preamble ? &Preamble->Preamble : nullptr, | |||
329 | *OverridenContents, Pos, TaggedFS.Value, PCHs); | |||
330 | return make_tagged(std::move(Result), TaggedFS.Tag); | |||
331 | } | |||
332 | ||||
333 | llvm::Expected<tooling::Replacements> | |||
334 | ClangdServer::formatRange(StringRef Code, PathRef File, Range Rng) { | |||
335 | size_t Begin = positionToOffset(Code, Rng.start); | |||
336 | size_t Len = positionToOffset(Code, Rng.end) - Begin; | |||
337 | return formatCode(Code, File, {tooling::Range(Begin, Len)}); | |||
338 | } | |||
339 | ||||
340 | llvm::Expected<tooling::Replacements> ClangdServer::formatFile(StringRef Code, | |||
341 | PathRef File) { | |||
342 | // Format everything. | |||
343 | return formatCode(Code, File, {tooling::Range(0, Code.size())}); | |||
344 | } | |||
345 | ||||
346 | llvm::Expected<tooling::Replacements> | |||
347 | ClangdServer::formatOnType(StringRef Code, PathRef File, Position Pos) { | |||
348 | // Look for the previous opening brace from the character position and | |||
349 | // format starting from there. | |||
350 | size_t CursorPos = positionToOffset(Code, Pos); | |||
351 | size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos); | |||
352 | if (PreviousLBracePos == StringRef::npos) | |||
353 | PreviousLBracePos = CursorPos; | |||
354 | size_t Len = CursorPos - PreviousLBracePos; | |||
355 | ||||
356 | return formatCode(Code, File, {tooling::Range(PreviousLBracePos, Len)}); | |||
357 | } | |||
358 | ||||
359 | Expected<std::vector<tooling::Replacement>> | |||
360 | ClangdServer::rename(const Context &Ctx, PathRef File, Position Pos, | |||
361 | llvm::StringRef NewName) { | |||
362 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
363 | RefactoringResultCollector ResultCollector; | |||
364 | Resources->getAST().get()->runUnderLock([&](ParsedAST *AST) { | |||
| ||||
365 | const SourceManager &SourceMgr = AST->getASTContext().getSourceManager(); | |||
| ||||
366 | const FileEntry *FE = | |||
367 | SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); | |||
368 | if (!FE) | |||
369 | return; | |||
370 | SourceLocation SourceLocationBeg = | |||
371 | clangd::getBeginningOfIdentifier(*AST, Pos, FE); | |||
372 | tooling::RefactoringRuleContext Context( | |||
373 | AST->getASTContext().getSourceManager()); | |||
374 | Context.setASTContext(AST->getASTContext()); | |||
375 | auto Rename = clang::tooling::RenameOccurrences::initiate( | |||
376 | Context, SourceRange(SourceLocationBeg), NewName.str()); | |||
377 | if (!Rename) { | |||
378 | ResultCollector.Result = Rename.takeError(); | |||
379 | return; | |||
380 | } | |||
381 | Rename->invoke(ResultCollector, Context); | |||
382 | }); | |||
383 | assert(ResultCollector.Result.hasValue())(static_cast <bool> (ResultCollector.Result.hasValue()) ? void (0) : __assert_fail ("ResultCollector.Result.hasValue()" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 383, __extension__ __PRETTY_FUNCTION__)); | |||
384 | if (!ResultCollector.Result.getValue()) | |||
385 | return ResultCollector.Result->takeError(); | |||
386 | ||||
387 | std::vector<tooling::Replacement> Replacements; | |||
388 | for (const tooling::AtomicChange &Change : ResultCollector.Result->get()) { | |||
389 | tooling::Replacements ChangeReps = Change.getReplacements(); | |||
390 | for (const auto &Rep : ChangeReps) { | |||
391 | // FIXME: Right now we only support renaming the main file, so we drop | |||
392 | // replacements not for the main file. In the future, we might consider to | |||
393 | // support: | |||
394 | // * rename in any included header | |||
395 | // * rename only in the "main" header | |||
396 | // * provide an error if there are symbols we won't rename (e.g. | |||
397 | // std::vector) | |||
398 | // * rename globally in project | |||
399 | // * rename in open files | |||
400 | if (Rep.getFilePath() == File) | |||
401 | Replacements.push_back(Rep); | |||
402 | } | |||
403 | } | |||
404 | return Replacements; | |||
405 | } | |||
406 | ||||
407 | llvm::Optional<std::string> ClangdServer::getDocument(PathRef File) { | |||
408 | auto Latest = DraftMgr.getDraft(File); | |||
409 | if (!Latest.Draft) | |||
410 | return llvm::None; | |||
411 | return std::move(*Latest.Draft); | |||
412 | } | |||
413 | ||||
414 | std::string ClangdServer::dumpAST(PathRef File) { | |||
415 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
416 | if (!Resources) | |||
417 | return "<non-added file>"; | |||
418 | ||||
419 | std::string Result; | |||
420 | Resources->getAST().get()->runUnderLock([&Result](ParsedAST *AST) { | |||
421 | llvm::raw_string_ostream ResultOS(Result); | |||
422 | if (AST) { | |||
423 | clangd::dumpAST(*AST, ResultOS); | |||
424 | } else { | |||
425 | ResultOS << "<no-ast>"; | |||
426 | } | |||
427 | ResultOS.flush(); | |||
428 | }); | |||
429 | return Result; | |||
430 | } | |||
431 | ||||
432 | llvm::Expected<Tagged<std::vector<Location>>> | |||
433 | ClangdServer::findDefinitions(const Context &Ctx, PathRef File, Position Pos) { | |||
434 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
435 | ||||
436 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
437 | if (!Resources) | |||
438 | return llvm::make_error<llvm::StringError>( | |||
439 | "findDefinitions called on non-added file", | |||
440 | llvm::errc::invalid_argument); | |||
441 | ||||
442 | std::vector<Location> Result; | |||
443 | Resources->getAST().get()->runUnderLock([Pos, &Result, &Ctx](ParsedAST *AST) { | |||
444 | if (!AST) | |||
445 | return; | |||
446 | Result = clangd::findDefinitions(Ctx, *AST, Pos); | |||
447 | }); | |||
448 | return make_tagged(std::move(Result), TaggedFS.Tag); | |||
449 | } | |||
450 | ||||
451 | llvm::Optional<Path> ClangdServer::switchSourceHeader(PathRef Path) { | |||
452 | ||||
453 | StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx", | |||
454 | ".c++", ".m", ".mm"}; | |||
455 | StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx", ".inc"}; | |||
456 | ||||
457 | StringRef PathExt = llvm::sys::path::extension(Path); | |||
458 | ||||
459 | // Lookup in a list of known extensions. | |||
460 | auto SourceIter = | |||
461 | std::find_if(std::begin(SourceExtensions), std::end(SourceExtensions), | |||
462 | [&PathExt](PathRef SourceExt) { | |||
463 | return SourceExt.equals_lower(PathExt); | |||
464 | }); | |||
465 | bool IsSource = SourceIter != std::end(SourceExtensions); | |||
466 | ||||
467 | auto HeaderIter = | |||
468 | std::find_if(std::begin(HeaderExtensions), std::end(HeaderExtensions), | |||
469 | [&PathExt](PathRef HeaderExt) { | |||
470 | return HeaderExt.equals_lower(PathExt); | |||
471 | }); | |||
472 | ||||
473 | bool IsHeader = HeaderIter != std::end(HeaderExtensions); | |||
474 | ||||
475 | // We can only switch between extensions known extensions. | |||
476 | if (!IsSource && !IsHeader) | |||
477 | return llvm::None; | |||
478 | ||||
479 | // Array to lookup extensions for the switch. An opposite of where original | |||
480 | // extension was found. | |||
481 | ArrayRef<StringRef> NewExts; | |||
482 | if (IsSource) | |||
483 | NewExts = HeaderExtensions; | |||
484 | else | |||
485 | NewExts = SourceExtensions; | |||
486 | ||||
487 | // Storage for the new path. | |||
488 | SmallString<128> NewPath = StringRef(Path); | |||
489 | ||||
490 | // Instance of vfs::FileSystem, used for file existence checks. | |||
491 | auto FS = FSProvider.getTaggedFileSystem(Path).Value; | |||
492 | ||||
493 | // Loop through switched extension candidates. | |||
494 | for (StringRef NewExt : NewExts) { | |||
495 | llvm::sys::path::replace_extension(NewPath, NewExt); | |||
496 | if (FS->exists(NewPath)) | |||
497 | return NewPath.str().str(); // First str() to convert from SmallString to | |||
498 | // StringRef, second to convert from StringRef | |||
499 | // to std::string | |||
500 | ||||
501 | // Also check NewExt in upper-case, just in case. | |||
502 | llvm::sys::path::replace_extension(NewPath, NewExt.upper()); | |||
503 | if (FS->exists(NewPath)) | |||
504 | return NewPath.str().str(); | |||
505 | } | |||
506 | ||||
507 | return llvm::None; | |||
508 | } | |||
509 | ||||
510 | llvm::Expected<tooling::Replacements> | |||
511 | ClangdServer::formatCode(llvm::StringRef Code, PathRef File, | |||
512 | ArrayRef<tooling::Range> Ranges) { | |||
513 | // Call clang-format. | |||
514 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
515 | auto StyleOrError = | |||
516 | format::getStyle("file", File, "LLVM", Code, TaggedFS.Value.get()); | |||
517 | if (!StyleOrError) { | |||
518 | return StyleOrError.takeError(); | |||
519 | } else { | |||
520 | return format::reformat(StyleOrError.get(), Code, Ranges, File); | |||
521 | } | |||
522 | } | |||
523 | ||||
524 | llvm::Expected<Tagged<std::vector<DocumentHighlight>>> | |||
525 | ClangdServer::findDocumentHighlights(const Context &Ctx, PathRef File, | |||
526 | Position Pos) { | |||
527 | auto FileContents = DraftMgr.getDraft(File); | |||
528 | if (!FileContents.Draft) | |||
529 | return llvm::make_error<llvm::StringError>( | |||
530 | "findDocumentHighlights called on non-added file", | |||
531 | llvm::errc::invalid_argument); | |||
532 | ||||
533 | auto TaggedFS = FSProvider.getTaggedFileSystem(File); | |||
534 | ||||
535 | std::shared_ptr<CppFile> Resources = Units.getFile(File); | |||
536 | if (!Resources) | |||
537 | return llvm::make_error<llvm::StringError>( | |||
538 | "findDocumentHighlights called on non-added file", | |||
539 | llvm::errc::invalid_argument); | |||
540 | ||||
541 | std::vector<DocumentHighlight> Result; | |||
542 | llvm::Optional<llvm::Error> Err; | |||
543 | Resources->getAST().get()->runUnderLock([Pos, &Ctx, &Err, | |||
544 | &Result](ParsedAST *AST) { | |||
545 | if (!AST) { | |||
546 | Err = llvm::make_error<llvm::StringError>("Invalid AST", | |||
547 | llvm::errc::invalid_argument); | |||
548 | return; | |||
549 | } | |||
550 | Result = clangd::findDocumentHighlights(Ctx, *AST, Pos); | |||
551 | }); | |||
552 | ||||
553 | if (Err) | |||
554 | return std::move(*Err); | |||
555 | return make_tagged(Result, TaggedFS.Tag); | |||
556 | } | |||
557 | ||||
558 | std::future<Context> ClangdServer::scheduleReparseAndDiags( | |||
559 | Context Ctx, PathRef File, VersionedDraft Contents, | |||
560 | std::shared_ptr<CppFile> Resources, | |||
561 | Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS) { | |||
562 | assert(Contents.Draft && "Draft must have contents")(static_cast <bool> (Contents.Draft && "Draft must have contents" ) ? void (0) : __assert_fail ("Contents.Draft && \"Draft must have contents\"" , "/build/llvm-toolchain-snapshot-7~svn323740/tools/clang/tools/extra/clangd/ClangdServer.cpp" , 562, __extension__ __PRETTY_FUNCTION__)); | |||
563 | ParseInputs Inputs = {CompileArgs.getCompileCommand(File), | |||
564 | std::move(TaggedFS.Value), *std::move(Contents.Draft)}; | |||
565 | ||||
566 | UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)> | |||
567 | DeferredRebuild = Resources->deferRebuild(std::move(Inputs)); | |||
568 | std::promise<Context> DonePromise; | |||
569 | std::future<Context> DoneFuture = DonePromise.get_future(); | |||
570 | ||||
571 | DocVersion Version = Contents.Version; | |||
572 | Path FileStr = File; | |||
573 | VFSTag Tag = TaggedFS.Tag; | |||
574 | auto ReparseAndPublishDiags = | |||
575 | [this, FileStr, Version, | |||
576 | Tag](UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>( | |||
577 | const Context &)> | |||
578 | DeferredRebuild, | |||
579 | std::promise<Context> DonePromise, Context Ctx) -> void { | |||
580 | auto Guard = | |||
581 | llvm::make_scope_exit([&]() { DonePromise.set_value(std::move(Ctx)); }); | |||
582 | ||||
583 | auto CurrentVersion = DraftMgr.getVersion(FileStr); | |||
584 | if (CurrentVersion != Version) | |||
585 | return; // This request is outdated | |||
586 | ||||
587 | auto Diags = DeferredRebuild(Ctx); | |||
588 | if (!Diags) | |||
589 | return; // A new reparse was requested before this one completed. | |||
590 | ||||
591 | // We need to serialize access to resulting diagnostics to avoid calling | |||
592 | // `onDiagnosticsReady` in the wrong order. | |||
593 | std::lock_guard<std::mutex> DiagsLock(DiagnosticsMutex); | |||
594 | DocVersion &LastReportedDiagsVersion = ReportedDiagnosticVersions[FileStr]; | |||
595 | // FIXME(ibiryukov): get rid of '<' comparison here. In the current | |||
596 | // implementation diagnostics will not be reported after version counters' | |||
597 | // overflow. This should not happen in practice, since DocVersion is a | |||
598 | // 64-bit unsigned integer. | |||
599 | if (Version < LastReportedDiagsVersion) | |||
600 | return; | |||
601 | LastReportedDiagsVersion = Version; | |||
602 | ||||
603 | DiagConsumer.onDiagnosticsReady(Ctx, FileStr, | |||
604 | make_tagged(std::move(*Diags), Tag)); | |||
605 | }; | |||
606 | ||||
607 | WorkScheduler.addToFront(std::move(ReparseAndPublishDiags), | |||
608 | std::move(DeferredRebuild), std::move(DonePromise), | |||
609 | std::move(Ctx)); | |||
610 | return DoneFuture; | |||
611 | } | |||
612 | ||||
613 | std::future<Context> | |||
614 | ClangdServer::scheduleCancelRebuild(Context Ctx, | |||
615 | std::shared_ptr<CppFile> Resources) { | |||
616 | std::promise<Context> DonePromise; | |||
617 | std::future<Context> DoneFuture = DonePromise.get_future(); | |||
618 | if (!Resources) { | |||
619 | // No need to schedule any cleanup. | |||
620 | DonePromise.set_value(std::move(Ctx)); | |||
621 | return DoneFuture; | |||
622 | } | |||
623 | ||||
624 | UniqueFunction<void()> DeferredCancel = Resources->deferCancelRebuild(); | |||
625 | auto CancelReparses = [Resources](std::promise<Context> DonePromise, | |||
626 | UniqueFunction<void()> DeferredCancel, | |||
627 | Context Ctx) { | |||
628 | DeferredCancel(); | |||
629 | DonePromise.set_value(std::move(Ctx)); | |||
630 | }; | |||
631 | WorkScheduler.addToFront(std::move(CancelReparses), std::move(DonePromise), | |||
632 | std::move(DeferredCancel), std::move(Ctx)); | |||
633 | return DoneFuture; | |||
634 | } | |||
635 | ||||
636 | void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { | |||
637 | // FIXME: Do nothing for now. This will be used for indexing and potentially | |||
638 | // invalidating other caches. | |||
639 | } | |||
640 | ||||
641 | std::vector<std::pair<Path, std::size_t>> | |||
642 | ClangdServer::getUsedBytesPerFile() const { | |||
643 | return Units.getUsedBytesPerFile(); | |||
644 | } |
1 | //===--- ClangdUnit.h -------------------------------------------*- 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 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H |
11 | #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H |
12 | |
13 | #include "Context.h" |
14 | #include "Function.h" |
15 | #include "Path.h" |
16 | #include "Protocol.h" |
17 | #include "clang/Frontend/FrontendAction.h" |
18 | #include "clang/Frontend/PrecompiledPreamble.h" |
19 | #include "clang/Serialization/ASTBitCodes.h" |
20 | #include "clang/Tooling/CompilationDatabase.h" |
21 | #include "clang/Tooling/Core/Replacement.h" |
22 | #include <atomic> |
23 | #include <future> |
24 | #include <memory> |
25 | #include <mutex> |
26 | |
27 | namespace llvm { |
28 | class raw_ostream; |
29 | } |
30 | |
31 | namespace clang { |
32 | class PCHContainerOperations; |
33 | |
34 | namespace vfs { |
35 | class FileSystem; |
36 | } |
37 | |
38 | namespace tooling { |
39 | struct CompileCommand; |
40 | } |
41 | |
42 | namespace clangd { |
43 | |
44 | /// A diagnostic with its FixIts. |
45 | struct DiagWithFixIts { |
46 | clangd::Diagnostic Diag; |
47 | llvm::SmallVector<TextEdit, 1> FixIts; |
48 | }; |
49 | |
50 | // Stores Preamble and associated data. |
51 | struct PreambleData { |
52 | PreambleData(PrecompiledPreamble Preamble, |
53 | std::vector<serialization::DeclID> TopLevelDeclIDs, |
54 | std::vector<DiagWithFixIts> Diags); |
55 | |
56 | PrecompiledPreamble Preamble; |
57 | std::vector<serialization::DeclID> TopLevelDeclIDs; |
58 | std::vector<DiagWithFixIts> Diags; |
59 | }; |
60 | |
61 | /// Information required to run clang, e.g. to parse AST or do code completion. |
62 | struct ParseInputs { |
63 | tooling::CompileCommand CompileCommand; |
64 | IntrusiveRefCntPtr<vfs::FileSystem> FS; |
65 | std::string Contents; |
66 | }; |
67 | |
68 | /// Stores and provides access to parsed AST. |
69 | class ParsedAST { |
70 | public: |
71 | /// Attempts to run Clang and store parsed AST. If \p Preamble is non-null |
72 | /// it is reused during parsing. |
73 | static llvm::Optional<ParsedAST> |
74 | Build(const Context &Ctx, std::unique_ptr<clang::CompilerInvocation> CI, |
75 | std::shared_ptr<const PreambleData> Preamble, |
76 | std::unique_ptr<llvm::MemoryBuffer> Buffer, |
77 | std::shared_ptr<PCHContainerOperations> PCHs, |
78 | IntrusiveRefCntPtr<vfs::FileSystem> VFS); |
79 | |
80 | ParsedAST(ParsedAST &&Other); |
81 | ParsedAST &operator=(ParsedAST &&Other); |
82 | |
83 | ~ParsedAST(); |
84 | |
85 | ASTContext &getASTContext(); |
86 | const ASTContext &getASTContext() const; |
87 | |
88 | Preprocessor &getPreprocessor(); |
89 | std::shared_ptr<Preprocessor> getPreprocessorPtr(); |
90 | const Preprocessor &getPreprocessor() const; |
91 | |
92 | /// This function returns all top-level decls, including those that come |
93 | /// from Preamble. Decls, coming from Preamble, have to be deserialized, so |
94 | /// this call might be expensive. |
95 | ArrayRef<const Decl *> getTopLevelDecls(); |
96 | |
97 | const std::vector<DiagWithFixIts> &getDiagnostics() const; |
98 | |
99 | /// Returns the esitmated size of the AST and the accessory structures, in |
100 | /// bytes. Does not include the size of the preamble. |
101 | std::size_t getUsedBytes() const; |
102 | |
103 | private: |
104 | ParsedAST(std::shared_ptr<const PreambleData> Preamble, |
105 | std::unique_ptr<CompilerInstance> Clang, |
106 | std::unique_ptr<FrontendAction> Action, |
107 | std::vector<const Decl *> TopLevelDecls, |
108 | std::vector<DiagWithFixIts> Diags); |
109 | |
110 | private: |
111 | void ensurePreambleDeclsDeserialized(); |
112 | |
113 | // In-memory preambles must outlive the AST, it is important that this member |
114 | // goes before Clang and Action. |
115 | std::shared_ptr<const PreambleData> Preamble; |
116 | // We store an "incomplete" FrontendAction (i.e. no EndSourceFile was called |
117 | // on it) and CompilerInstance used to run it. That way we don't have to do |
118 | // complex memory management of all Clang structures on our own. (They are |
119 | // stored in CompilerInstance and cleaned up by |
120 | // FrontendAction.EndSourceFile). |
121 | std::unique_ptr<CompilerInstance> Clang; |
122 | std::unique_ptr<FrontendAction> Action; |
123 | |
124 | // Data, stored after parsing. |
125 | std::vector<DiagWithFixIts> Diags; |
126 | std::vector<const Decl *> TopLevelDecls; |
127 | bool PreambleDeclsDeserialized; |
128 | }; |
129 | |
130 | // Provides thread-safe access to ParsedAST. |
131 | class ParsedASTWrapper { |
132 | public: |
133 | ParsedASTWrapper(ParsedASTWrapper &&Wrapper); |
134 | ParsedASTWrapper(llvm::Optional<ParsedAST> AST); |
135 | |
136 | /// Runs \p F on wrapped ParsedAST under lock. Ensures it is not accessed |
137 | /// concurrently. |
138 | template <class Func> void runUnderLock(Func F) const { |
139 | std::lock_guard<std::mutex> Lock(Mutex); |
140 | F(AST ? AST.getPointer() : nullptr); |
141 | } |
142 | |
143 | private: |
144 | // This wrapper is used as an argument to std::shared_future (and it returns a |
145 | // const ref in get()), but we need to have non-const ref in order to |
146 | // implement some features. |
147 | mutable std::mutex Mutex; |
148 | mutable llvm::Optional<ParsedAST> AST; |
149 | }; |
150 | |
151 | using ASTParsedCallback = |
152 | std::function<void(const Context &Ctx, PathRef Path, ParsedAST *)>; |
153 | |
154 | /// Manages resources, required by clangd. Allows to rebuild file with new |
155 | /// contents, and provides AST and Preamble for it. |
156 | class CppFile : public std::enable_shared_from_this<CppFile> { |
157 | public: |
158 | // We only allow to create CppFile as shared_ptr, because a future returned by |
159 | // deferRebuild will hold references to it. |
160 | static std::shared_ptr<CppFile> |
161 | Create(PathRef FileName, bool StorePreamblesInMemory, |
162 | std::shared_ptr<PCHContainerOperations> PCHs, |
163 | ASTParsedCallback ASTCallback); |
164 | |
165 | private: |
166 | CppFile(PathRef FileName, bool StorePreamblesInMemory, |
167 | std::shared_ptr<PCHContainerOperations> PCHs, |
168 | ASTParsedCallback ASTCallback); |
169 | |
170 | public: |
171 | CppFile(CppFile const &) = delete; |
172 | CppFile(CppFile &&) = delete; |
173 | |
174 | /// Cancels a scheduled rebuild, if any, and sets AST and Preamble to nulls. |
175 | /// If a rebuild is in progress, will wait for it to finish. |
176 | void cancelRebuild(); |
177 | |
178 | /// Similar to deferRebuild, but sets both Preamble and AST to nulls instead |
179 | /// of doing an actual parsing. Returned function is a deferred computation |
180 | /// that will wait for any ongoing rebuilds to finish and actually set the AST |
181 | /// and Preamble to nulls. It can be run on a different thread. This function |
182 | /// is useful to cancel ongoing rebuilds, if any, before removing CppFile. |
183 | UniqueFunction<void()> deferCancelRebuild(); |
184 | |
185 | /// Rebuild AST and Preamble synchronously on the calling thread. |
186 | /// Returns a list of diagnostics or a llvm::None, if another rebuild was |
187 | /// requested in parallel (effectively cancelling this rebuild) before |
188 | /// diagnostics were produced. |
189 | llvm::Optional<std::vector<DiagWithFixIts>> rebuild(const Context &Ctx, |
190 | ParseInputs &&Inputs); |
191 | |
192 | /// Schedule a rebuild and return a deferred computation that will finish the |
193 | /// rebuild, that can be called on a different thread. |
194 | /// After calling this method, resources, available via futures returned by |
195 | /// getPreamble() and getAST(), will be waiting for rebuild to finish. A |
196 | /// continuation fininshing rebuild, returned by this function, must be |
197 | /// computed(i.e., operator() must be called on it) in order to make those |
198 | /// resources ready. If deferRebuild is called again before the rebuild is |
199 | /// finished (either because returned future had not been called or because it |
200 | /// had not returned yet), the previous rebuild request is cancelled and the |
201 | /// resource futures (returned by getPreamble() or getAST()) that were not |
202 | /// ready will be waiting for the last rebuild to finish instead. |
203 | /// The future to finish rebuild returns a list of diagnostics built during |
204 | /// reparse, or None, if another deferRebuild was called before this |
205 | /// rebuild was finished. |
206 | UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)> |
207 | deferRebuild(ParseInputs &&Inputs); |
208 | |
209 | /// Returns a future to get the most fresh PreambleData for a file. The |
210 | /// future will wait until the Preamble is rebuilt. |
211 | std::shared_future<std::shared_ptr<const PreambleData>> getPreamble() const; |
212 | /// Return some preamble for a file. It might be stale, but won't wait for |
213 | /// rebuild to finish. |
214 | std::shared_ptr<const PreambleData> getPossiblyStalePreamble() const; |
215 | |
216 | /// Returns a future to get the most fresh AST for a file. Returned AST is |
217 | /// wrapped to prevent concurrent accesses. |
218 | /// We use std::shared_ptr here because MVSC fails to compile non-copyable |
219 | /// classes as template arguments of promise/future. It is guaranteed to |
220 | /// always be non-null. |
221 | std::shared_future<std::shared_ptr<ParsedASTWrapper>> getAST() const; |
222 | |
223 | /// Returns an estimated size, in bytes, currently occupied by the AST and the |
224 | /// Preamble. |
225 | std::size_t getUsedBytes() const; |
226 | |
227 | private: |
228 | /// A helper guard that manages the state of CppFile during rebuild. |
229 | class RebuildGuard { |
230 | public: |
231 | RebuildGuard(CppFile &File, unsigned RequestRebuildCounter); |
232 | ~RebuildGuard(); |
233 | |
234 | bool wasCancelledBeforeConstruction() const; |
235 | |
236 | private: |
237 | CppFile &File; |
238 | unsigned RequestRebuildCounter; |
239 | bool WasCancelledBeforeConstruction; |
240 | }; |
241 | |
242 | Path FileName; |
243 | bool StorePreamblesInMemory; |
244 | |
245 | /// Mutex protects all fields, declared below it, FileName and Command are not |
246 | /// mutated. |
247 | mutable std::mutex Mutex; |
248 | /// A counter to cancel old rebuilds. |
249 | unsigned RebuildCounter; |
250 | /// Used to wait when rebuild is finished before starting another one. |
251 | bool RebuildInProgress; |
252 | /// Condition variable to indicate changes to RebuildInProgress. |
253 | std::condition_variable RebuildCond; |
254 | |
255 | /// Size of the last built AST, in bytes. |
256 | std::size_t ASTMemUsage; |
257 | /// Size of the last build Preamble, in bytes. |
258 | std::size_t PreambleMemUsage; |
259 | |
260 | /// Promise and future for the latests AST. Fulfilled during rebuild. |
261 | /// We use std::shared_ptr here because MVSC fails to compile non-copyable |
262 | /// classes as template arguments of promise/future. |
263 | std::promise<std::shared_ptr<ParsedASTWrapper>> ASTPromise; |
264 | std::shared_future<std::shared_ptr<ParsedASTWrapper>> ASTFuture; |
265 | |
266 | /// Promise and future for the latests Preamble. Fulfilled during rebuild. |
267 | std::promise<std::shared_ptr<const PreambleData>> PreamblePromise; |
268 | std::shared_future<std::shared_ptr<const PreambleData>> PreambleFuture; |
269 | /// Latest preamble that was built. May be stale, but always available without |
270 | /// waiting for rebuild to finish. |
271 | std::shared_ptr<const PreambleData> LatestAvailablePreamble; |
272 | /// Utility class, required by clang. |
273 | std::shared_ptr<PCHContainerOperations> PCHs; |
274 | /// This is called after the file is parsed. This can be nullptr if there is |
275 | /// no callback. |
276 | ASTParsedCallback ASTCallback; |
277 | }; |
278 | |
279 | /// Get the beginning SourceLocation at a specified \p Pos. |
280 | SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, |
281 | const FileEntry *FE); |
282 | |
283 | /// For testing/debugging purposes. Note that this method deserializes all |
284 | /// unserialized Decls, so use with care. |
285 | void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS); |
286 | |
287 | } // namespace clangd |
288 | } // namespace clang |
289 | #endif |