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/Logger.h"
34 #include "support/MemoryTree.h"
35 #include "support/ThreadsafeFS.h"
36 #include "support/Trace.h"
37 #include "clang/Basic/Stack.h"
38 #include "clang/Format/Format.h"
39 #include "clang/Lex/Preprocessor.h"
40 #include "clang/Tooling/CompilationDatabase.h"
41 #include "clang/Tooling/Core/Replacement.h"
42 #include "llvm/ADT/ArrayRef.h"
43 #include "llvm/ADT/STLExtras.h"
44 #include "llvm/ADT/StringRef.h"
45 #include "llvm/Support/Error.h"
46 #include "llvm/Support/Path.h"
47 #include "llvm/Support/raw_ostream.h"
55 #include <type_traits>
63 // Tracks number of times a tweak has been offered.
64 static constexpr trace::Metric
TweakAvailable(
65 "tweak_available", trace::Metric::Counter
, "tweak_id");
67 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
68 struct UpdateIndexCallbacks
: public ParsingCallbacks
{
69 UpdateIndexCallbacks(FileIndex
*FIndex
,
70 ClangdServer::Callbacks
*ServerCallbacks
,
71 const ThreadsafeFS
&TFS
, AsyncTaskRunner
*Tasks
,
72 bool CollectInactiveRegions
)
73 : FIndex(FIndex
), ServerCallbacks(ServerCallbacks
), TFS(TFS
),
74 Stdlib
{std::make_shared
<StdLibSet
>()}, Tasks(Tasks
),
75 CollectInactiveRegions(CollectInactiveRegions
) {}
78 PathRef Path
, llvm::StringRef Version
, CapturedASTCtx ASTCtx
,
79 std::shared_ptr
<const include_cleaner::PragmaIncludes
> PI
) override
{
84 auto &PP
= ASTCtx
.getPreprocessor();
85 auto &CI
= ASTCtx
.getCompilerInvocation();
86 if (auto Loc
= Stdlib
->add(CI
.getLangOpts(), PP
.getHeaderSearchInfo()))
87 indexStdlib(CI
, std::move(*Loc
));
89 // FIndex outlives the UpdateIndexCallbacks.
90 auto Task
= [FIndex(FIndex
), Path(Path
.str()), Version(Version
.str()),
91 ASTCtx(std::move(ASTCtx
)), PI(std::move(PI
))]() mutable {
92 trace::Span
Tracer("PreambleIndexing");
93 FIndex
->updatePreamble(Path
, Version
, ASTCtx
.getASTContext(),
94 ASTCtx
.getPreprocessor(), *PI
);
98 Tasks
->runAsync("Preamble indexing for:" + Path
+ Version
,
104 void indexStdlib(const CompilerInvocation
&CI
, StdLibLocation Loc
) {
105 // This task is owned by Tasks, which outlives the TUScheduler and
106 // therefore the UpdateIndexCallbacks.
107 // We must be careful that the references we capture outlive TUScheduler.
108 auto Task
= [LO(CI
.getLangOpts()), Loc(std::move(Loc
)),
109 CI(std::make_unique
<CompilerInvocation
>(CI
)),
110 // External values that outlive ClangdServer
112 // Index outlives TUScheduler (declared first)
114 // shared_ptr extends lifetime
115 Stdlib(Stdlib
)]() mutable {
116 clang::noteBottomOfStack();
118 IF
.Symbols
= indexStandardLibrary(std::move(CI
), Loc
, *TFS
);
119 if (Stdlib
->isBest(LO
))
120 FIndex
->updatePreamble(std::move(IF
));
123 // This doesn't have a semaphore to enforce -j, but it's rare.
124 Tasks
->runAsync("IndexStdlib", std::move(Task
));
129 void onMainAST(PathRef Path
, ParsedAST
&AST
, PublishFn Publish
) override
{
131 FIndex
->updateMain(Path
, AST
);
135 ServerCallbacks
->onDiagnosticsReady(Path
, AST
.version(),
136 AST
.getDiagnostics());
137 if (CollectInactiveRegions
) {
138 ServerCallbacks
->onInactiveRegionsReady(Path
,
139 getInactiveRegions(AST
));
144 void onFailedAST(PathRef Path
, llvm::StringRef Version
,
145 std::vector
<Diag
> Diags
, PublishFn Publish
) override
{
148 [&]() { ServerCallbacks
->onDiagnosticsReady(Path
, Version
, Diags
); });
151 void onFileUpdated(PathRef File
, const TUStatus
&Status
) override
{
153 ServerCallbacks
->onFileUpdated(File
, Status
);
156 void onPreamblePublished(PathRef File
) override
{
158 ServerCallbacks
->onSemanticsMaybeChanged(File
);
163 ClangdServer::Callbacks
*ServerCallbacks
;
164 const ThreadsafeFS
&TFS
;
165 std::shared_ptr
<StdLibSet
> Stdlib
;
166 AsyncTaskRunner
*Tasks
;
167 bool CollectInactiveRegions
;
170 class DraftStoreFS
: public ThreadsafeFS
{
172 DraftStoreFS(const ThreadsafeFS
&Base
, const DraftStore
&Drafts
)
173 : Base(Base
), DirtyFiles(Drafts
) {}
176 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> viewImpl() const override
{
177 auto OFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(
178 Base
.view(std::nullopt
));
179 OFS
->pushOverlay(DirtyFiles
.asVFS());
183 const ThreadsafeFS
&Base
;
184 const DraftStore
&DirtyFiles
;
189 ClangdServer::Options
ClangdServer::optsForTest() {
190 ClangdServer::Options Opts
;
191 Opts
.UpdateDebounce
= DebouncePolicy::fixed(/*zero*/ {});
192 Opts
.StorePreamblesInMemory
= true;
193 Opts
.AsyncThreadsCount
= 4; // Consistent!
197 ClangdServer::Options::operator TUScheduler::Options() const {
198 TUScheduler::Options Opts
;
199 Opts
.AsyncThreadsCount
= AsyncThreadsCount
;
200 Opts
.RetentionPolicy
= RetentionPolicy
;
201 Opts
.StorePreamblesInMemory
= StorePreamblesInMemory
;
202 Opts
.UpdateDebounce
= UpdateDebounce
;
203 Opts
.ContextProvider
= ContextProvider
;
204 Opts
.PreambleThrottler
= PreambleThrottler
;
208 ClangdServer::ClangdServer(const GlobalCompilationDatabase
&CDB
,
209 const ThreadsafeFS
&TFS
, const Options
&Opts
,
210 Callbacks
*Callbacks
)
211 : FeatureModules(Opts
.FeatureModules
), CDB(CDB
), TFS(TFS
),
212 DynamicIdx(Opts
.BuildDynamicSymbolIndex
? new FileIndex() : nullptr),
213 ClangTidyProvider(Opts
.ClangTidyProvider
),
214 UseDirtyHeaders(Opts
.UseDirtyHeaders
),
215 LineFoldingOnly(Opts
.LineFoldingOnly
),
216 PreambleParseForwardingFunctions(Opts
.PreambleParseForwardingFunctions
),
217 ImportInsertions(Opts
.ImportInsertions
),
218 PublishInactiveRegions(Opts
.PublishInactiveRegions
),
219 WorkspaceRoot(Opts
.WorkspaceRoot
),
220 Transient(Opts
.ImplicitCancellation
? TUScheduler::InvalidateOnUpdate
221 : TUScheduler::NoInvalidation
),
222 DirtyFS(std::make_unique
<DraftStoreFS
>(TFS
, DraftMgr
)) {
223 if (Opts
.AsyncThreadsCount
!= 0)
224 IndexTasks
.emplace();
225 // Pass a callback into `WorkScheduler` to extract symbols from a newly
226 // parsed file and rebuild the file index synchronously each time an AST
228 WorkScheduler
.emplace(CDB
, TUScheduler::Options(Opts
),
229 std::make_unique
<UpdateIndexCallbacks
>(
230 DynamicIdx
.get(), Callbacks
, TFS
,
231 IndexTasks
? &*IndexTasks
: nullptr,
232 PublishInactiveRegions
));
233 // Adds an index to the stack, at higher priority than existing indexes.
234 auto AddIndex
= [&](SymbolIndex
*Idx
) {
235 if (this->Index
!= nullptr) {
236 MergedIdx
.push_back(std::make_unique
<MergedIndex
>(Idx
, this->Index
));
237 this->Index
= MergedIdx
.back().get();
242 if (Opts
.StaticIndex
)
243 AddIndex(Opts
.StaticIndex
);
244 if (Opts
.BackgroundIndex
) {
245 BackgroundIndex::Options BGOpts
;
246 BGOpts
.ThreadPoolSize
= std::max(Opts
.AsyncThreadsCount
, 1u);
247 BGOpts
.OnProgress
= [Callbacks
](BackgroundQueue::Stats S
) {
249 Callbacks
->onBackgroundIndexProgress(S
);
251 BGOpts
.ContextProvider
= Opts
.ContextProvider
;
252 BackgroundIdx
= std::make_unique
<BackgroundIndex
>(
254 BackgroundIndexStorage::createDiskBackedStorageFactory(
255 [&CDB
](llvm::StringRef File
) { return CDB
.getProjectInfo(File
); }),
257 AddIndex(BackgroundIdx
.get());
260 AddIndex(DynamicIdx
.get());
262 if (Opts
.FeatureModules
) {
263 FeatureModule::Facilities F
{
264 *this->WorkScheduler
,
268 for (auto &Mod
: *Opts
.FeatureModules
)
273 ClangdServer::~ClangdServer() {
274 // Destroying TUScheduler first shuts down request threads that might
275 // otherwise access members concurrently.
276 // (Nobody can be using TUScheduler because we're on the main thread).
277 WorkScheduler
.reset();
278 // Now requests have stopped, we can shut down feature modules.
279 if (FeatureModules
) {
280 for (auto &Mod
: *FeatureModules
)
282 for (auto &Mod
: *FeatureModules
)
283 Mod
.blockUntilIdle(Deadline::infinity());
287 void ClangdServer::addDocument(PathRef File
, llvm::StringRef Contents
,
288 llvm::StringRef Version
,
289 WantDiagnostics WantDiags
, bool ForceRebuild
) {
290 std::string ActualVersion
= DraftMgr
.addDraft(File
, Version
, Contents
);
292 Opts
.PreambleParseForwardingFunctions
= PreambleParseForwardingFunctions
;
293 Opts
.ImportInsertions
= ImportInsertions
;
295 // Compile command is set asynchronously during update, as it can be slow.
297 Inputs
.TFS
= &getHeaderFS();
298 Inputs
.Contents
= std::string(Contents
);
299 Inputs
.Version
= std::move(ActualVersion
);
300 Inputs
.ForceRebuild
= ForceRebuild
;
301 Inputs
.Opts
= std::move(Opts
);
302 Inputs
.Index
= Index
;
303 Inputs
.ClangTidyProvider
= ClangTidyProvider
;
304 Inputs
.FeatureModules
= FeatureModules
;
305 bool NewFile
= WorkScheduler
->update(File
, Inputs
, WantDiags
);
306 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
307 if (NewFile
&& BackgroundIdx
)
308 BackgroundIdx
->boostRelated(File
);
311 void ClangdServer::reparseOpenFilesIfNeeded(
312 llvm::function_ref
<bool(llvm::StringRef File
)> Filter
) {
313 // Reparse only opened files that were modified.
314 for (const Path
&FilePath
: DraftMgr
.getActiveFiles())
315 if (Filter(FilePath
))
316 if (auto Draft
= DraftMgr
.getDraft(FilePath
)) // else disappeared in race?
317 addDocument(FilePath
, *Draft
->Contents
, Draft
->Version
,
318 WantDiagnostics::Auto
);
321 std::shared_ptr
<const std::string
> ClangdServer::getDraft(PathRef File
) const {
322 auto Draft
= DraftMgr
.getDraft(File
);
325 return std::move(Draft
->Contents
);
328 std::function
<Context(PathRef
)>
329 ClangdServer::createConfiguredContextProvider(const config::Provider
*Provider
,
330 Callbacks
*Publish
) {
332 return [](llvm::StringRef
) { return Context::current().clone(); };
335 const config::Provider
*Provider
;
336 ClangdServer::Callbacks
*Publish
;
337 std::mutex PublishMu
;
339 Impl(const config::Provider
*Provider
, ClangdServer::Callbacks
*Publish
)
340 : Provider(Provider
), Publish(Publish
) {}
342 Context
operator()(llvm::StringRef File
) {
343 config::Params Params
;
344 // Don't reread config files excessively often.
345 // FIXME: when we see a config file change event, use the event timestamp?
347 std::chrono::steady_clock::now() - std::chrono::seconds(5);
348 llvm::SmallString
<256> PosixPath
;
350 assert(llvm::sys::path::is_absolute(File
));
351 llvm::sys::path::native(File
, PosixPath
, llvm::sys::path::Style::posix
);
352 Params
.Path
= PosixPath
.str();
355 llvm::StringMap
<std::vector
<Diag
>> ReportableDiagnostics
;
356 Config C
= Provider
->getConfig(Params
, [&](const llvm::SMDiagnostic
&D
) {
357 // Create the map entry even for note diagnostics we don't report.
358 // This means that when the file is parsed with no warnings, we
359 // publish an empty set of diagnostics, clearing any the client has.
360 handleDiagnostic(D
, !Publish
|| D
.getFilename().empty()
362 : &ReportableDiagnostics
[D
.getFilename()]);
364 // Blindly publish diagnostics for the (unopened) parsed config files.
365 // We must avoid reporting diagnostics for *the same file* concurrently.
366 // Source diags are published elsewhere, but those are different files.
367 if (!ReportableDiagnostics
.empty()) {
368 std::lock_guard
<std::mutex
> Lock(PublishMu
);
369 for (auto &Entry
: ReportableDiagnostics
)
370 Publish
->onDiagnosticsReady(Entry
.first(), /*Version=*/"",
373 return Context::current().derive(Config::Key
, std::move(C
));
376 void handleDiagnostic(const llvm::SMDiagnostic
&D
,
377 std::vector
<Diag
> *ClientDiagnostics
) {
378 switch (D
.getKind()) {
379 case llvm::SourceMgr::DK_Error
:
380 elog("config error at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
381 D
.getColumnNo(), D
.getMessage());
383 case llvm::SourceMgr::DK_Warning
:
384 log("config warning at {0}:{1}:{2}: {3}", D
.getFilename(),
385 D
.getLineNo(), D
.getColumnNo(), D
.getMessage());
387 case llvm::SourceMgr::DK_Note
:
388 case llvm::SourceMgr::DK_Remark
:
389 vlog("config note at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
390 D
.getColumnNo(), D
.getMessage());
391 ClientDiagnostics
= nullptr; // Don't emit notes as LSP diagnostics.
394 if (ClientDiagnostics
)
395 ClientDiagnostics
->push_back(toDiag(D
, Diag::ClangdConfig
));
400 return [I(std::make_shared
<Impl
>(Provider
, Publish
))](llvm::StringRef Path
) {
405 void ClangdServer::removeDocument(PathRef File
) {
406 DraftMgr
.removeDraft(File
);
407 WorkScheduler
->remove(File
);
410 void ClangdServer::codeComplete(PathRef File
, Position Pos
,
411 const clangd::CodeCompleteOptions
&Opts
,
412 Callback
<CodeCompleteResult
> CB
) {
413 // Copy completion options for passing them to async task handler.
414 auto CodeCompleteOpts
= Opts
;
415 if (!CodeCompleteOpts
.Index
) // Respect overridden index.
416 CodeCompleteOpts
.Index
= Index
;
418 auto Task
= [Pos
, CodeCompleteOpts
, File
= File
.str(), CB
= std::move(CB
),
419 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
421 return CB(IP
.takeError());
422 if (auto Reason
= isCancelled())
423 return CB(llvm::make_error
<CancelledError
>(Reason
));
425 std::optional
<SpeculativeFuzzyFind
> SpecFuzzyFind
;
427 // No speculation in Fallback mode, as it's supposed to be much faster
428 // without compiling.
429 vlog("Build for file {0} is not ready. Enter fallback mode.", File
);
430 } else if (CodeCompleteOpts
.Index
) {
431 SpecFuzzyFind
.emplace();
433 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
434 SpecFuzzyFind
->CachedReq
= CachedCompletionFuzzyFindRequestByFile
[File
];
437 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
438 // FIXME: Add traling new line if there is none at eof, workaround a crash,
439 // see https://github.com/clangd/clangd/issues/332
440 if (!IP
->Contents
.endswith("\n"))
441 ParseInput
.Contents
.append("\n");
442 ParseInput
.Index
= Index
;
444 CodeCompleteOpts
.MainFileSignals
= IP
->Signals
;
445 CodeCompleteOpts
.AllScopes
= Config::current().Completion
.AllScopes
;
446 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
447 // both the old and the new version in case only one of them matches.
448 CodeCompleteResult Result
= clangd::codeComplete(
449 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
450 SpecFuzzyFind
? &*SpecFuzzyFind
: nullptr);
452 clang::clangd::trace::Span
Tracer("Completion results callback");
453 CB(std::move(Result
));
455 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
456 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
457 CachedCompletionFuzzyFindRequestByFile
[File
] = *SpecFuzzyFind
->NewReq
;
459 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
460 // We don't want `codeComplete` to wait for the async call if it doesn't use
461 // the result (e.g. non-index completion, speculation fails), so that `CB`
462 // is called as soon as results are available.
465 // We use a potentially-stale preamble because latency is critical here.
466 WorkScheduler
->runWithPreamble(
467 "CodeComplete", File
,
468 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
470 : TUScheduler::StaleOrAbsent
,
474 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
475 MarkupKind DocumentationFormat
,
476 Callback
<SignatureHelp
> CB
) {
478 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
480 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
482 return CB(IP
.takeError());
484 const auto *PreambleData
= IP
->Preamble
;
486 return CB(error("Failed to parse includes"));
488 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
489 // FIXME: Add traling new line if there is none at eof, workaround a crash,
490 // see https://github.com/clangd/clangd/issues/332
491 if (!IP
->Contents
.endswith("\n"))
492 ParseInput
.Contents
.append("\n");
493 ParseInput
.Index
= Index
;
494 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
495 DocumentationFormat
));
498 // Unlike code completion, we wait for a preamble here.
499 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
503 void ClangdServer::formatFile(PathRef File
, std::optional
<Range
> Rng
,
504 Callback
<tooling::Replacements
> CB
) {
505 auto Code
= getDraft(File
);
507 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
508 ErrorCode::InvalidParams
));
509 tooling::Range RequestedRange
;
511 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
513 return CB(Begin
.takeError());
514 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
516 return CB(End
.takeError());
517 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
519 RequestedRange
= tooling::Range(0, Code
->size());
522 // Call clang-format.
523 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
524 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
525 CB
= std::move(CB
), this]() mutable {
526 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
);
527 tooling::Replacements IncludeReplaces
=
528 format::sortIncludes(Style
, Code
, Ranges
, File
);
529 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
531 return CB(Changed
.takeError());
533 CB(IncludeReplaces
.merge(format::reformat(
535 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
538 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
541 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
542 StringRef TriggerText
,
543 Callback
<std::vector
<TextEdit
>> CB
) {
544 auto Code
= getDraft(File
);
546 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
547 ErrorCode::InvalidParams
));
548 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
550 return CB(CursorPos
.takeError());
551 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
552 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
553 CB
= std::move(CB
), this]() mutable {
554 auto Style
= format::getStyle(format::DefaultFormatStyle
, File
,
555 format::DefaultFallbackStyle
, Code
,
556 TFS
.view(/*CWD=*/std::nullopt
).get());
558 return CB(Style
.takeError());
560 std::vector
<TextEdit
> Result
;
561 for (const tooling::Replacement
&R
:
562 formatIncremental(Code
, CursorPos
, TriggerText
, *Style
))
563 Result
.push_back(replacementToEdit(Code
, R
));
566 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
569 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
570 std::optional
<std::string
> NewName
,
571 const RenameOptions
&RenameOpts
,
572 Callback
<RenameResult
> CB
) {
573 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
574 NewName
= std::move(NewName
),
575 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
577 return CB(InpAST
.takeError());
578 // prepareRename is latency-sensitive: we don't query the index, as we
579 // only need main-file references
581 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
582 InpAST
->AST
, File
, /*FS=*/nullptr,
583 /*Index=*/nullptr, RenameOpts
});
585 // LSP says to return null on failure, but that will result in a generic
586 // failure message. If we send an LSP error response, clients can surface
587 // the message to users (VSCode does).
588 return CB(Results
.takeError());
592 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
595 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
596 const RenameOptions
&Opts
,
597 Callback
<RenameResult
> CB
) {
598 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
600 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
601 // Tracks number of files edited per invocation.
602 static constexpr trace::Metric
RenameFiles("rename_files",
603 trace::Metric::Distribution
);
605 return CB(InpAST
.takeError());
606 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
607 DirtyFS
->view(std::nullopt
), Index
, Opts
});
609 return CB(R
.takeError());
611 if (Opts
.WantFormat
) {
612 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
613 *InpAST
->Inputs
.TFS
);
614 llvm::Error Err
= llvm::Error::success();
615 for (auto &E
: R
->GlobalChanges
)
617 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
620 return CB(std::move(Err
));
622 RenameFiles
.record(R
->GlobalChanges
.size());
625 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
628 // May generate several candidate selections, due to SelectionTree ambiguity.
629 // vector of pointers because GCC doesn't like non-copyable Selection.
630 static llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
631 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
632 llvm::vfs::FileSystem
*FS
) {
633 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
635 return Begin
.takeError();
636 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
638 return End
.takeError();
639 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
640 SelectionTree::createEach(
641 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
642 [&](SelectionTree T
) {
643 Result
.push_back(std::make_unique
<Tweak::Selection
>(
644 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
647 assert(!Result
.empty() && "Expected at least one SelectionTree");
648 return std::move(Result
);
651 void ClangdServer::codeAction(const CodeActionInputs
&Params
,
652 Callback
<CodeActionResult
> CB
) {
653 auto Action
= [Params
, CB
= std::move(CB
),
654 FeatureModules(this->FeatureModules
)](
655 Expected
<InputsAndAST
> InpAST
) mutable {
657 return CB(InpAST
.takeError());
659 [Only(Params
.RequestedActionKinds
)](llvm::StringRef Kind
) {
662 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
663 return Kind
.consume_front(Base
) &&
664 (Kind
.empty() || Kind
.startswith("."));
668 CodeActionResult Result
;
669 Result
.Version
= InpAST
->AST
.version().str();
670 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
671 auto FindMatchedFixes
=
672 [&InpAST
](const DiagRef
&DR
) -> llvm::ArrayRef
<Fix
> {
673 for (const auto &Diag
: InpAST
->AST
.getDiagnostics())
674 if (Diag
.Range
== DR
.Range
&& Diag
.Message
== DR
.Message
)
678 for (const auto &Diag
: Params
.Diagnostics
)
679 for (const auto &Fix
: FindMatchedFixes(Diag
))
680 Result
.QuickFixes
.push_back({Diag
, Fix
});
684 auto Selections
= tweakSelection(Params
.Selection
, *InpAST
, /*FS=*/nullptr);
686 return CB(Selections
.takeError());
687 // Don't allow a tweak to fire more than once across ambiguous selections.
688 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
689 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
690 return KindAllowed(T
.kind()) && Params
.TweakFilter(T
) &&
691 !PreparedTweaks
.count(T
.id());
693 for (const auto &Sel
: *Selections
) {
694 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
695 Result
.TweakRefs
.push_back(TweakRef
{T
->id(), T
->title(), T
->kind()});
696 PreparedTweaks
.insert(T
->id());
697 TweakAvailable
.record(1, T
->id());
700 CB(std::move(Result
));
703 WorkScheduler
->runWithAST("codeAction", Params
.File
, std::move(Action
),
707 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
708 Callback
<Tweak::Effect
> CB
) {
709 // Tracks number of times a tweak has been attempted.
710 static constexpr trace::Metric
TweakAttempt(
711 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
712 // Tracks number of times a tweak has failed to produce edits.
713 static constexpr trace::Metric
TweakFailed(
714 "tweak_failed", trace::Metric::Counter
, "tweak_id");
715 TweakAttempt
.record(1, TweakID
);
716 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
718 this](Expected
<InputsAndAST
> InpAST
) mutable {
720 return CB(InpAST
.takeError());
721 auto FS
= DirtyFS
->view(std::nullopt
);
722 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
724 return CB(Selections
.takeError());
725 std::optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
726 // Try each selection, take the first one that prepare()s.
727 // If they all fail, Effect will hold get the last error.
728 for (const auto &Selection
: *Selections
) {
729 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
731 Effect
= (*T
)->apply(*Selection
);
734 Effect
= T
.takeError();
736 assert(Effect
&& "Expected at least one selection");
737 if (*Effect
&& (*Effect
)->FormatEdits
) {
738 // Format tweaks that require it centrally here.
739 for (auto &It
: (*Effect
)->ApplyEdits
) {
741 format::FormatStyle Style
=
742 getFormatStyleForFile(File
, E
.InitialCode
, TFS
);
743 if (llvm::Error Err
= reformatEdit(E
, Style
))
744 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
747 TweakFailed
.record(1, TweakID
);
749 return CB(std::move(*Effect
));
751 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
754 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
755 Callback
<std::vector
<LocatedSymbol
>> CB
) {
756 auto Action
= [Pos
, CB
= std::move(CB
),
757 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
759 return CB(InpAST
.takeError());
760 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
763 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
766 void ClangdServer::switchSourceHeader(
767 PathRef Path
, Callback
<std::optional
<clangd::Path
>> CB
) {
768 // We want to return the result as fast as possible, strategy is:
769 // 1) use the file-only heuristic, it requires some IO but it is much
770 // faster than building AST, but it only works when .h/.cc files are in
771 // the same directory.
772 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
773 // different code layout.
774 if (auto CorrespondingFile
=
775 getCorrespondingHeaderOrSource(Path
, TFS
.view(std::nullopt
)))
776 return CB(std::move(CorrespondingFile
));
777 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
778 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
780 return CB(InpAST
.takeError());
781 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
783 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
786 void ClangdServer::findDocumentHighlights(
787 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
789 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
791 return CB(InpAST
.takeError());
792 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
795 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
798 void ClangdServer::findHover(PathRef File
, Position Pos
,
799 Callback
<std::optional
<HoverInfo
>> CB
) {
800 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
801 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
803 return CB(InpAST
.takeError());
804 format::FormatStyle Style
= getFormatStyleForFile(
805 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
);
806 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
809 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
812 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
813 TypeHierarchyDirection Direction
,
814 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
815 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
816 this](Expected
<InputsAndAST
> InpAST
) mutable {
818 return CB(InpAST
.takeError());
819 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
823 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
826 void ClangdServer::superTypes(
827 const TypeHierarchyItem
&Item
,
828 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> CB
) {
829 WorkScheduler
->run("typeHierarchy/superTypes", /*Path=*/"",
830 [=, CB
= std::move(CB
)]() mutable {
831 CB(clangd::superTypes(Item
, Index
));
835 void ClangdServer::subTypes(const TypeHierarchyItem
&Item
,
836 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
838 "typeHierarchy/subTypes", /*Path=*/"",
839 [=, CB
= std::move(CB
)]() mutable { CB(clangd::subTypes(Item
, Index
)); });
842 void ClangdServer::resolveTypeHierarchy(
843 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
844 Callback
<std::optional
<TypeHierarchyItem
>> CB
) {
846 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
847 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
852 void ClangdServer::prepareCallHierarchy(
853 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
854 auto Action
= [File
= File
.str(), Pos
,
855 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
857 return CB(InpAST
.takeError());
858 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
860 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
863 void ClangdServer::incomingCalls(
864 const CallHierarchyItem
&Item
,
865 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
866 WorkScheduler
->run("Incoming Calls", "",
867 [CB
= std::move(CB
), Item
, this]() mutable {
868 CB(clangd::incomingCalls(Item
, Index
));
872 void ClangdServer::inlayHints(PathRef File
, std::optional
<Range
> RestrictRange
,
873 Callback
<std::vector
<InlayHint
>> CB
) {
874 auto Action
= [RestrictRange(std::move(RestrictRange
)),
875 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
877 return CB(InpAST
.takeError());
878 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
880 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
883 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
884 // FIXME: Do nothing for now. This will be used for indexing and potentially
885 // invalidating other caches.
888 void ClangdServer::workspaceSymbols(
889 llvm::StringRef Query
, int Limit
,
890 Callback
<std::vector
<SymbolInformation
>> CB
) {
892 "getWorkspaceSymbols", /*Path=*/"",
893 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
894 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
895 WorkspaceRoot
.value_or("")));
899 void ClangdServer::documentSymbols(llvm::StringRef File
,
900 Callback
<std::vector
<DocumentSymbol
>> CB
) {
902 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
904 return CB(InpAST
.takeError());
905 CB(clangd::getDocumentSymbols(InpAST
->AST
));
907 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
911 void ClangdServer::foldingRanges(llvm::StringRef File
,
912 Callback
<std::vector
<FoldingRange
>> CB
) {
913 auto Code
= getDraft(File
);
915 return CB(llvm::make_error
<LSPError
>(
916 "trying to compute folding ranges for non-added document",
917 ErrorCode::InvalidParams
));
918 auto Action
= [LineFoldingOnly
= LineFoldingOnly
, CB
= std::move(CB
),
919 Code
= std::move(*Code
)]() mutable {
920 CB(clangd::getFoldingRanges(Code
, LineFoldingOnly
));
922 // We want to make sure folding ranges are always available for all the open
923 // files, hence prefer runQuick to not wait for operations on other files.
924 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
927 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
928 Callback
<std::vector
<LocatedSymbol
>> CB
) {
929 auto Action
= [Pos
, CB
= std::move(CB
),
930 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
932 return CB(InpAST
.takeError());
933 CB(clangd::findType(InpAST
->AST
, Pos
, Index
));
935 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
938 void ClangdServer::findImplementations(
939 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
940 auto Action
= [Pos
, CB
= std::move(CB
),
941 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
943 return CB(InpAST
.takeError());
944 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
947 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
950 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
952 Callback
<ReferencesResult
> CB
) {
953 auto Action
= [Pos
, Limit
, AddContainer
, CB
= std::move(CB
),
954 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
956 return CB(InpAST
.takeError());
957 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
, AddContainer
));
960 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
963 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
964 Callback
<std::vector
<SymbolDetails
>> CB
) {
966 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
968 return CB(InpAST
.takeError());
969 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
972 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
975 void ClangdServer::semanticRanges(PathRef File
,
976 const std::vector
<Position
> &Positions
,
977 Callback
<std::vector
<SelectionRange
>> CB
) {
978 auto Action
= [Positions
, CB
= std::move(CB
)](
979 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
981 return CB(InpAST
.takeError());
982 std::vector
<SelectionRange
> Result
;
983 for (const auto &Pos
: Positions
) {
984 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
985 Result
.push_back(std::move(*Range
));
987 return CB(Range
.takeError());
989 CB(std::move(Result
));
991 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
994 void ClangdServer::documentLinks(PathRef File
,
995 Callback
<std::vector
<DocumentLink
>> CB
) {
997 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
999 return CB(InpAST
.takeError());
1000 CB(clangd::getDocumentLinks(InpAST
->AST
));
1002 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
1006 void ClangdServer::semanticHighlights(
1007 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
1009 auto Action
= [CB
= std::move(CB
),
1010 PublishInactiveRegions
= PublishInactiveRegions
](
1011 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1013 return CB(InpAST
.takeError());
1014 // Include inactive regions in semantic highlighting tokens only if the
1015 // client doesn't support a dedicated protocol for being informed about
1017 CB(clangd::getSemanticHighlightings(InpAST
->AST
, !PublishInactiveRegions
));
1019 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
1023 void ClangdServer::getAST(PathRef File
, std::optional
<Range
> R
,
1024 Callback
<std::optional
<ASTNode
>> CB
) {
1026 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
1028 return CB(Inputs
.takeError());
1030 // It's safe to pass in the TU, as dumpAST() does not
1031 // deserialize the preamble.
1032 auto Node
= DynTypedNode::create(
1033 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
1034 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
1035 Inputs
->AST
.getASTContext()));
1037 unsigned Start
, End
;
1038 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
1041 return CB(Offset
.takeError());
1042 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
1045 return CB(Offset
.takeError());
1046 bool Success
= SelectionTree::createEach(
1047 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
1048 [&](SelectionTree T
) {
1049 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
1050 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
1051 Inputs
->AST
.getASTContext()));
1059 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
1062 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
1063 Callback
<InputsAndAST
> Action
) {
1064 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
1067 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
1069 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1071 return CB(InpAST
.takeError());
1072 return CB(InpAST
->AST
.getDiagnostics());
1075 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
1078 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1079 return WorkScheduler
->fileStats();
1083 ClangdServer::blockUntilIdleForTest(std::optional
<double> TimeoutSeconds
) {
1084 // Order is important here: we don't want to block on A and then B,
1085 // if B might schedule work on A.
1087 #if defined(__has_feature) && \
1088 (__has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) || \
1089 __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer))
1090 if (TimeoutSeconds
.has_value())
1091 (*TimeoutSeconds
) *= 10;
1094 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1095 // and we're blocking the main thread.
1096 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1098 // TUScheduler is the only thing that starts background indexing work.
1099 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1102 // Unfortunately we don't have strict topological order between the rest of
1103 // the components. E.g. CDB broadcast triggers backrgound indexing.
1104 // This queries the CDB which may discover new work if disk has changed.
1106 // So try each one a few times in a loop.
1107 // If there are no tricky interactions then all after the first are no-ops.
1108 // Then on the last iteration, verify they're idle without waiting.
1110 // There's a small chance they're juggling work and we didn't catch them :-(
1111 for (std::optional
<double> Timeout
:
1112 {TimeoutSeconds
, TimeoutSeconds
, std::optional
<double>(0)}) {
1113 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1115 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1117 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1118 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1123 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1124 "Something scheduled work while we're blocking the main thread!");
1128 void ClangdServer::profile(MemoryTree
&MT
) const {
1130 DynamicIdx
->profile(MT
.child("dynamic_index"));
1132 BackgroundIdx
->profile(MT
.child("background_index"));
1133 WorkScheduler
->profile(MT
.child("tuscheduler"));
1135 } // namespace clangd
1136 } // namespace clang