1 //===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
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
7 //===-------------------------------------------------------------------===//
9 #include "ClangdServer.h"
10 #include "CodeComplete.h"
12 #include "Diagnostics.h"
14 #include "FindSymbols.h"
16 #include "HeaderSourceSwitch.h"
17 #include "InlayHints.h"
18 #include "ParsedAST.h"
21 #include "SemanticHighlighting.h"
22 #include "SemanticSelection.h"
23 #include "SourceCode.h"
24 #include "TUScheduler.h"
26 #include "clang-include-cleaner/Record.h"
27 #include "index/FileIndex.h"
28 #include "index/Merge.h"
29 #include "index/StdLib.h"
30 #include "refactor/Rename.h"
31 #include "refactor/Tweak.h"
32 #include "support/Cancellation.h"
33 #include "support/Context.h"
34 #include "support/Logger.h"
35 #include "support/MemoryTree.h"
36 #include "support/ThreadsafeFS.h"
37 #include "support/Trace.h"
38 #include "clang/Basic/Stack.h"
39 #include "clang/Format/Format.h"
40 #include "clang/Lex/Preprocessor.h"
41 #include "clang/Tooling/CompilationDatabase.h"
42 #include "clang/Tooling/Core/Replacement.h"
43 #include "llvm/ADT/ArrayRef.h"
44 #include "llvm/ADT/STLExtras.h"
45 #include "llvm/ADT/StringRef.h"
46 #include "llvm/Support/Error.h"
47 #include "llvm/Support/Path.h"
48 #include "llvm/Support/raw_ostream.h"
56 #include <type_traits>
64 // Tracks number of times a tweak has been offered.
65 static constexpr trace::Metric
TweakAvailable(
66 "tweak_available", trace::Metric::Counter
, "tweak_id");
68 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
69 struct UpdateIndexCallbacks
: public ParsingCallbacks
{
70 UpdateIndexCallbacks(FileIndex
*FIndex
,
71 ClangdServer::Callbacks
*ServerCallbacks
,
72 const ThreadsafeFS
&TFS
, AsyncTaskRunner
*Tasks
,
73 bool CollectInactiveRegions
)
74 : FIndex(FIndex
), ServerCallbacks(ServerCallbacks
), TFS(TFS
),
75 Stdlib
{std::make_shared
<StdLibSet
>()}, Tasks(Tasks
),
76 CollectInactiveRegions(CollectInactiveRegions
) {}
79 PathRef Path
, llvm::StringRef Version
, CapturedASTCtx ASTCtx
,
80 std::shared_ptr
<const include_cleaner::PragmaIncludes
> PI
) override
{
85 auto &PP
= ASTCtx
.getPreprocessor();
86 auto &CI
= ASTCtx
.getCompilerInvocation();
87 if (auto Loc
= Stdlib
->add(CI
.getLangOpts(), PP
.getHeaderSearchInfo()))
88 indexStdlib(CI
, std::move(*Loc
));
90 // FIndex outlives the UpdateIndexCallbacks.
91 auto Task
= [FIndex(FIndex
), Path(Path
.str()), Version(Version
.str()),
92 ASTCtx(std::move(ASTCtx
)), PI(std::move(PI
))]() mutable {
93 trace::Span
Tracer("PreambleIndexing");
94 FIndex
->updatePreamble(Path
, Version
, ASTCtx
.getASTContext(),
95 ASTCtx
.getPreprocessor(), *PI
);
99 Tasks
->runAsync("Preamble indexing for:" + Path
+ Version
,
105 void indexStdlib(const CompilerInvocation
&CI
, StdLibLocation Loc
) {
106 // This task is owned by Tasks, which outlives the TUScheduler and
107 // therefore the UpdateIndexCallbacks.
108 // We must be careful that the references we capture outlive TUScheduler.
109 auto Task
= [LO(CI
.getLangOpts()), Loc(std::move(Loc
)),
110 CI(std::make_unique
<CompilerInvocation
>(CI
)),
111 // External values that outlive ClangdServer
113 // Index outlives TUScheduler (declared first)
115 // shared_ptr extends lifetime
117 // We have some FS implementations that rely on information in
119 Ctx(Context::current().clone())]() mutable {
120 // Make sure we install the context into current thread.
121 WithContext
C(std::move(Ctx
));
122 clang::noteBottomOfStack();
124 IF
.Symbols
= indexStandardLibrary(std::move(CI
), Loc
, *TFS
);
125 if (Stdlib
->isBest(LO
))
126 FIndex
->updatePreamble(std::move(IF
));
129 // This doesn't have a semaphore to enforce -j, but it's rare.
130 Tasks
->runAsync("IndexStdlib", std::move(Task
));
135 void onMainAST(PathRef Path
, ParsedAST
&AST
, PublishFn Publish
) override
{
137 FIndex
->updateMain(Path
, AST
);
141 ServerCallbacks
->onDiagnosticsReady(Path
, AST
.version(),
142 AST
.getDiagnostics());
143 if (CollectInactiveRegions
) {
144 ServerCallbacks
->onInactiveRegionsReady(Path
,
145 getInactiveRegions(AST
));
150 void onFailedAST(PathRef Path
, llvm::StringRef Version
,
151 std::vector
<Diag
> Diags
, PublishFn Publish
) override
{
154 [&]() { ServerCallbacks
->onDiagnosticsReady(Path
, Version
, Diags
); });
157 void onFileUpdated(PathRef File
, const TUStatus
&Status
) override
{
159 ServerCallbacks
->onFileUpdated(File
, Status
);
162 void onPreamblePublished(PathRef File
) override
{
164 ServerCallbacks
->onSemanticsMaybeChanged(File
);
169 ClangdServer::Callbacks
*ServerCallbacks
;
170 const ThreadsafeFS
&TFS
;
171 std::shared_ptr
<StdLibSet
> Stdlib
;
172 AsyncTaskRunner
*Tasks
;
173 bool CollectInactiveRegions
;
176 class DraftStoreFS
: public ThreadsafeFS
{
178 DraftStoreFS(const ThreadsafeFS
&Base
, const DraftStore
&Drafts
)
179 : Base(Base
), DirtyFiles(Drafts
) {}
182 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> viewImpl() const override
{
183 auto OFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(
184 Base
.view(std::nullopt
));
185 OFS
->pushOverlay(DirtyFiles
.asVFS());
189 const ThreadsafeFS
&Base
;
190 const DraftStore
&DirtyFiles
;
195 ClangdServer::Options
ClangdServer::optsForTest() {
196 ClangdServer::Options Opts
;
197 Opts
.UpdateDebounce
= DebouncePolicy::fixed(/*zero*/ {});
198 Opts
.StorePreamblesInMemory
= true;
199 Opts
.AsyncThreadsCount
= 4; // Consistent!
203 ClangdServer::Options::operator TUScheduler::Options() const {
204 TUScheduler::Options Opts
;
205 Opts
.AsyncThreadsCount
= AsyncThreadsCount
;
206 Opts
.RetentionPolicy
= RetentionPolicy
;
207 Opts
.StorePreamblesInMemory
= StorePreamblesInMemory
;
208 Opts
.UpdateDebounce
= UpdateDebounce
;
209 Opts
.ContextProvider
= ContextProvider
;
210 Opts
.PreambleThrottler
= PreambleThrottler
;
214 ClangdServer::ClangdServer(const GlobalCompilationDatabase
&CDB
,
215 const ThreadsafeFS
&TFS
, const Options
&Opts
,
216 Callbacks
*Callbacks
)
217 : FeatureModules(Opts
.FeatureModules
), CDB(CDB
), TFS(TFS
),
218 DynamicIdx(Opts
.BuildDynamicSymbolIndex
219 ? new FileIndex(Opts
.EnableOutgoingCalls
)
221 ModulesManager(Opts
.ModulesManager
),
222 ClangTidyProvider(Opts
.ClangTidyProvider
),
223 UseDirtyHeaders(Opts
.UseDirtyHeaders
),
224 LineFoldingOnly(Opts
.LineFoldingOnly
),
225 PreambleParseForwardingFunctions(Opts
.PreambleParseForwardingFunctions
),
226 ImportInsertions(Opts
.ImportInsertions
),
227 PublishInactiveRegions(Opts
.PublishInactiveRegions
),
228 WorkspaceRoot(Opts
.WorkspaceRoot
),
229 Transient(Opts
.ImplicitCancellation
? TUScheduler::InvalidateOnUpdate
230 : TUScheduler::NoInvalidation
),
231 DirtyFS(std::make_unique
<DraftStoreFS
>(TFS
, DraftMgr
)) {
232 if (Opts
.AsyncThreadsCount
!= 0)
233 IndexTasks
.emplace();
234 // Pass a callback into `WorkScheduler` to extract symbols from a newly
235 // parsed file and rebuild the file index synchronously each time an AST
237 WorkScheduler
.emplace(CDB
, TUScheduler::Options(Opts
),
238 std::make_unique
<UpdateIndexCallbacks
>(
239 DynamicIdx
.get(), Callbacks
, TFS
,
240 IndexTasks
? &*IndexTasks
: nullptr,
241 PublishInactiveRegions
));
242 // Adds an index to the stack, at higher priority than existing indexes.
243 auto AddIndex
= [&](SymbolIndex
*Idx
) {
244 if (this->Index
!= nullptr) {
245 MergedIdx
.push_back(std::make_unique
<MergedIndex
>(Idx
, this->Index
));
246 this->Index
= MergedIdx
.back().get();
251 if (Opts
.StaticIndex
)
252 AddIndex(Opts
.StaticIndex
);
253 if (Opts
.BackgroundIndex
) {
254 BackgroundIndex::Options BGOpts
;
255 BGOpts
.ThreadPoolSize
= std::max(Opts
.AsyncThreadsCount
, 1u);
256 BGOpts
.OnProgress
= [Callbacks
](BackgroundQueue::Stats S
) {
258 Callbacks
->onBackgroundIndexProgress(S
);
260 BGOpts
.ContextProvider
= Opts
.ContextProvider
;
261 BGOpts
.SupportContainedRefs
= Opts
.EnableOutgoingCalls
;
262 BackgroundIdx
= std::make_unique
<BackgroundIndex
>(
264 BackgroundIndexStorage::createDiskBackedStorageFactory(
265 [&CDB
](llvm::StringRef File
) { return CDB
.getProjectInfo(File
); }),
267 AddIndex(BackgroundIdx
.get());
270 AddIndex(DynamicIdx
.get());
272 if (Opts
.FeatureModules
) {
273 FeatureModule::Facilities F
{
274 *this->WorkScheduler
,
278 for (auto &Mod
: *Opts
.FeatureModules
)
283 ClangdServer::~ClangdServer() {
284 // Destroying TUScheduler first shuts down request threads that might
285 // otherwise access members concurrently.
286 // (Nobody can be using TUScheduler because we're on the main thread).
287 WorkScheduler
.reset();
288 // Now requests have stopped, we can shut down feature modules.
289 if (FeatureModules
) {
290 for (auto &Mod
: *FeatureModules
)
292 for (auto &Mod
: *FeatureModules
)
293 Mod
.blockUntilIdle(Deadline::infinity());
297 void ClangdServer::addDocument(PathRef File
, llvm::StringRef Contents
,
298 llvm::StringRef Version
,
299 WantDiagnostics WantDiags
, bool ForceRebuild
) {
300 std::string ActualVersion
= DraftMgr
.addDraft(File
, Version
, Contents
);
302 Opts
.PreambleParseForwardingFunctions
= PreambleParseForwardingFunctions
;
303 Opts
.ImportInsertions
= ImportInsertions
;
305 // Compile command is set asynchronously during update, as it can be slow.
307 Inputs
.TFS
= &getHeaderFS();
308 Inputs
.Contents
= std::string(Contents
);
309 Inputs
.Version
= std::move(ActualVersion
);
310 Inputs
.ForceRebuild
= ForceRebuild
;
311 Inputs
.Opts
= std::move(Opts
);
312 Inputs
.Index
= Index
;
313 Inputs
.ClangTidyProvider
= ClangTidyProvider
;
314 Inputs
.FeatureModules
= FeatureModules
;
315 Inputs
.ModulesManager
= ModulesManager
;
316 bool NewFile
= WorkScheduler
->update(File
, Inputs
, WantDiags
);
317 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
318 if (NewFile
&& BackgroundIdx
)
319 BackgroundIdx
->boostRelated(File
);
322 void ClangdServer::reparseOpenFilesIfNeeded(
323 llvm::function_ref
<bool(llvm::StringRef File
)> Filter
) {
324 // Reparse only opened files that were modified.
325 for (const Path
&FilePath
: DraftMgr
.getActiveFiles())
326 if (Filter(FilePath
))
327 if (auto Draft
= DraftMgr
.getDraft(FilePath
)) // else disappeared in race?
328 addDocument(FilePath
, *Draft
->Contents
, Draft
->Version
,
329 WantDiagnostics::Auto
);
332 std::shared_ptr
<const std::string
> ClangdServer::getDraft(PathRef File
) const {
333 auto Draft
= DraftMgr
.getDraft(File
);
336 return std::move(Draft
->Contents
);
339 std::function
<Context(PathRef
)>
340 ClangdServer::createConfiguredContextProvider(const config::Provider
*Provider
,
341 Callbacks
*Publish
) {
343 return [](llvm::StringRef
) { return Context::current().clone(); };
346 const config::Provider
*Provider
;
347 ClangdServer::Callbacks
*Publish
;
348 std::mutex PublishMu
;
350 Impl(const config::Provider
*Provider
, ClangdServer::Callbacks
*Publish
)
351 : Provider(Provider
), Publish(Publish
) {}
353 Context
operator()(llvm::StringRef File
) {
354 config::Params Params
;
355 // Don't reread config files excessively often.
356 // FIXME: when we see a config file change event, use the event timestamp?
358 std::chrono::steady_clock::now() - std::chrono::seconds(5);
359 llvm::SmallString
<256> PosixPath
;
361 assert(llvm::sys::path::is_absolute(File
));
362 llvm::sys::path::native(File
, PosixPath
, llvm::sys::path::Style::posix
);
363 Params
.Path
= PosixPath
.str();
366 llvm::StringMap
<std::vector
<Diag
>> ReportableDiagnostics
;
367 Config C
= Provider
->getConfig(Params
, [&](const llvm::SMDiagnostic
&D
) {
368 // Create the map entry even for note diagnostics we don't report.
369 // This means that when the file is parsed with no warnings, we
370 // publish an empty set of diagnostics, clearing any the client has.
371 handleDiagnostic(D
, !Publish
|| D
.getFilename().empty()
373 : &ReportableDiagnostics
[D
.getFilename()]);
375 // Blindly publish diagnostics for the (unopened) parsed config files.
376 // We must avoid reporting diagnostics for *the same file* concurrently.
377 // Source diags are published elsewhere, but those are different files.
378 if (!ReportableDiagnostics
.empty()) {
379 std::lock_guard
<std::mutex
> Lock(PublishMu
);
380 for (auto &Entry
: ReportableDiagnostics
)
381 Publish
->onDiagnosticsReady(Entry
.first(), /*Version=*/"",
384 return Context::current().derive(Config::Key
, std::move(C
));
387 void handleDiagnostic(const llvm::SMDiagnostic
&D
,
388 std::vector
<Diag
> *ClientDiagnostics
) {
389 switch (D
.getKind()) {
390 case llvm::SourceMgr::DK_Error
:
391 elog("config error at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
392 D
.getColumnNo(), D
.getMessage());
394 case llvm::SourceMgr::DK_Warning
:
395 log("config warning at {0}:{1}:{2}: {3}", D
.getFilename(),
396 D
.getLineNo(), D
.getColumnNo(), D
.getMessage());
398 case llvm::SourceMgr::DK_Note
:
399 case llvm::SourceMgr::DK_Remark
:
400 vlog("config note at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
401 D
.getColumnNo(), D
.getMessage());
402 ClientDiagnostics
= nullptr; // Don't emit notes as LSP diagnostics.
405 if (ClientDiagnostics
)
406 ClientDiagnostics
->push_back(toDiag(D
, Diag::ClangdConfig
));
411 return [I(std::make_shared
<Impl
>(Provider
, Publish
))](llvm::StringRef Path
) {
416 void ClangdServer::removeDocument(PathRef File
) {
417 DraftMgr
.removeDraft(File
);
418 WorkScheduler
->remove(File
);
421 void ClangdServer::codeComplete(PathRef File
, Position Pos
,
422 const clangd::CodeCompleteOptions
&Opts
,
423 Callback
<CodeCompleteResult
> CB
) {
424 // Copy completion options for passing them to async task handler.
425 auto CodeCompleteOpts
= Opts
;
426 if (!CodeCompleteOpts
.Index
) // Respect overridden index.
427 CodeCompleteOpts
.Index
= Index
;
429 auto Task
= [Pos
, CodeCompleteOpts
, File
= File
.str(), CB
= std::move(CB
),
430 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
432 return CB(IP
.takeError());
433 if (auto Reason
= isCancelled())
434 return CB(llvm::make_error
<CancelledError
>(Reason
));
436 std::optional
<SpeculativeFuzzyFind
> SpecFuzzyFind
;
438 // No speculation in Fallback mode, as it's supposed to be much faster
439 // without compiling.
440 vlog("Build for file {0} is not ready. Enter fallback mode.", File
);
441 } else if (CodeCompleteOpts
.Index
) {
442 SpecFuzzyFind
.emplace();
444 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
445 SpecFuzzyFind
->CachedReq
= CachedCompletionFuzzyFindRequestByFile
[File
];
448 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
449 // FIXME: Add traling new line if there is none at eof, workaround a crash,
450 // see https://github.com/clangd/clangd/issues/332
451 if (!IP
->Contents
.ends_with("\n"))
452 ParseInput
.Contents
.append("\n");
453 ParseInput
.Index
= Index
;
455 CodeCompleteOpts
.MainFileSignals
= IP
->Signals
;
456 CodeCompleteOpts
.AllScopes
= Config::current().Completion
.AllScopes
;
457 CodeCompleteOpts
.ArgumentLists
= Config::current().Completion
.ArgumentLists
;
458 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
459 // both the old and the new version in case only one of them matches.
460 CodeCompleteResult Result
= clangd::codeComplete(
461 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
462 SpecFuzzyFind
? &*SpecFuzzyFind
: nullptr);
464 clang::clangd::trace::Span
Tracer("Completion results callback");
465 CB(std::move(Result
));
467 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
468 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
469 CachedCompletionFuzzyFindRequestByFile
[File
] = *SpecFuzzyFind
->NewReq
;
471 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
472 // We don't want `codeComplete` to wait for the async call if it doesn't use
473 // the result (e.g. non-index completion, speculation fails), so that `CB`
474 // is called as soon as results are available.
477 // We use a potentially-stale preamble because latency is critical here.
478 WorkScheduler
->runWithPreamble(
479 "CodeComplete", File
,
480 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
482 : TUScheduler::StaleOrAbsent
,
486 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
487 MarkupKind DocumentationFormat
,
488 Callback
<SignatureHelp
> CB
) {
490 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
492 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
494 return CB(IP
.takeError());
496 const auto *PreambleData
= IP
->Preamble
;
498 return CB(error("Failed to parse includes"));
500 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
501 // FIXME: Add traling new line if there is none at eof, workaround a crash,
502 // see https://github.com/clangd/clangd/issues/332
503 if (!IP
->Contents
.ends_with("\n"))
504 ParseInput
.Contents
.append("\n");
505 ParseInput
.Index
= Index
;
506 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
507 DocumentationFormat
));
510 // Unlike code completion, we wait for a preamble here.
511 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
515 void ClangdServer::formatFile(PathRef File
, std::optional
<Range
> Rng
,
516 Callback
<tooling::Replacements
> CB
) {
517 auto Code
= getDraft(File
);
519 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
520 ErrorCode::InvalidParams
));
521 tooling::Range RequestedRange
;
523 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
525 return CB(Begin
.takeError());
526 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
528 return CB(End
.takeError());
529 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
531 RequestedRange
= tooling::Range(0, Code
->size());
534 // Call clang-format.
535 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
536 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
537 CB
= std::move(CB
), this]() mutable {
538 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
, true);
539 tooling::Replacements IncludeReplaces
=
540 format::sortIncludes(Style
, Code
, Ranges
, File
);
541 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
543 return CB(Changed
.takeError());
545 CB(IncludeReplaces
.merge(format::reformat(
547 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
550 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
553 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
554 StringRef TriggerText
,
555 Callback
<std::vector
<TextEdit
>> CB
) {
556 auto Code
= getDraft(File
);
558 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
559 ErrorCode::InvalidParams
));
560 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
562 return CB(CursorPos
.takeError());
563 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
564 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
565 CB
= std::move(CB
), this]() mutable {
566 auto Style
= getFormatStyleForFile(File
, Code
, TFS
, false);
567 std::vector
<TextEdit
> Result
;
568 for (const tooling::Replacement
&R
:
569 formatIncremental(Code
, CursorPos
, TriggerText
, Style
))
570 Result
.push_back(replacementToEdit(Code
, R
));
573 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
576 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
577 std::optional
<std::string
> NewName
,
578 const RenameOptions
&RenameOpts
,
579 Callback
<RenameResult
> CB
) {
580 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
581 NewName
= std::move(NewName
),
582 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
584 return CB(InpAST
.takeError());
585 // prepareRename is latency-sensitive: we don't query the index, as we
586 // only need main-file references
588 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
589 InpAST
->AST
, File
, /*FS=*/nullptr,
590 /*Index=*/nullptr, RenameOpts
});
592 // LSP says to return null on failure, but that will result in a generic
593 // failure message. If we send an LSP error response, clients can surface
594 // the message to users (VSCode does).
595 return CB(Results
.takeError());
599 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
602 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
603 const RenameOptions
&Opts
,
604 Callback
<RenameResult
> CB
) {
605 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
607 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
608 // Tracks number of files edited per invocation.
609 static constexpr trace::Metric
RenameFiles("rename_files",
610 trace::Metric::Distribution
);
612 return CB(InpAST
.takeError());
613 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
614 DirtyFS
->view(std::nullopt
), Index
, Opts
});
616 return CB(R
.takeError());
618 if (Opts
.WantFormat
) {
619 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
620 *InpAST
->Inputs
.TFS
, false);
621 llvm::Error Err
= llvm::Error::success();
622 for (auto &E
: R
->GlobalChanges
)
624 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
627 return CB(std::move(Err
));
629 RenameFiles
.record(R
->GlobalChanges
.size());
632 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
636 // May generate several candidate selections, due to SelectionTree ambiguity.
637 // vector of pointers because GCC doesn't like non-copyable Selection.
638 llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
639 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
640 llvm::vfs::FileSystem
*FS
) {
641 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
643 return Begin
.takeError();
644 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
646 return End
.takeError();
647 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
648 SelectionTree::createEach(
649 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
650 [&](SelectionTree T
) {
651 Result
.push_back(std::make_unique
<Tweak::Selection
>(
652 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
655 assert(!Result
.empty() && "Expected at least one SelectionTree");
656 return std::move(Result
);
659 // Some fixes may perform local renaming, we want to convert those to clangd
660 // rename commands, such that we can leverage the index for more accurate
662 std::optional
<ClangdServer::CodeActionResult::Rename
>
663 tryConvertToRename(const Diag
*Diag
, const Fix
&Fix
) {
664 bool IsClangTidyRename
= Diag
->Source
== Diag::ClangTidy
&&
665 Diag
->Name
== "readability-identifier-naming" &&
667 if (IsClangTidyRename
&& Diag
->InsideMainFile
) {
668 ClangdServer::CodeActionResult::Rename R
;
669 R
.NewName
= Fix
.Edits
.front().newText
;
670 R
.FixMessage
= Fix
.Message
;
671 R
.Diag
= {Diag
->Range
, Diag
->Message
};
680 void ClangdServer::codeAction(const CodeActionInputs
&Params
,
681 Callback
<CodeActionResult
> CB
) {
682 auto Action
= [Params
, CB
= std::move(CB
),
683 FeatureModules(this->FeatureModules
)](
684 Expected
<InputsAndAST
> InpAST
) mutable {
686 return CB(InpAST
.takeError());
688 [Only(Params
.RequestedActionKinds
)](llvm::StringRef Kind
) {
691 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
692 return Kind
.consume_front(Base
) &&
693 (Kind
.empty() || Kind
.starts_with("."));
697 CodeActionResult Result
;
698 Result
.Version
= InpAST
->AST
.version().str();
699 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
700 auto FindMatchedDiag
= [&InpAST
](const DiagRef
&DR
) -> const Diag
* {
701 for (const auto &Diag
: InpAST
->AST
.getDiagnostics())
702 if (Diag
.Range
== DR
.Range
&& Diag
.Message
== DR
.Message
)
706 for (const auto &DiagRef
: Params
.Diagnostics
) {
707 if (const auto *Diag
= FindMatchedDiag(DiagRef
))
708 for (const auto &Fix
: Diag
->Fixes
) {
709 if (auto Rename
= tryConvertToRename(Diag
, Fix
)) {
710 Result
.Renames
.emplace_back(std::move(*Rename
));
712 Result
.QuickFixes
.push_back({DiagRef
, Fix
});
719 auto Selections
= tweakSelection(Params
.Selection
, *InpAST
, /*FS=*/nullptr);
721 return CB(Selections
.takeError());
722 // Don't allow a tweak to fire more than once across ambiguous selections.
723 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
724 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
725 return KindAllowed(T
.kind()) && Params
.TweakFilter(T
) &&
726 !PreparedTweaks
.count(T
.id());
728 for (const auto &Sel
: *Selections
) {
729 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
730 Result
.TweakRefs
.push_back(TweakRef
{T
->id(), T
->title(), T
->kind()});
731 PreparedTweaks
.insert(T
->id());
732 TweakAvailable
.record(1, T
->id());
735 CB(std::move(Result
));
738 WorkScheduler
->runWithAST("codeAction", Params
.File
, std::move(Action
),
742 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
743 Callback
<Tweak::Effect
> CB
) {
744 // Tracks number of times a tweak has been attempted.
745 static constexpr trace::Metric
TweakAttempt(
746 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
747 // Tracks number of times a tweak has failed to produce edits.
748 static constexpr trace::Metric
TweakFailed(
749 "tweak_failed", trace::Metric::Counter
, "tweak_id");
750 TweakAttempt
.record(1, TweakID
);
751 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
753 this](Expected
<InputsAndAST
> InpAST
) mutable {
755 return CB(InpAST
.takeError());
756 auto FS
= DirtyFS
->view(std::nullopt
);
757 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
759 return CB(Selections
.takeError());
760 std::optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
761 // Try each selection, take the first one that prepare()s.
762 // If they all fail, Effect will hold get the last error.
763 for (const auto &Selection
: *Selections
) {
764 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
766 Effect
= (*T
)->apply(*Selection
);
769 Effect
= T
.takeError();
771 assert(Effect
&& "Expected at least one selection");
772 if (*Effect
&& (*Effect
)->FormatEdits
) {
773 // Format tweaks that require it centrally here.
774 for (auto &It
: (*Effect
)->ApplyEdits
) {
776 format::FormatStyle Style
=
777 getFormatStyleForFile(File
, E
.InitialCode
, TFS
, false);
778 if (llvm::Error Err
= reformatEdit(E
, Style
))
779 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
782 TweakFailed
.record(1, TweakID
);
784 return CB(std::move(*Effect
));
786 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
789 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
790 Callback
<std::vector
<LocatedSymbol
>> CB
) {
791 auto Action
= [Pos
, CB
= std::move(CB
),
792 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
794 return CB(InpAST
.takeError());
795 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
798 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
801 void ClangdServer::switchSourceHeader(
802 PathRef Path
, Callback
<std::optional
<clangd::Path
>> CB
) {
803 // We want to return the result as fast as possible, strategy is:
804 // 1) use the file-only heuristic, it requires some IO but it is much
805 // faster than building AST, but it only works when .h/.cc files are in
806 // the same directory.
807 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
808 // different code layout.
809 if (auto CorrespondingFile
=
810 getCorrespondingHeaderOrSource(Path
, TFS
.view(std::nullopt
)))
811 return CB(std::move(CorrespondingFile
));
812 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
813 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
815 return CB(InpAST
.takeError());
816 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
818 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
821 void ClangdServer::findDocumentHighlights(
822 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
824 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
826 return CB(InpAST
.takeError());
827 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
830 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
833 void ClangdServer::findHover(PathRef File
, Position Pos
,
834 Callback
<std::optional
<HoverInfo
>> CB
) {
835 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
836 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
838 return CB(InpAST
.takeError());
839 format::FormatStyle Style
= getFormatStyleForFile(
840 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
, false);
841 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
844 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
847 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
848 TypeHierarchyDirection Direction
,
849 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
850 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
851 this](Expected
<InputsAndAST
> InpAST
) mutable {
853 return CB(InpAST
.takeError());
854 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
858 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
861 void ClangdServer::superTypes(
862 const TypeHierarchyItem
&Item
,
863 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> CB
) {
864 WorkScheduler
->run("typeHierarchy/superTypes", /*Path=*/"",
865 [=, CB
= std::move(CB
)]() mutable {
866 CB(clangd::superTypes(Item
, Index
));
870 void ClangdServer::subTypes(const TypeHierarchyItem
&Item
,
871 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
873 "typeHierarchy/subTypes", /*Path=*/"",
874 [=, CB
= std::move(CB
)]() mutable { CB(clangd::subTypes(Item
, Index
)); });
877 void ClangdServer::resolveTypeHierarchy(
878 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
879 Callback
<std::optional
<TypeHierarchyItem
>> CB
) {
881 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
882 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
887 void ClangdServer::prepareCallHierarchy(
888 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
889 auto Action
= [File
= File
.str(), Pos
,
890 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
892 return CB(InpAST
.takeError());
893 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
895 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
898 void ClangdServer::incomingCalls(
899 const CallHierarchyItem
&Item
,
900 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
901 WorkScheduler
->run("Incoming Calls", "",
902 [CB
= std::move(CB
), Item
, this]() mutable {
903 CB(clangd::incomingCalls(Item
, Index
));
907 void ClangdServer::inlayHints(PathRef File
, std::optional
<Range
> RestrictRange
,
908 Callback
<std::vector
<InlayHint
>> CB
) {
909 auto Action
= [RestrictRange(std::move(RestrictRange
)),
910 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
912 return CB(InpAST
.takeError());
913 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
915 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
918 void ClangdServer::outgoingCalls(
919 const CallHierarchyItem
&Item
,
920 Callback
<std::vector
<CallHierarchyOutgoingCall
>> CB
) {
921 WorkScheduler
->run("Outgoing Calls", "",
922 [CB
= std::move(CB
), Item
, this]() mutable {
923 CB(clangd::outgoingCalls(Item
, Index
));
927 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
928 // FIXME: Do nothing for now. This will be used for indexing and potentially
929 // invalidating other caches.
932 void ClangdServer::workspaceSymbols(
933 llvm::StringRef Query
, int Limit
,
934 Callback
<std::vector
<SymbolInformation
>> CB
) {
936 "getWorkspaceSymbols", /*Path=*/"",
937 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
938 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
939 WorkspaceRoot
.value_or("")));
943 void ClangdServer::documentSymbols(llvm::StringRef File
,
944 Callback
<std::vector
<DocumentSymbol
>> CB
) {
946 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
948 return CB(InpAST
.takeError());
949 CB(clangd::getDocumentSymbols(InpAST
->AST
));
951 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
955 void ClangdServer::foldingRanges(llvm::StringRef File
,
956 Callback
<std::vector
<FoldingRange
>> CB
) {
957 auto Code
= getDraft(File
);
959 return CB(llvm::make_error
<LSPError
>(
960 "trying to compute folding ranges for non-added document",
961 ErrorCode::InvalidParams
));
962 auto Action
= [LineFoldingOnly
= LineFoldingOnly
, CB
= std::move(CB
),
963 Code
= std::move(*Code
)]() mutable {
964 CB(clangd::getFoldingRanges(Code
, LineFoldingOnly
));
966 // We want to make sure folding ranges are always available for all the open
967 // files, hence prefer runQuick to not wait for operations on other files.
968 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
971 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
972 Callback
<std::vector
<LocatedSymbol
>> CB
) {
973 auto Action
= [Pos
, CB
= std::move(CB
),
974 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
976 return CB(InpAST
.takeError());
977 CB(clangd::findType(InpAST
->AST
, Pos
, Index
));
979 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
982 void ClangdServer::findImplementations(
983 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
984 auto Action
= [Pos
, CB
= std::move(CB
),
985 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
987 return CB(InpAST
.takeError());
988 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
991 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
994 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
996 Callback
<ReferencesResult
> CB
) {
997 auto Action
= [Pos
, Limit
, AddContainer
, CB
= std::move(CB
),
998 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1000 return CB(InpAST
.takeError());
1001 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
, AddContainer
));
1004 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
1007 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
1008 Callback
<std::vector
<SymbolDetails
>> CB
) {
1010 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1012 return CB(InpAST
.takeError());
1013 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
1016 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
1019 void ClangdServer::semanticRanges(PathRef File
,
1020 const std::vector
<Position
> &Positions
,
1021 Callback
<std::vector
<SelectionRange
>> CB
) {
1022 auto Action
= [Positions
, CB
= std::move(CB
)](
1023 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1025 return CB(InpAST
.takeError());
1026 std::vector
<SelectionRange
> Result
;
1027 for (const auto &Pos
: Positions
) {
1028 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
1029 Result
.push_back(std::move(*Range
));
1031 return CB(Range
.takeError());
1033 CB(std::move(Result
));
1035 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
1038 void ClangdServer::documentLinks(PathRef File
,
1039 Callback
<std::vector
<DocumentLink
>> CB
) {
1041 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1043 return CB(InpAST
.takeError());
1044 CB(clangd::getDocumentLinks(InpAST
->AST
));
1046 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
1050 void ClangdServer::semanticHighlights(
1051 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
1053 auto Action
= [CB
= std::move(CB
),
1054 PublishInactiveRegions
= PublishInactiveRegions
](
1055 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1057 return CB(InpAST
.takeError());
1058 // Include inactive regions in semantic highlighting tokens only if the
1059 // client doesn't support a dedicated protocol for being informed about
1061 CB(clangd::getSemanticHighlightings(InpAST
->AST
, !PublishInactiveRegions
));
1063 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
1067 void ClangdServer::getAST(PathRef File
, std::optional
<Range
> R
,
1068 Callback
<std::optional
<ASTNode
>> CB
) {
1070 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
1072 return CB(Inputs
.takeError());
1074 // It's safe to pass in the TU, as dumpAST() does not
1075 // deserialize the preamble.
1076 auto Node
= DynTypedNode::create(
1077 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
1078 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
1079 Inputs
->AST
.getASTContext()));
1081 unsigned Start
, End
;
1082 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
1085 return CB(Offset
.takeError());
1086 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
1089 return CB(Offset
.takeError());
1090 bool Success
= SelectionTree::createEach(
1091 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
1092 [&](SelectionTree T
) {
1093 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
1094 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
1095 Inputs
->AST
.getASTContext()));
1103 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
1106 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
1107 Callback
<InputsAndAST
> Action
) {
1108 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
1111 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
1113 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1115 return CB(InpAST
.takeError());
1116 return CB(InpAST
->AST
.getDiagnostics());
1119 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
1122 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1123 return WorkScheduler
->fileStats();
1127 ClangdServer::blockUntilIdleForTest(std::optional
<double> TimeoutSeconds
) {
1128 // Order is important here: we don't want to block on A and then B,
1129 // if B might schedule work on A.
1131 #if defined(__has_feature) && \
1132 (__has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) || \
1133 __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer))
1134 if (TimeoutSeconds
.has_value())
1135 (*TimeoutSeconds
) *= 10;
1138 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1139 // and we're blocking the main thread.
1140 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1142 // TUScheduler is the only thing that starts background indexing work.
1143 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1146 // Unfortunately we don't have strict topological order between the rest of
1147 // the components. E.g. CDB broadcast triggers backrgound indexing.
1148 // This queries the CDB which may discover new work if disk has changed.
1150 // So try each one a few times in a loop.
1151 // If there are no tricky interactions then all after the first are no-ops.
1152 // Then on the last iteration, verify they're idle without waiting.
1154 // There's a small chance they're juggling work and we didn't catch them :-(
1155 for (std::optional
<double> Timeout
:
1156 {TimeoutSeconds
, TimeoutSeconds
, std::optional
<double>(0)}) {
1157 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1159 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1161 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1162 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1167 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1168 "Something scheduled work while we're blocking the main thread!");
1172 void ClangdServer::profile(MemoryTree
&MT
) const {
1174 DynamicIdx
->profile(MT
.child("dynamic_index"));
1176 BackgroundIdx
->profile(MT
.child("background_index"));
1177 WorkScheduler
->profile(MT
.child("tuscheduler"));
1179 } // namespace clangd
1180 } // namespace clang