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