• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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