1 //===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===-------------------------------------------------------------------===//
8
9 #include "ClangdServer.h"
10 #include "CodeComplete.h"
11 #include "Config.h"
12 #include "DumpAST.h"
13 #include "FindSymbols.h"
14 #include "Format.h"
15 #include "HeaderSourceSwitch.h"
16 #include "Headers.h"
17 #include "ParsedAST.h"
18 #include "Preamble.h"
19 #include "Protocol.h"
20 #include "SemanticHighlighting.h"
21 #include "SemanticSelection.h"
22 #include "SourceCode.h"
23 #include "TUScheduler.h"
24 #include "XRefs.h"
25 #include "index/CanonicalIncludes.h"
26 #include "index/FileIndex.h"
27 #include "index/Merge.h"
28 #include "refactor/Rename.h"
29 #include "refactor/Tweak.h"
30 #include "support/Logger.h"
31 #include "support/Markup.h"
32 #include "support/MemoryTree.h"
33 #include "support/ThreadsafeFS.h"
34 #include "support/Trace.h"
35 #include "clang/Format/Format.h"
36 #include "clang/Frontend/CompilerInstance.h"
37 #include "clang/Frontend/CompilerInvocation.h"
38 #include "clang/Lex/Preprocessor.h"
39 #include "clang/Tooling/CompilationDatabase.h"
40 #include "clang/Tooling/Core/Replacement.h"
41 #include "llvm/ADT/ArrayRef.h"
42 #include "llvm/ADT/Optional.h"
43 #include "llvm/ADT/STLExtras.h"
44 #include "llvm/ADT/ScopeExit.h"
45 #include "llvm/ADT/StringExtras.h"
46 #include "llvm/ADT/StringRef.h"
47 #include "llvm/Support/Errc.h"
48 #include "llvm/Support/Error.h"
49 #include "llvm/Support/FileSystem.h"
50 #include "llvm/Support/Path.h"
51 #include "llvm/Support/ScopedPrinter.h"
52 #include "llvm/Support/raw_ostream.h"
53 #include <algorithm>
54 #include <chrono>
55 #include <future>
56 #include <memory>
57 #include <mutex>
58 #include <string>
59 #include <type_traits>
60
61 namespace clang {
62 namespace clangd {
63 namespace {
64
65 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
66 struct UpdateIndexCallbacks : public ParsingCallbacks {
UpdateIndexCallbacksclang::clangd::__anonb0c0fe120111::UpdateIndexCallbacks67 UpdateIndexCallbacks(FileIndex *FIndex,
68 ClangdServer::Callbacks *ServerCallbacks,
69 bool TheiaSemanticHighlighting)
70 : FIndex(FIndex), ServerCallbacks(ServerCallbacks),
71 TheiaSemanticHighlighting(TheiaSemanticHighlighting) {}
72
onPreambleASTclang::clangd::__anonb0c0fe120111::UpdateIndexCallbacks73 void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx,
74 std::shared_ptr<clang::Preprocessor> PP,
75 const CanonicalIncludes &CanonIncludes) override {
76 if (FIndex)
77 FIndex->updatePreamble(Path, Version, Ctx, std::move(PP), CanonIncludes);
78 }
79
onMainASTclang::clangd::__anonb0c0fe120111::UpdateIndexCallbacks80 void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override {
81 if (FIndex)
82 FIndex->updateMain(Path, AST);
83
84 std::vector<Diag> Diagnostics = AST.getDiagnostics();
85 std::vector<HighlightingToken> Highlightings;
86 if (TheiaSemanticHighlighting)
87 Highlightings = getSemanticHighlightings(AST);
88
89 if (ServerCallbacks)
90 Publish([&]() {
91 ServerCallbacks->onDiagnosticsReady(Path, AST.version(),
92 std::move(Diagnostics));
93 if (TheiaSemanticHighlighting)
94 ServerCallbacks->onHighlightingsReady(Path, AST.version(),
95 std::move(Highlightings));
96 });
97 }
98
onFailedASTclang::clangd::__anonb0c0fe120111::UpdateIndexCallbacks99 void onFailedAST(PathRef Path, llvm::StringRef Version,
100 std::vector<Diag> Diags, PublishFn Publish) override {
101 if (ServerCallbacks)
102 Publish(
103 [&]() { ServerCallbacks->onDiagnosticsReady(Path, Version, Diags); });
104 }
105
onFileUpdatedclang::clangd::__anonb0c0fe120111::UpdateIndexCallbacks106 void onFileUpdated(PathRef File, const TUStatus &Status) override {
107 if (ServerCallbacks)
108 ServerCallbacks->onFileUpdated(File, Status);
109 }
110
111 private:
112 FileIndex *FIndex;
113 ClangdServer::Callbacks *ServerCallbacks;
114 bool TheiaSemanticHighlighting;
115 };
116
117 } // namespace
118
optsForTest()119 ClangdServer::Options ClangdServer::optsForTest() {
120 ClangdServer::Options Opts;
121 Opts.UpdateDebounce = DebouncePolicy::fixed(/*zero*/ {});
122 Opts.StorePreamblesInMemory = true;
123 Opts.AsyncThreadsCount = 4; // Consistent!
124 Opts.TheiaSemanticHighlighting = true;
125 Opts.AsyncPreambleBuilds = true;
126 return Opts;
127 }
128
operator TUScheduler::Options() const129 ClangdServer::Options::operator TUScheduler::Options() const {
130 TUScheduler::Options Opts;
131 Opts.AsyncThreadsCount = AsyncThreadsCount;
132 Opts.RetentionPolicy = RetentionPolicy;
133 Opts.StorePreamblesInMemory = StorePreamblesInMemory;
134 Opts.UpdateDebounce = UpdateDebounce;
135 Opts.AsyncPreambleBuilds = AsyncPreambleBuilds;
136 return Opts;
137 }
138
ClangdServer(const GlobalCompilationDatabase & CDB,const ThreadsafeFS & TFS,const Options & Opts,Callbacks * Callbacks)139 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
140 const ThreadsafeFS &TFS, const Options &Opts,
141 Callbacks *Callbacks)
142 : ConfigProvider(Opts.ConfigProvider), TFS(TFS), ServerCallbacks(Callbacks),
143 DynamicIdx(Opts.BuildDynamicSymbolIndex
144 ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex,
145 Opts.CollectMainFileRefs)
146 : nullptr),
147 ClangTidyProvider(Opts.ClangTidyProvider),
148 SuggestMissingIncludes(Opts.SuggestMissingIncludes),
149 BuildRecoveryAST(Opts.BuildRecoveryAST),
150 PreserveRecoveryASTType(Opts.PreserveRecoveryASTType),
151 WorkspaceRoot(Opts.WorkspaceRoot),
152 // Pass a callback into `WorkScheduler` to extract symbols from a newly
153 // parsed file and rebuild the file index synchronously each time an AST
154 // is parsed.
155 // FIXME(ioeric): this can be slow and we may be able to index on less
156 // critical paths.
157 WorkScheduler(
158 CDB,
159 [&, this] {
160 TUScheduler::Options O(Opts);
161 O.ContextProvider = [this](PathRef P) {
162 return createProcessingContext(P);
163 };
164 return O;
165 }(),
166 std::make_unique<UpdateIndexCallbacks>(
167 DynamicIdx.get(), Callbacks, Opts.TheiaSemanticHighlighting)) {
168 // Adds an index to the stack, at higher priority than existing indexes.
__anonb0c0fe120602(SymbolIndex *Idx) 169 auto AddIndex = [&](SymbolIndex *Idx) {
170 if (this->Index != nullptr) {
171 MergedIdx.push_back(std::make_unique<MergedIndex>(Idx, this->Index));
172 this->Index = MergedIdx.back().get();
173 } else {
174 this->Index = Idx;
175 }
176 };
177 if (Opts.StaticIndex)
178 AddIndex(Opts.StaticIndex);
179 if (Opts.BackgroundIndex) {
180 BackgroundIndex::Options BGOpts;
181 BGOpts.ThreadPoolSize = std::max(Opts.AsyncThreadsCount, 1u);
__anonb0c0fe120702(BackgroundQueue::Stats S) 182 BGOpts.OnProgress = [Callbacks](BackgroundQueue::Stats S) {
183 if (Callbacks)
184 Callbacks->onBackgroundIndexProgress(S);
185 };
__anonb0c0fe120802(PathRef P) 186 BGOpts.ContextProvider = [this](PathRef P) {
187 return createProcessingContext(P);
188 };
189 BGOpts.CollectMainFileRefs = Opts.CollectMainFileRefs;
190 BackgroundIdx = std::make_unique<BackgroundIndex>(
191 TFS, CDB,
192 BackgroundIndexStorage::createDiskBackedStorageFactory(
__anonb0c0fe120902(llvm::StringRef File) 193 [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }),
194 std::move(BGOpts));
195 AddIndex(BackgroundIdx.get());
196 }
197 if (DynamicIdx)
198 AddIndex(DynamicIdx.get());
199 }
200
addDocument(PathRef File,llvm::StringRef Contents,llvm::StringRef Version,WantDiagnostics WantDiags,bool ForceRebuild)201 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
202 llvm::StringRef Version,
203 WantDiagnostics WantDiags, bool ForceRebuild) {
204 ParseOptions Opts;
205 Opts.SuggestMissingIncludes = SuggestMissingIncludes;
206
207 // Compile command is set asynchronously during update, as it can be slow.
208 ParseInputs Inputs;
209 Inputs.TFS = &TFS;
210 Inputs.Contents = std::string(Contents);
211 Inputs.Version = Version.str();
212 Inputs.ForceRebuild = ForceRebuild;
213 Inputs.Opts = std::move(Opts);
214 Inputs.Index = Index;
215 Inputs.ClangTidyProvider = ClangTidyProvider;
216 Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST;
217 Inputs.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType;
218 bool NewFile = WorkScheduler.update(File, Inputs, WantDiags);
219 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
220 if (NewFile && BackgroundIdx)
221 BackgroundIdx->boostRelated(File);
222 }
223
removeDocument(PathRef File)224 void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); }
225
codeComplete(PathRef File,Position Pos,const clangd::CodeCompleteOptions & Opts,Callback<CodeCompleteResult> CB)226 void ClangdServer::codeComplete(PathRef File, Position Pos,
227 const clangd::CodeCompleteOptions &Opts,
228 Callback<CodeCompleteResult> CB) {
229 // Copy completion options for passing them to async task handler.
230 auto CodeCompleteOpts = Opts;
231 if (!CodeCompleteOpts.Index) // Respect overridden index.
232 CodeCompleteOpts.Index = Index;
233
234 auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB),
235 this](llvm::Expected<InputsAndPreamble> IP) mutable {
236 if (!IP)
237 return CB(IP.takeError());
238 if (auto Reason = isCancelled())
239 return CB(llvm::make_error<CancelledError>(Reason));
240
241 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind;
242 if (!IP->Preamble) {
243 // No speculation in Fallback mode, as it's supposed to be much faster
244 // without compiling.
245 vlog("Build for file {0} is not ready. Enter fallback mode.", File);
246 } else {
247 if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) {
248 SpecFuzzyFind.emplace();
249 {
250 std::lock_guard<std::mutex> Lock(
251 CachedCompletionFuzzyFindRequestMutex);
252 SpecFuzzyFind->CachedReq =
253 CachedCompletionFuzzyFindRequestByFile[File];
254 }
255 }
256 }
257 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()};
258 ParseInput.Index = Index;
259 ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST;
260 ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType;
261
262 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
263 // both the old and the new version in case only one of them matches.
264 CodeCompleteResult Result = clangd::codeComplete(
265 File, Pos, IP->Preamble, ParseInput, CodeCompleteOpts,
266 SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr);
267 {
268 clang::clangd::trace::Span Tracer("Completion results callback");
269 CB(std::move(Result));
270 }
271 if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) {
272 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex);
273 CachedCompletionFuzzyFindRequestByFile[File] =
274 SpecFuzzyFind->NewReq.getValue();
275 }
276 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
277 // We don't want `codeComplete` to wait for the async call if it doesn't use
278 // the result (e.g. non-index completion, speculation fails), so that `CB`
279 // is called as soon as results are available.
280 };
281
282 // We use a potentially-stale preamble because latency is critical here.
283 WorkScheduler.runWithPreamble(
284 "CodeComplete", File,
285 (Opts.RunParser == CodeCompleteOptions::AlwaysParse)
286 ? TUScheduler::Stale
287 : TUScheduler::StaleOrAbsent,
288 std::move(Task));
289 }
290
signatureHelp(PathRef File,Position Pos,Callback<SignatureHelp> CB)291 void ClangdServer::signatureHelp(PathRef File, Position Pos,
292 Callback<SignatureHelp> CB) {
293
294 auto Action = [Pos, File = File.str(), CB = std::move(CB),
295 this](llvm::Expected<InputsAndPreamble> IP) mutable {
296 if (!IP)
297 return CB(IP.takeError());
298
299 const auto *PreambleData = IP->Preamble;
300 if (!PreambleData)
301 return CB(error("Failed to parse includes"));
302
303 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()};
304 ParseInput.Index = Index;
305 ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST;
306 ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType;
307 CB(clangd::signatureHelp(File, Pos, *PreambleData, ParseInput));
308 };
309
310 // Unlike code completion, we wait for a preamble here.
311 WorkScheduler.runWithPreamble("SignatureHelp", File, TUScheduler::Stale,
312 std::move(Action));
313 }
314
formatRange(PathRef File,llvm::StringRef Code,Range Rng,Callback<tooling::Replacements> CB)315 void ClangdServer::formatRange(PathRef File, llvm::StringRef Code, Range Rng,
316 Callback<tooling::Replacements> CB) {
317 llvm::Expected<size_t> Begin = positionToOffset(Code, Rng.start);
318 if (!Begin)
319 return CB(Begin.takeError());
320 llvm::Expected<size_t> End = positionToOffset(Code, Rng.end);
321 if (!End)
322 return CB(End.takeError());
323 formatCode(File, Code, {tooling::Range(*Begin, *End - *Begin)},
324 std::move(CB));
325 }
326
formatFile(PathRef File,llvm::StringRef Code,Callback<tooling::Replacements> CB)327 void ClangdServer::formatFile(PathRef File, llvm::StringRef Code,
328 Callback<tooling::Replacements> CB) {
329 // Format everything.
330 formatCode(File, Code, {tooling::Range(0, Code.size())}, std::move(CB));
331 }
332
formatOnType(PathRef File,llvm::StringRef Code,Position Pos,StringRef TriggerText,Callback<std::vector<TextEdit>> CB)333 void ClangdServer::formatOnType(PathRef File, llvm::StringRef Code,
334 Position Pos, StringRef TriggerText,
335 Callback<std::vector<TextEdit>> CB) {
336 llvm::Expected<size_t> CursorPos = positionToOffset(Code, Pos);
337 if (!CursorPos)
338 return CB(CursorPos.takeError());
339 auto Action = [File = File.str(), Code = Code.str(),
340 TriggerText = TriggerText.str(), CursorPos = *CursorPos,
341 CB = std::move(CB), this]() mutable {
342 auto Style = format::getStyle(format::DefaultFormatStyle, File,
343 format::DefaultFallbackStyle, Code,
344 TFS.view(/*CWD=*/llvm::None).get());
345 if (!Style)
346 return CB(Style.takeError());
347
348 std::vector<TextEdit> Result;
349 for (const tooling::Replacement &R :
350 formatIncremental(Code, CursorPos, TriggerText, *Style))
351 Result.push_back(replacementToEdit(Code, R));
352 return CB(Result);
353 };
354 WorkScheduler.run("FormatOnType", File, std::move(Action));
355 }
356
prepareRename(PathRef File,Position Pos,llvm::Optional<std::string> NewName,const RenameOptions & RenameOpts,Callback<RenameResult> CB)357 void ClangdServer::prepareRename(PathRef File, Position Pos,
358 llvm::Optional<std::string> NewName,
359 const RenameOptions &RenameOpts,
360 Callback<RenameResult> CB) {
361 auto Action = [Pos, File = File.str(), CB = std::move(CB),
362 NewName = std::move(NewName), RenameOpts,
363 this](llvm::Expected<InputsAndAST> InpAST) mutable {
364 if (!InpAST)
365 return CB(InpAST.takeError());
366 // prepareRename is latency-sensitive:
367 // - for single-file rename, performing rename isn't substantially more
368 // expensive than doing an AST-based check (the index is used to see if
369 // the rename is complete);
370 // - for cross-file rename, we deliberately pass a nullptr index to save
371 // the cost, thus the result may be incomplete as it only contains
372 // main-file occurrences;
373 auto Results = clangd::rename(
374 {Pos, NewName.getValueOr("__clangd_rename_dummy"), InpAST->AST, File,
375 RenameOpts.AllowCrossFile ? nullptr : Index, RenameOpts});
376 if (!Results) {
377 // LSP says to return null on failure, but that will result in a generic
378 // failure message. If we send an LSP error response, clients can surface
379 // the message to users (VSCode does).
380 return CB(Results.takeError());
381 }
382 return CB(*Results);
383 };
384 WorkScheduler.runWithAST("PrepareRename", File, std::move(Action));
385 }
386
rename(PathRef File,Position Pos,llvm::StringRef NewName,const RenameOptions & Opts,Callback<RenameResult> CB)387 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
388 const RenameOptions &Opts,
389 Callback<RenameResult> CB) {
390 // A snapshot of all file dirty buffers.
391 llvm::StringMap<std::string> Snapshot = WorkScheduler.getAllFileContents();
392 auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts,
393 CB = std::move(CB), Snapshot = std::move(Snapshot),
394 this](llvm::Expected<InputsAndAST> InpAST) mutable {
395 // Tracks number of files edited per invocation.
396 static constexpr trace::Metric RenameFiles("rename_files",
397 trace::Metric::Distribution);
398 if (!InpAST)
399 return CB(InpAST.takeError());
400 auto GetDirtyBuffer =
401 [&Snapshot](PathRef AbsPath) -> llvm::Optional<std::string> {
402 auto It = Snapshot.find(AbsPath);
403 if (It == Snapshot.end())
404 return llvm::None;
405 return It->second;
406 };
407 auto R = clangd::rename(
408 {Pos, NewName, InpAST->AST, File, Index, Opts, GetDirtyBuffer});
409 if (!R)
410 return CB(R.takeError());
411
412 if (Opts.WantFormat) {
413 auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
414 *InpAST->Inputs.TFS);
415 llvm::Error Err = llvm::Error::success();
416 for (auto &E : R->GlobalChanges)
417 Err =
418 llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err));
419
420 if (Err)
421 return CB(std::move(Err));
422 }
423 RenameFiles.record(R->GlobalChanges.size());
424 return CB(*R);
425 };
426 WorkScheduler.runWithAST("Rename", File, std::move(Action));
427 }
428
429 // May generate several candidate selections, due to SelectionTree ambiguity.
430 // vector of pointers because GCC doesn't like non-copyable Selection.
431 static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>>
tweakSelection(const Range & Sel,const InputsAndAST & AST)432 tweakSelection(const Range &Sel, const InputsAndAST &AST) {
433 auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start);
434 if (!Begin)
435 return Begin.takeError();
436 auto End = positionToOffset(AST.Inputs.Contents, Sel.end);
437 if (!End)
438 return End.takeError();
439 std::vector<std::unique_ptr<Tweak::Selection>> Result;
440 SelectionTree::createEach(
441 AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End,
442 [&](SelectionTree T) {
443 Result.push_back(std::make_unique<Tweak::Selection>(
444 AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T)));
445 return false;
446 });
447 assert(!Result.empty() && "Expected at least one SelectionTree");
448 return std::move(Result);
449 }
450
enumerateTweaks(PathRef File,Range Sel,llvm::unique_function<bool (const Tweak &)> Filter,Callback<std::vector<TweakRef>> CB)451 void ClangdServer::enumerateTweaks(
452 PathRef File, Range Sel, llvm::unique_function<bool(const Tweak &)> Filter,
453 Callback<std::vector<TweakRef>> CB) {
454 // Tracks number of times a tweak has been offered.
455 static constexpr trace::Metric TweakAvailable(
456 "tweak_available", trace::Metric::Counter, "tweak_id");
457 auto Action = [File = File.str(), Sel, CB = std::move(CB),
458 Filter =
459 std::move(Filter)](Expected<InputsAndAST> InpAST) mutable {
460 if (!InpAST)
461 return CB(InpAST.takeError());
462 auto Selections = tweakSelection(Sel, *InpAST);
463 if (!Selections)
464 return CB(Selections.takeError());
465 std::vector<TweakRef> Res;
466 // Don't allow a tweak to fire more than once across ambiguous selections.
467 llvm::DenseSet<llvm::StringRef> PreparedTweaks;
468 auto DeduplicatingFilter = [&](const Tweak &T) {
469 return Filter(T) && !PreparedTweaks.count(T.id());
470 };
471 for (const auto &Sel : *Selections) {
472 for (auto &T : prepareTweaks(*Sel, DeduplicatingFilter)) {
473 Res.push_back({T->id(), T->title(), T->kind()});
474 PreparedTweaks.insert(T->id());
475 TweakAvailable.record(1, T->id());
476 }
477 }
478
479 CB(std::move(Res));
480 };
481
482 WorkScheduler.runWithAST("EnumerateTweaks", File, std::move(Action),
483 TUScheduler::InvalidateOnUpdate);
484 }
485
applyTweak(PathRef File,Range Sel,StringRef TweakID,Callback<Tweak::Effect> CB)486 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
487 Callback<Tweak::Effect> CB) {
488 // Tracks number of times a tweak has been attempted.
489 static constexpr trace::Metric TweakAttempt(
490 "tweak_attempt", trace::Metric::Counter, "tweak_id");
491 // Tracks number of times a tweak has failed to produce edits.
492 static constexpr trace::Metric TweakFailed(
493 "tweak_failed", trace::Metric::Counter, "tweak_id");
494 TweakAttempt.record(1, TweakID);
495 auto Action = [File = File.str(), Sel, TweakID = TweakID.str(),
496 CB = std::move(CB),
497 this](Expected<InputsAndAST> InpAST) mutable {
498 if (!InpAST)
499 return CB(InpAST.takeError());
500 auto Selections = tweakSelection(Sel, *InpAST);
501 if (!Selections)
502 return CB(Selections.takeError());
503 llvm::Optional<llvm::Expected<Tweak::Effect>> Effect;
504 // Try each selection, take the first one that prepare()s.
505 // If they all fail, Effect will hold get the last error.
506 for (const auto &Selection : *Selections) {
507 auto T = prepareTweak(TweakID, *Selection);
508 if (T) {
509 Effect = (*T)->apply(*Selection);
510 break;
511 }
512 Effect = T.takeError();
513 }
514 assert(Effect.hasValue() && "Expected at least one selection");
515 if (*Effect) {
516 // Tweaks don't apply clang-format, do that centrally here.
517 for (auto &It : (*Effect)->ApplyEdits) {
518 Edit &E = It.second;
519 format::FormatStyle Style =
520 getFormatStyleForFile(File, E.InitialCode, TFS);
521 if (llvm::Error Err = reformatEdit(E, Style))
522 elog("Failed to format {0}: {1}", It.first(), std::move(Err));
523 }
524 } else {
525 TweakFailed.record(1, TweakID);
526 }
527 return CB(std::move(*Effect));
528 };
529 WorkScheduler.runWithAST("ApplyTweak", File, std::move(Action));
530 }
531
locateSymbolAt(PathRef File,Position Pos,Callback<std::vector<LocatedSymbol>> CB)532 void ClangdServer::locateSymbolAt(PathRef File, Position Pos,
533 Callback<std::vector<LocatedSymbol>> CB) {
534 auto Action = [Pos, CB = std::move(CB),
535 this](llvm::Expected<InputsAndAST> InpAST) mutable {
536 if (!InpAST)
537 return CB(InpAST.takeError());
538 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index));
539 };
540
541 WorkScheduler.runWithAST("Definitions", File, std::move(Action));
542 }
543
switchSourceHeader(PathRef Path,Callback<llvm::Optional<clangd::Path>> CB)544 void ClangdServer::switchSourceHeader(
545 PathRef Path, Callback<llvm::Optional<clangd::Path>> CB) {
546 // We want to return the result as fast as possible, strategy is:
547 // 1) use the file-only heuristic, it requires some IO but it is much
548 // faster than building AST, but it only works when .h/.cc files are in
549 // the same directory.
550 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
551 // different code layout.
552 if (auto CorrespondingFile = getCorrespondingHeaderOrSource(
553 std::string(Path), TFS.view(llvm::None)))
554 return CB(std::move(CorrespondingFile));
555 auto Action = [Path = Path.str(), CB = std::move(CB),
556 this](llvm::Expected<InputsAndAST> InpAST) mutable {
557 if (!InpAST)
558 return CB(InpAST.takeError());
559 CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index));
560 };
561 WorkScheduler.runWithAST("SwitchHeaderSource", Path, std::move(Action));
562 }
563
formatCode(PathRef File,llvm::StringRef Code,llvm::ArrayRef<tooling::Range> Ranges,Callback<tooling::Replacements> CB)564 void ClangdServer::formatCode(PathRef File, llvm::StringRef Code,
565 llvm::ArrayRef<tooling::Range> Ranges,
566 Callback<tooling::Replacements> CB) {
567 // Call clang-format.
568 auto Action = [File = File.str(), Code = Code.str(), Ranges = Ranges.vec(),
569 CB = std::move(CB), this]() mutable {
570 format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS);
571 tooling::Replacements IncludeReplaces =
572 format::sortIncludes(Style, Code, Ranges, File);
573 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
574 if (!Changed)
575 return CB(Changed.takeError());
576
577 CB(IncludeReplaces.merge(format::reformat(
578 Style, *Changed,
579 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges),
580 File)));
581 };
582 WorkScheduler.run("Format", File, std::move(Action));
583 }
584
findDocumentHighlights(PathRef File,Position Pos,Callback<std::vector<DocumentHighlight>> CB)585 void ClangdServer::findDocumentHighlights(
586 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) {
587 auto Action =
588 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
589 if (!InpAST)
590 return CB(InpAST.takeError());
591 CB(clangd::findDocumentHighlights(InpAST->AST, Pos));
592 };
593
594 WorkScheduler.runWithAST("Highlights", File, std::move(Action),
595 TUScheduler::InvalidateOnUpdate);
596 }
597
findHover(PathRef File,Position Pos,Callback<llvm::Optional<HoverInfo>> CB)598 void ClangdServer::findHover(PathRef File, Position Pos,
599 Callback<llvm::Optional<HoverInfo>> CB) {
600 auto Action = [File = File.str(), Pos, CB = std::move(CB),
601 this](llvm::Expected<InputsAndAST> InpAST) mutable {
602 if (!InpAST)
603 return CB(InpAST.takeError());
604 format::FormatStyle Style = getFormatStyleForFile(
605 File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS);
606 CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index));
607 };
608
609 WorkScheduler.runWithAST("Hover", File, std::move(Action),
610 TUScheduler::InvalidateOnUpdate);
611 }
612
typeHierarchy(PathRef File,Position Pos,int Resolve,TypeHierarchyDirection Direction,Callback<Optional<TypeHierarchyItem>> CB)613 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
614 TypeHierarchyDirection Direction,
615 Callback<Optional<TypeHierarchyItem>> CB) {
616 auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
617 this](Expected<InputsAndAST> InpAST) mutable {
618 if (!InpAST)
619 return CB(InpAST.takeError());
620 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index,
621 File));
622 };
623
624 WorkScheduler.runWithAST("Type Hierarchy", File, std::move(Action));
625 }
626
resolveTypeHierarchy(TypeHierarchyItem Item,int Resolve,TypeHierarchyDirection Direction,Callback<llvm::Optional<TypeHierarchyItem>> CB)627 void ClangdServer::resolveTypeHierarchy(
628 TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
629 Callback<llvm::Optional<TypeHierarchyItem>> CB) {
630 WorkScheduler.run(
631 "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable {
632 clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index);
633 CB(Item);
634 });
635 }
636
prepareCallHierarchy(PathRef File,Position Pos,Callback<std::vector<CallHierarchyItem>> CB)637 void ClangdServer::prepareCallHierarchy(
638 PathRef File, Position Pos, Callback<std::vector<CallHierarchyItem>> CB) {
639 auto Action = [File = File.str(), Pos,
640 CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable {
641 if (!InpAST)
642 return CB(InpAST.takeError());
643 CB(clangd::prepareCallHierarchy(InpAST->AST, Pos, File));
644 };
645 WorkScheduler.runWithAST("Call Hierarchy", File, std::move(Action));
646 }
647
incomingCalls(const CallHierarchyItem & Item,Callback<std::vector<CallHierarchyIncomingCall>> CB)648 void ClangdServer::incomingCalls(
649 const CallHierarchyItem &Item,
650 Callback<std::vector<CallHierarchyIncomingCall>> CB) {
651 WorkScheduler.run("Incoming Calls", "",
652 [CB = std::move(CB), Item, this]() mutable {
653 CB(clangd::incomingCalls(Item, Index));
654 });
655 }
656
onFileEvent(const DidChangeWatchedFilesParams & Params)657 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
658 // FIXME: Do nothing for now. This will be used for indexing and potentially
659 // invalidating other caches.
660 }
661
workspaceSymbols(llvm::StringRef Query,int Limit,Callback<std::vector<SymbolInformation>> CB)662 void ClangdServer::workspaceSymbols(
663 llvm::StringRef Query, int Limit,
664 Callback<std::vector<SymbolInformation>> CB) {
665 WorkScheduler.run(
666 "getWorkspaceSymbols", /*Path=*/"",
667 [Query = Query.str(), Limit, CB = std::move(CB), this]() mutable {
668 CB(clangd::getWorkspaceSymbols(Query, Limit, Index,
669 WorkspaceRoot.getValueOr("")));
670 });
671 }
672
documentSymbols(llvm::StringRef File,Callback<std::vector<DocumentSymbol>> CB)673 void ClangdServer::documentSymbols(llvm::StringRef File,
674 Callback<std::vector<DocumentSymbol>> CB) {
675 auto Action =
676 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
677 if (!InpAST)
678 return CB(InpAST.takeError());
679 CB(clangd::getDocumentSymbols(InpAST->AST));
680 };
681 WorkScheduler.runWithAST("documentSymbols", File, std::move(Action),
682 TUScheduler::InvalidateOnUpdate);
683 }
684
foldingRanges(llvm::StringRef File,Callback<std::vector<FoldingRange>> CB)685 void ClangdServer::foldingRanges(llvm::StringRef File,
686 Callback<std::vector<FoldingRange>> CB) {
687 auto Action =
688 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
689 if (!InpAST)
690 return CB(InpAST.takeError());
691 CB(clangd::getFoldingRanges(InpAST->AST));
692 };
693 WorkScheduler.runWithAST("foldingRanges", File, std::move(Action),
694 TUScheduler::InvalidateOnUpdate);
695 }
696
findImplementations(PathRef File,Position Pos,Callback<std::vector<LocatedSymbol>> CB)697 void ClangdServer::findImplementations(
698 PathRef File, Position Pos, Callback<std::vector<LocatedSymbol>> CB) {
699 auto Action = [Pos, CB = std::move(CB),
700 this](llvm::Expected<InputsAndAST> InpAST) mutable {
701 if (!InpAST)
702 return CB(InpAST.takeError());
703 CB(clangd::findImplementations(InpAST->AST, Pos, Index));
704 };
705
706 WorkScheduler.runWithAST("Implementations", File, std::move(Action));
707 }
708
findReferences(PathRef File,Position Pos,uint32_t Limit,Callback<ReferencesResult> CB)709 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit,
710 Callback<ReferencesResult> CB) {
711 auto Action = [Pos, Limit, CB = std::move(CB),
712 this](llvm::Expected<InputsAndAST> InpAST) mutable {
713 if (!InpAST)
714 return CB(InpAST.takeError());
715 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index));
716 };
717
718 WorkScheduler.runWithAST("References", File, std::move(Action));
719 }
720
symbolInfo(PathRef File,Position Pos,Callback<std::vector<SymbolDetails>> CB)721 void ClangdServer::symbolInfo(PathRef File, Position Pos,
722 Callback<std::vector<SymbolDetails>> CB) {
723 auto Action =
724 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
725 if (!InpAST)
726 return CB(InpAST.takeError());
727 CB(clangd::getSymbolInfo(InpAST->AST, Pos));
728 };
729
730 WorkScheduler.runWithAST("SymbolInfo", File, std::move(Action));
731 }
732
semanticRanges(PathRef File,const std::vector<Position> & Positions,Callback<std::vector<SelectionRange>> CB)733 void ClangdServer::semanticRanges(PathRef File,
734 const std::vector<Position> &Positions,
735 Callback<std::vector<SelectionRange>> CB) {
736 auto Action = [Positions, CB = std::move(CB)](
737 llvm::Expected<InputsAndAST> InpAST) mutable {
738 if (!InpAST)
739 return CB(InpAST.takeError());
740 std::vector<SelectionRange> Result;
741 for (const auto &Pos : Positions) {
742 if (auto Range = clangd::getSemanticRanges(InpAST->AST, Pos))
743 Result.push_back(std::move(*Range));
744 else
745 return CB(Range.takeError());
746 }
747 CB(std::move(Result));
748 };
749 WorkScheduler.runWithAST("SemanticRanges", File, std::move(Action));
750 }
751
documentLinks(PathRef File,Callback<std::vector<DocumentLink>> CB)752 void ClangdServer::documentLinks(PathRef File,
753 Callback<std::vector<DocumentLink>> CB) {
754 auto Action =
755 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
756 if (!InpAST)
757 return CB(InpAST.takeError());
758 CB(clangd::getDocumentLinks(InpAST->AST));
759 };
760 WorkScheduler.runWithAST("DocumentLinks", File, std::move(Action),
761 TUScheduler::InvalidateOnUpdate);
762 }
763
semanticHighlights(PathRef File,Callback<std::vector<HighlightingToken>> CB)764 void ClangdServer::semanticHighlights(
765 PathRef File, Callback<std::vector<HighlightingToken>> CB) {
766 auto Action =
767 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
768 if (!InpAST)
769 return CB(InpAST.takeError());
770 CB(clangd::getSemanticHighlightings(InpAST->AST));
771 };
772 WorkScheduler.runWithAST("SemanticHighlights", File, std::move(Action),
773 TUScheduler::InvalidateOnUpdate);
774 }
775
getAST(PathRef File,Range R,Callback<llvm::Optional<ASTNode>> CB)776 void ClangdServer::getAST(PathRef File, Range R,
777 Callback<llvm::Optional<ASTNode>> CB) {
778 auto Action =
779 [R, CB(std::move(CB))](llvm::Expected<InputsAndAST> Inputs) mutable {
780 if (!Inputs)
781 return CB(Inputs.takeError());
782 unsigned Start, End;
783 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R.start))
784 Start = *Offset;
785 else
786 return CB(Offset.takeError());
787 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R.end))
788 End = *Offset;
789 else
790 return CB(Offset.takeError());
791
792 bool Success = SelectionTree::createEach(
793 Inputs->AST.getASTContext(), Inputs->AST.getTokens(), Start, End,
794 [&](SelectionTree T) {
795 if (const SelectionTree::Node *N = T.commonAncestor()) {
796 CB(dumpAST(N->ASTNode, Inputs->AST.getTokens(),
797 Inputs->AST.getASTContext()));
798 return true;
799 }
800 return false;
801 });
802 if (!Success)
803 CB(llvm::None);
804 };
805 WorkScheduler.runWithAST("GetAST", File, std::move(Action));
806 }
807
customAction(PathRef File,llvm::StringRef Name,Callback<InputsAndAST> Action)808 void ClangdServer::customAction(PathRef File, llvm::StringRef Name,
809 Callback<InputsAndAST> Action) {
810 WorkScheduler.runWithAST(Name, File, std::move(Action));
811 }
812
fileStats() const813 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const {
814 return WorkScheduler.fileStats();
815 }
816
createProcessingContext(PathRef File) const817 Context ClangdServer::createProcessingContext(PathRef File) const {
818 if (!ConfigProvider)
819 return Context::current().clone();
820
821 config::Params Params;
822 // Don't reread config files excessively often.
823 // FIXME: when we see a config file change event, use the event timestamp.
824 Params.FreshTime = std::chrono::steady_clock::now() - std::chrono::seconds(5);
825 llvm::SmallString<256> PosixPath;
826 if (!File.empty()) {
827 assert(llvm::sys::path::is_absolute(File));
828 llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix);
829 Params.Path = PosixPath.str();
830 }
831
832 llvm::StringMap<std::vector<Diag>> ReportableDiagnostics;
833 auto ConfigDiagnosticHandler = [&](const llvm::SMDiagnostic &D) {
834 // Ensure we create the map entry even for note diagnostics we don't report.
835 // This means that when the file is parsed with no warnings, we'll
836 // publish an empty set of diagnostics, clearing any the client has.
837 auto *Reportable = D.getFilename().empty()
838 ? nullptr
839 : &ReportableDiagnostics[D.getFilename()];
840 switch (D.getKind()) {
841 case llvm::SourceMgr::DK_Error:
842 elog("config error at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(),
843 D.getColumnNo(), D.getMessage());
844 if (Reportable)
845 Reportable->push_back(toDiag(D, Diag::ClangdConfig));
846 break;
847 case llvm::SourceMgr::DK_Warning:
848 log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(),
849 D.getColumnNo(), D.getMessage());
850 if (Reportable)
851 Reportable->push_back(toDiag(D, Diag::ClangdConfig));
852 break;
853 case llvm::SourceMgr::DK_Note:
854 case llvm::SourceMgr::DK_Remark:
855 vlog("config note at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(),
856 D.getColumnNo(), D.getMessage());
857 break;
858 }
859 };
860 Config C = ConfigProvider->getConfig(Params, ConfigDiagnosticHandler);
861 // Blindly publish diagnostics for the (unopened) parsed config files.
862 // We must avoid reporting diagnostics for *the same file* concurrently.
863 // Source file diags are published elsewhere, but those are different files.
864 if (!ReportableDiagnostics.empty()) {
865 std::lock_guard<std::mutex> Lock(ConfigDiagnosticsMu);
866 for (auto &Entry : ReportableDiagnostics)
867 ServerCallbacks->onDiagnosticsReady(Entry.first(), /*Version=*/"",
868 std::move(Entry.second));
869 }
870 return Context::current().derive(Config::Key, std::move(C));
871 }
872
873 LLVM_NODISCARD bool
blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds)874 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
875 return WorkScheduler.blockUntilIdle(timeoutSeconds(TimeoutSeconds)) &&
876 (!BackgroundIdx ||
877 BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds));
878 }
879
profile(MemoryTree & MT) const880 void ClangdServer::profile(MemoryTree &MT) const {
881 if (DynamicIdx)
882 DynamicIdx->profile(MT.child("dynamic_index"));
883 if (BackgroundIdx)
884 BackgroundIdx->profile(MT.child("background_index"));
885 WorkScheduler.profile(MT.child("tuscheduler"));
886 }
887 } // namespace clangd
888 } // namespace clang
889