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