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
? new FileIndex() : nullptr),
219 ModulesManager(Opts
.ModulesManager
),
220 ClangTidyProvider(Opts
.ClangTidyProvider
),
221 UseDirtyHeaders(Opts
.UseDirtyHeaders
),
222 LineFoldingOnly(Opts
.LineFoldingOnly
),
223 PreambleParseForwardingFunctions(Opts
.PreambleParseForwardingFunctions
),
224 ImportInsertions(Opts
.ImportInsertions
),
225 PublishInactiveRegions(Opts
.PublishInactiveRegions
),
226 WorkspaceRoot(Opts
.WorkspaceRoot
),
227 Transient(Opts
.ImplicitCancellation
? TUScheduler::InvalidateOnUpdate
228 : TUScheduler::NoInvalidation
),
229 DirtyFS(std::make_unique
<DraftStoreFS
>(TFS
, DraftMgr
)) {
230 if (Opts
.AsyncThreadsCount
!= 0)
231 IndexTasks
.emplace();
232 // Pass a callback into `WorkScheduler` to extract symbols from a newly
233 // parsed file and rebuild the file index synchronously each time an AST
235 WorkScheduler
.emplace(CDB
, TUScheduler::Options(Opts
),
236 std::make_unique
<UpdateIndexCallbacks
>(
237 DynamicIdx
.get(), Callbacks
, TFS
,
238 IndexTasks
? &*IndexTasks
: nullptr,
239 PublishInactiveRegions
));
240 // Adds an index to the stack, at higher priority than existing indexes.
241 auto AddIndex
= [&](SymbolIndex
*Idx
) {
242 if (this->Index
!= nullptr) {
243 MergedIdx
.push_back(std::make_unique
<MergedIndex
>(Idx
, this->Index
));
244 this->Index
= MergedIdx
.back().get();
249 if (Opts
.StaticIndex
)
250 AddIndex(Opts
.StaticIndex
);
251 if (Opts
.BackgroundIndex
) {
252 BackgroundIndex::Options BGOpts
;
253 BGOpts
.ThreadPoolSize
= std::max(Opts
.AsyncThreadsCount
, 1u);
254 BGOpts
.OnProgress
= [Callbacks
](BackgroundQueue::Stats S
) {
256 Callbacks
->onBackgroundIndexProgress(S
);
258 BGOpts
.ContextProvider
= Opts
.ContextProvider
;
259 BackgroundIdx
= std::make_unique
<BackgroundIndex
>(
261 BackgroundIndexStorage::createDiskBackedStorageFactory(
262 [&CDB
](llvm::StringRef File
) { return CDB
.getProjectInfo(File
); }),
264 AddIndex(BackgroundIdx
.get());
267 AddIndex(DynamicIdx
.get());
269 if (Opts
.FeatureModules
) {
270 FeatureModule::Facilities F
{
271 *this->WorkScheduler
,
275 for (auto &Mod
: *Opts
.FeatureModules
)
280 ClangdServer::~ClangdServer() {
281 // Destroying TUScheduler first shuts down request threads that might
282 // otherwise access members concurrently.
283 // (Nobody can be using TUScheduler because we're on the main thread).
284 WorkScheduler
.reset();
285 // Now requests have stopped, we can shut down feature modules.
286 if (FeatureModules
) {
287 for (auto &Mod
: *FeatureModules
)
289 for (auto &Mod
: *FeatureModules
)
290 Mod
.blockUntilIdle(Deadline::infinity());
294 void ClangdServer::addDocument(PathRef File
, llvm::StringRef Contents
,
295 llvm::StringRef Version
,
296 WantDiagnostics WantDiags
, bool ForceRebuild
) {
297 std::string ActualVersion
= DraftMgr
.addDraft(File
, Version
, Contents
);
299 Opts
.PreambleParseForwardingFunctions
= PreambleParseForwardingFunctions
;
300 Opts
.ImportInsertions
= ImportInsertions
;
302 // Compile command is set asynchronously during update, as it can be slow.
304 Inputs
.TFS
= &getHeaderFS();
305 Inputs
.Contents
= std::string(Contents
);
306 Inputs
.Version
= std::move(ActualVersion
);
307 Inputs
.ForceRebuild
= ForceRebuild
;
308 Inputs
.Opts
= std::move(Opts
);
309 Inputs
.Index
= Index
;
310 Inputs
.ClangTidyProvider
= ClangTidyProvider
;
311 Inputs
.FeatureModules
= FeatureModules
;
312 Inputs
.ModulesManager
= ModulesManager
;
313 bool NewFile
= WorkScheduler
->update(File
, Inputs
, WantDiags
);
314 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
315 if (NewFile
&& BackgroundIdx
)
316 BackgroundIdx
->boostRelated(File
);
319 void ClangdServer::reparseOpenFilesIfNeeded(
320 llvm::function_ref
<bool(llvm::StringRef File
)> Filter
) {
321 // Reparse only opened files that were modified.
322 for (const Path
&FilePath
: DraftMgr
.getActiveFiles())
323 if (Filter(FilePath
))
324 if (auto Draft
= DraftMgr
.getDraft(FilePath
)) // else disappeared in race?
325 addDocument(FilePath
, *Draft
->Contents
, Draft
->Version
,
326 WantDiagnostics::Auto
);
329 std::shared_ptr
<const std::string
> ClangdServer::getDraft(PathRef File
) const {
330 auto Draft
= DraftMgr
.getDraft(File
);
333 return std::move(Draft
->Contents
);
336 std::function
<Context(PathRef
)>
337 ClangdServer::createConfiguredContextProvider(const config::Provider
*Provider
,
338 Callbacks
*Publish
) {
340 return [](llvm::StringRef
) { return Context::current().clone(); };
343 const config::Provider
*Provider
;
344 ClangdServer::Callbacks
*Publish
;
345 std::mutex PublishMu
;
347 Impl(const config::Provider
*Provider
, ClangdServer::Callbacks
*Publish
)
348 : Provider(Provider
), Publish(Publish
) {}
350 Context
operator()(llvm::StringRef File
) {
351 config::Params Params
;
352 // Don't reread config files excessively often.
353 // FIXME: when we see a config file change event, use the event timestamp?
355 std::chrono::steady_clock::now() - std::chrono::seconds(5);
356 llvm::SmallString
<256> PosixPath
;
358 assert(llvm::sys::path::is_absolute(File
));
359 llvm::sys::path::native(File
, PosixPath
, llvm::sys::path::Style::posix
);
360 Params
.Path
= PosixPath
.str();
363 llvm::StringMap
<std::vector
<Diag
>> ReportableDiagnostics
;
364 Config C
= Provider
->getConfig(Params
, [&](const llvm::SMDiagnostic
&D
) {
365 // Create the map entry even for note diagnostics we don't report.
366 // This means that when the file is parsed with no warnings, we
367 // publish an empty set of diagnostics, clearing any the client has.
368 handleDiagnostic(D
, !Publish
|| D
.getFilename().empty()
370 : &ReportableDiagnostics
[D
.getFilename()]);
372 // Blindly publish diagnostics for the (unopened) parsed config files.
373 // We must avoid reporting diagnostics for *the same file* concurrently.
374 // Source diags are published elsewhere, but those are different files.
375 if (!ReportableDiagnostics
.empty()) {
376 std::lock_guard
<std::mutex
> Lock(PublishMu
);
377 for (auto &Entry
: ReportableDiagnostics
)
378 Publish
->onDiagnosticsReady(Entry
.first(), /*Version=*/"",
381 return Context::current().derive(Config::Key
, std::move(C
));
384 void handleDiagnostic(const llvm::SMDiagnostic
&D
,
385 std::vector
<Diag
> *ClientDiagnostics
) {
386 switch (D
.getKind()) {
387 case llvm::SourceMgr::DK_Error
:
388 elog("config error at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
389 D
.getColumnNo(), D
.getMessage());
391 case llvm::SourceMgr::DK_Warning
:
392 log("config warning at {0}:{1}:{2}: {3}", D
.getFilename(),
393 D
.getLineNo(), D
.getColumnNo(), D
.getMessage());
395 case llvm::SourceMgr::DK_Note
:
396 case llvm::SourceMgr::DK_Remark
:
397 vlog("config note at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
398 D
.getColumnNo(), D
.getMessage());
399 ClientDiagnostics
= nullptr; // Don't emit notes as LSP diagnostics.
402 if (ClientDiagnostics
)
403 ClientDiagnostics
->push_back(toDiag(D
, Diag::ClangdConfig
));
408 return [I(std::make_shared
<Impl
>(Provider
, Publish
))](llvm::StringRef Path
) {
413 void ClangdServer::removeDocument(PathRef File
) {
414 DraftMgr
.removeDraft(File
);
415 WorkScheduler
->remove(File
);
418 void ClangdServer::codeComplete(PathRef File
, Position Pos
,
419 const clangd::CodeCompleteOptions
&Opts
,
420 Callback
<CodeCompleteResult
> CB
) {
421 // Copy completion options for passing them to async task handler.
422 auto CodeCompleteOpts
= Opts
;
423 if (!CodeCompleteOpts
.Index
) // Respect overridden index.
424 CodeCompleteOpts
.Index
= Index
;
426 auto Task
= [Pos
, CodeCompleteOpts
, File
= File
.str(), CB
= std::move(CB
),
427 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
429 return CB(IP
.takeError());
430 if (auto Reason
= isCancelled())
431 return CB(llvm::make_error
<CancelledError
>(Reason
));
433 std::optional
<SpeculativeFuzzyFind
> SpecFuzzyFind
;
435 // No speculation in Fallback mode, as it's supposed to be much faster
436 // without compiling.
437 vlog("Build for file {0} is not ready. Enter fallback mode.", File
);
438 } else if (CodeCompleteOpts
.Index
) {
439 SpecFuzzyFind
.emplace();
441 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
442 SpecFuzzyFind
->CachedReq
= CachedCompletionFuzzyFindRequestByFile
[File
];
445 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
446 // FIXME: Add traling new line if there is none at eof, workaround a crash,
447 // see https://github.com/clangd/clangd/issues/332
448 if (!IP
->Contents
.ends_with("\n"))
449 ParseInput
.Contents
.append("\n");
450 ParseInput
.Index
= Index
;
452 CodeCompleteOpts
.MainFileSignals
= IP
->Signals
;
453 CodeCompleteOpts
.AllScopes
= Config::current().Completion
.AllScopes
;
454 CodeCompleteOpts
.ArgumentLists
= Config::current().Completion
.ArgumentLists
;
455 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
456 // both the old and the new version in case only one of them matches.
457 CodeCompleteResult Result
= clangd::codeComplete(
458 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
459 SpecFuzzyFind
? &*SpecFuzzyFind
: nullptr);
461 clang::clangd::trace::Span
Tracer("Completion results callback");
462 CB(std::move(Result
));
464 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
465 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
466 CachedCompletionFuzzyFindRequestByFile
[File
] = *SpecFuzzyFind
->NewReq
;
468 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
469 // We don't want `codeComplete` to wait for the async call if it doesn't use
470 // the result (e.g. non-index completion, speculation fails), so that `CB`
471 // is called as soon as results are available.
474 // We use a potentially-stale preamble because latency is critical here.
475 WorkScheduler
->runWithPreamble(
476 "CodeComplete", File
,
477 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
479 : TUScheduler::StaleOrAbsent
,
483 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
484 MarkupKind DocumentationFormat
,
485 Callback
<SignatureHelp
> CB
) {
487 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
489 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
491 return CB(IP
.takeError());
493 const auto *PreambleData
= IP
->Preamble
;
495 return CB(error("Failed to parse includes"));
497 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
498 // FIXME: Add traling new line if there is none at eof, workaround a crash,
499 // see https://github.com/clangd/clangd/issues/332
500 if (!IP
->Contents
.ends_with("\n"))
501 ParseInput
.Contents
.append("\n");
502 ParseInput
.Index
= Index
;
503 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
504 DocumentationFormat
));
507 // Unlike code completion, we wait for a preamble here.
508 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
512 void ClangdServer::formatFile(PathRef File
, std::optional
<Range
> Rng
,
513 Callback
<tooling::Replacements
> CB
) {
514 auto Code
= getDraft(File
);
516 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
517 ErrorCode::InvalidParams
));
518 tooling::Range RequestedRange
;
520 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
522 return CB(Begin
.takeError());
523 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
525 return CB(End
.takeError());
526 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
528 RequestedRange
= tooling::Range(0, Code
->size());
531 // Call clang-format.
532 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
533 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
534 CB
= std::move(CB
), this]() mutable {
535 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
, true);
536 tooling::Replacements IncludeReplaces
=
537 format::sortIncludes(Style
, Code
, Ranges
, File
);
538 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
540 return CB(Changed
.takeError());
542 CB(IncludeReplaces
.merge(format::reformat(
544 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
547 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
550 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
551 StringRef TriggerText
,
552 Callback
<std::vector
<TextEdit
>> CB
) {
553 auto Code
= getDraft(File
);
555 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
556 ErrorCode::InvalidParams
));
557 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
559 return CB(CursorPos
.takeError());
560 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
561 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
562 CB
= std::move(CB
), this]() mutable {
563 auto Style
= getFormatStyleForFile(File
, Code
, TFS
, false);
564 std::vector
<TextEdit
> Result
;
565 for (const tooling::Replacement
&R
:
566 formatIncremental(Code
, CursorPos
, TriggerText
, Style
))
567 Result
.push_back(replacementToEdit(Code
, R
));
570 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
573 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
574 std::optional
<std::string
> NewName
,
575 const RenameOptions
&RenameOpts
,
576 Callback
<RenameResult
> CB
) {
577 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
578 NewName
= std::move(NewName
),
579 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
581 return CB(InpAST
.takeError());
582 // prepareRename is latency-sensitive: we don't query the index, as we
583 // only need main-file references
585 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
586 InpAST
->AST
, File
, /*FS=*/nullptr,
587 /*Index=*/nullptr, RenameOpts
});
589 // LSP says to return null on failure, but that will result in a generic
590 // failure message. If we send an LSP error response, clients can surface
591 // the message to users (VSCode does).
592 return CB(Results
.takeError());
596 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
599 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
600 const RenameOptions
&Opts
,
601 Callback
<RenameResult
> CB
) {
602 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
604 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
605 // Tracks number of files edited per invocation.
606 static constexpr trace::Metric
RenameFiles("rename_files",
607 trace::Metric::Distribution
);
609 return CB(InpAST
.takeError());
610 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
611 DirtyFS
->view(std::nullopt
), Index
, Opts
});
613 return CB(R
.takeError());
615 if (Opts
.WantFormat
) {
616 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
617 *InpAST
->Inputs
.TFS
, false);
618 llvm::Error Err
= llvm::Error::success();
619 for (auto &E
: R
->GlobalChanges
)
621 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
624 return CB(std::move(Err
));
626 RenameFiles
.record(R
->GlobalChanges
.size());
629 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
633 // May generate several candidate selections, due to SelectionTree ambiguity.
634 // vector of pointers because GCC doesn't like non-copyable Selection.
635 llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
636 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
637 llvm::vfs::FileSystem
*FS
) {
638 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
640 return Begin
.takeError();
641 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
643 return End
.takeError();
644 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
645 SelectionTree::createEach(
646 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
647 [&](SelectionTree T
) {
648 Result
.push_back(std::make_unique
<Tweak::Selection
>(
649 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
652 assert(!Result
.empty() && "Expected at least one SelectionTree");
653 return std::move(Result
);
656 // Some fixes may perform local renaming, we want to convert those to clangd
657 // rename commands, such that we can leverage the index for more accurate
659 std::optional
<ClangdServer::CodeActionResult::Rename
>
660 tryConvertToRename(const Diag
*Diag
, const Fix
&Fix
) {
661 bool IsClangTidyRename
= Diag
->Source
== Diag::ClangTidy
&&
662 Diag
->Name
== "readability-identifier-naming" &&
664 if (IsClangTidyRename
&& Diag
->InsideMainFile
) {
665 ClangdServer::CodeActionResult::Rename R
;
666 R
.NewName
= Fix
.Edits
.front().newText
;
667 R
.FixMessage
= Fix
.Message
;
668 R
.Diag
= {Diag
->Range
, Diag
->Message
};
677 void ClangdServer::codeAction(const CodeActionInputs
&Params
,
678 Callback
<CodeActionResult
> CB
) {
679 auto Action
= [Params
, CB
= std::move(CB
),
680 FeatureModules(this->FeatureModules
)](
681 Expected
<InputsAndAST
> InpAST
) mutable {
683 return CB(InpAST
.takeError());
685 [Only(Params
.RequestedActionKinds
)](llvm::StringRef Kind
) {
688 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
689 return Kind
.consume_front(Base
) &&
690 (Kind
.empty() || Kind
.starts_with("."));
694 CodeActionResult Result
;
695 Result
.Version
= InpAST
->AST
.version().str();
696 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
697 auto FindMatchedDiag
= [&InpAST
](const DiagRef
&DR
) -> const Diag
* {
698 for (const auto &Diag
: InpAST
->AST
.getDiagnostics())
699 if (Diag
.Range
== DR
.Range
&& Diag
.Message
== DR
.Message
)
703 for (const auto &DiagRef
: Params
.Diagnostics
) {
704 if (const auto *Diag
= FindMatchedDiag(DiagRef
))
705 for (const auto &Fix
: Diag
->Fixes
) {
706 if (auto Rename
= tryConvertToRename(Diag
, Fix
)) {
707 Result
.Renames
.emplace_back(std::move(*Rename
));
709 Result
.QuickFixes
.push_back({DiagRef
, Fix
});
716 auto Selections
= tweakSelection(Params
.Selection
, *InpAST
, /*FS=*/nullptr);
718 return CB(Selections
.takeError());
719 // Don't allow a tweak to fire more than once across ambiguous selections.
720 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
721 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
722 return KindAllowed(T
.kind()) && Params
.TweakFilter(T
) &&
723 !PreparedTweaks
.count(T
.id());
725 for (const auto &Sel
: *Selections
) {
726 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
727 Result
.TweakRefs
.push_back(TweakRef
{T
->id(), T
->title(), T
->kind()});
728 PreparedTweaks
.insert(T
->id());
729 TweakAvailable
.record(1, T
->id());
732 CB(std::move(Result
));
735 WorkScheduler
->runWithAST("codeAction", Params
.File
, std::move(Action
),
739 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
740 Callback
<Tweak::Effect
> CB
) {
741 // Tracks number of times a tweak has been attempted.
742 static constexpr trace::Metric
TweakAttempt(
743 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
744 // Tracks number of times a tweak has failed to produce edits.
745 static constexpr trace::Metric
TweakFailed(
746 "tweak_failed", trace::Metric::Counter
, "tweak_id");
747 TweakAttempt
.record(1, TweakID
);
748 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
750 this](Expected
<InputsAndAST
> InpAST
) mutable {
752 return CB(InpAST
.takeError());
753 auto FS
= DirtyFS
->view(std::nullopt
);
754 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
756 return CB(Selections
.takeError());
757 std::optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
758 // Try each selection, take the first one that prepare()s.
759 // If they all fail, Effect will hold get the last error.
760 for (const auto &Selection
: *Selections
) {
761 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
763 Effect
= (*T
)->apply(*Selection
);
766 Effect
= T
.takeError();
768 assert(Effect
&& "Expected at least one selection");
769 if (*Effect
&& (*Effect
)->FormatEdits
) {
770 // Format tweaks that require it centrally here.
771 for (auto &It
: (*Effect
)->ApplyEdits
) {
773 format::FormatStyle Style
=
774 getFormatStyleForFile(File
, E
.InitialCode
, TFS
, false);
775 if (llvm::Error Err
= reformatEdit(E
, Style
))
776 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
779 TweakFailed
.record(1, TweakID
);
781 return CB(std::move(*Effect
));
783 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
786 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
787 Callback
<std::vector
<LocatedSymbol
>> CB
) {
788 auto Action
= [Pos
, CB
= std::move(CB
),
789 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
791 return CB(InpAST
.takeError());
792 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
795 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
798 void ClangdServer::switchSourceHeader(
799 PathRef Path
, Callback
<std::optional
<clangd::Path
>> CB
) {
800 // We want to return the result as fast as possible, strategy is:
801 // 1) use the file-only heuristic, it requires some IO but it is much
802 // faster than building AST, but it only works when .h/.cc files are in
803 // the same directory.
804 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
805 // different code layout.
806 if (auto CorrespondingFile
=
807 getCorrespondingHeaderOrSource(Path
, TFS
.view(std::nullopt
)))
808 return CB(std::move(CorrespondingFile
));
809 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
810 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
812 return CB(InpAST
.takeError());
813 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
815 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
818 void ClangdServer::findDocumentHighlights(
819 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
821 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
823 return CB(InpAST
.takeError());
824 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
827 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
830 void ClangdServer::findHover(PathRef File
, Position Pos
,
831 Callback
<std::optional
<HoverInfo
>> CB
) {
832 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
833 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
835 return CB(InpAST
.takeError());
836 format::FormatStyle Style
= getFormatStyleForFile(
837 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
, false);
838 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
841 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
844 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
845 TypeHierarchyDirection Direction
,
846 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
847 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
848 this](Expected
<InputsAndAST
> InpAST
) mutable {
850 return CB(InpAST
.takeError());
851 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
855 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
858 void ClangdServer::superTypes(
859 const TypeHierarchyItem
&Item
,
860 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> CB
) {
861 WorkScheduler
->run("typeHierarchy/superTypes", /*Path=*/"",
862 [=, CB
= std::move(CB
)]() mutable {
863 CB(clangd::superTypes(Item
, Index
));
867 void ClangdServer::subTypes(const TypeHierarchyItem
&Item
,
868 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
870 "typeHierarchy/subTypes", /*Path=*/"",
871 [=, CB
= std::move(CB
)]() mutable { CB(clangd::subTypes(Item
, Index
)); });
874 void ClangdServer::resolveTypeHierarchy(
875 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
876 Callback
<std::optional
<TypeHierarchyItem
>> CB
) {
878 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
879 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
884 void ClangdServer::prepareCallHierarchy(
885 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
886 auto Action
= [File
= File
.str(), Pos
,
887 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
889 return CB(InpAST
.takeError());
890 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
892 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
895 void ClangdServer::incomingCalls(
896 const CallHierarchyItem
&Item
,
897 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
898 WorkScheduler
->run("Incoming Calls", "",
899 [CB
= std::move(CB
), Item
, this]() mutable {
900 CB(clangd::incomingCalls(Item
, Index
));
904 void ClangdServer::inlayHints(PathRef File
, std::optional
<Range
> RestrictRange
,
905 Callback
<std::vector
<InlayHint
>> CB
) {
906 auto Action
= [RestrictRange(std::move(RestrictRange
)),
907 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
909 return CB(InpAST
.takeError());
910 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
912 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
915 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
916 // FIXME: Do nothing for now. This will be used for indexing and potentially
917 // invalidating other caches.
920 void ClangdServer::workspaceSymbols(
921 llvm::StringRef Query
, int Limit
,
922 Callback
<std::vector
<SymbolInformation
>> CB
) {
924 "getWorkspaceSymbols", /*Path=*/"",
925 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
926 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
927 WorkspaceRoot
.value_or("")));
931 void ClangdServer::documentSymbols(llvm::StringRef File
,
932 Callback
<std::vector
<DocumentSymbol
>> CB
) {
934 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
936 return CB(InpAST
.takeError());
937 CB(clangd::getDocumentSymbols(InpAST
->AST
));
939 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
943 void ClangdServer::foldingRanges(llvm::StringRef File
,
944 Callback
<std::vector
<FoldingRange
>> CB
) {
945 auto Code
= getDraft(File
);
947 return CB(llvm::make_error
<LSPError
>(
948 "trying to compute folding ranges for non-added document",
949 ErrorCode::InvalidParams
));
950 auto Action
= [LineFoldingOnly
= LineFoldingOnly
, CB
= std::move(CB
),
951 Code
= std::move(*Code
)]() mutable {
952 CB(clangd::getFoldingRanges(Code
, LineFoldingOnly
));
954 // We want to make sure folding ranges are always available for all the open
955 // files, hence prefer runQuick to not wait for operations on other files.
956 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
959 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
960 Callback
<std::vector
<LocatedSymbol
>> CB
) {
961 auto Action
= [Pos
, CB
= std::move(CB
),
962 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
964 return CB(InpAST
.takeError());
965 CB(clangd::findType(InpAST
->AST
, Pos
, Index
));
967 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
970 void ClangdServer::findImplementations(
971 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
972 auto Action
= [Pos
, CB
= std::move(CB
),
973 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
975 return CB(InpAST
.takeError());
976 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
979 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
982 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
984 Callback
<ReferencesResult
> CB
) {
985 auto Action
= [Pos
, Limit
, AddContainer
, CB
= std::move(CB
),
986 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
988 return CB(InpAST
.takeError());
989 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
, AddContainer
));
992 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
995 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
996 Callback
<std::vector
<SymbolDetails
>> CB
) {
998 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1000 return CB(InpAST
.takeError());
1001 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
1004 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
1007 void ClangdServer::semanticRanges(PathRef File
,
1008 const std::vector
<Position
> &Positions
,
1009 Callback
<std::vector
<SelectionRange
>> CB
) {
1010 auto Action
= [Positions
, CB
= std::move(CB
)](
1011 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1013 return CB(InpAST
.takeError());
1014 std::vector
<SelectionRange
> Result
;
1015 for (const auto &Pos
: Positions
) {
1016 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
1017 Result
.push_back(std::move(*Range
));
1019 return CB(Range
.takeError());
1021 CB(std::move(Result
));
1023 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
1026 void ClangdServer::documentLinks(PathRef File
,
1027 Callback
<std::vector
<DocumentLink
>> CB
) {
1029 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1031 return CB(InpAST
.takeError());
1032 CB(clangd::getDocumentLinks(InpAST
->AST
));
1034 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
1038 void ClangdServer::semanticHighlights(
1039 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
1041 auto Action
= [CB
= std::move(CB
),
1042 PublishInactiveRegions
= PublishInactiveRegions
](
1043 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1045 return CB(InpAST
.takeError());
1046 // Include inactive regions in semantic highlighting tokens only if the
1047 // client doesn't support a dedicated protocol for being informed about
1049 CB(clangd::getSemanticHighlightings(InpAST
->AST
, !PublishInactiveRegions
));
1051 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
1055 void ClangdServer::getAST(PathRef File
, std::optional
<Range
> R
,
1056 Callback
<std::optional
<ASTNode
>> CB
) {
1058 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
1060 return CB(Inputs
.takeError());
1062 // It's safe to pass in the TU, as dumpAST() does not
1063 // deserialize the preamble.
1064 auto Node
= DynTypedNode::create(
1065 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
1066 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
1067 Inputs
->AST
.getASTContext()));
1069 unsigned Start
, End
;
1070 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
1073 return CB(Offset
.takeError());
1074 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
1077 return CB(Offset
.takeError());
1078 bool Success
= SelectionTree::createEach(
1079 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
1080 [&](SelectionTree T
) {
1081 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
1082 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
1083 Inputs
->AST
.getASTContext()));
1091 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
1094 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
1095 Callback
<InputsAndAST
> Action
) {
1096 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
1099 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
1101 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1103 return CB(InpAST
.takeError());
1104 return CB(InpAST
->AST
.getDiagnostics());
1107 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
1110 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1111 return WorkScheduler
->fileStats();
1115 ClangdServer::blockUntilIdleForTest(std::optional
<double> TimeoutSeconds
) {
1116 // Order is important here: we don't want to block on A and then B,
1117 // if B might schedule work on A.
1119 #if defined(__has_feature) && \
1120 (__has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) || \
1121 __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer))
1122 if (TimeoutSeconds
.has_value())
1123 (*TimeoutSeconds
) *= 10;
1126 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1127 // and we're blocking the main thread.
1128 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1130 // TUScheduler is the only thing that starts background indexing work.
1131 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1134 // Unfortunately we don't have strict topological order between the rest of
1135 // the components. E.g. CDB broadcast triggers backrgound indexing.
1136 // This queries the CDB which may discover new work if disk has changed.
1138 // So try each one a few times in a loop.
1139 // If there are no tricky interactions then all after the first are no-ops.
1140 // Then on the last iteration, verify they're idle without waiting.
1142 // There's a small chance they're juggling work and we didn't catch them :-(
1143 for (std::optional
<double> Timeout
:
1144 {TimeoutSeconds
, TimeoutSeconds
, std::optional
<double>(0)}) {
1145 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1147 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1149 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1150 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1155 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1156 "Something scheduled work while we're blocking the main thread!");
1160 void ClangdServer::profile(MemoryTree
&MT
) const {
1162 DynamicIdx
->profile(MT
.child("dynamic_index"));
1164 BackgroundIdx
->profile(MT
.child("background_index"));
1165 WorkScheduler
->profile(MT
.child("tuscheduler"));
1167 } // namespace clangd
1168 } // namespace clang