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/Format/Format.h"
38 #include "clang/Lex/Preprocessor.h"
39 #include "clang/Tooling/CompilationDatabase.h"
40 #include "clang/Tooling/Core/Replacement.h"
41 #include "llvm/ADT/ArrayRef.h"
42 #include "llvm/ADT/STLExtras.h"
43 #include "llvm/ADT/StringRef.h"
44 #include "llvm/Support/Error.h"
45 #include "llvm/Support/Path.h"
46 #include "llvm/Support/raw_ostream.h"
54 #include <type_traits>
62 // Tracks number of times a tweak has been offered.
63 static constexpr trace::Metric
TweakAvailable(
64 "tweak_available", trace::Metric::Counter
, "tweak_id");
66 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
67 struct UpdateIndexCallbacks
: public ParsingCallbacks
{
68 UpdateIndexCallbacks(FileIndex
*FIndex
,
69 ClangdServer::Callbacks
*ServerCallbacks
,
70 const ThreadsafeFS
&TFS
, AsyncTaskRunner
*Tasks
,
71 bool CollectInactiveRegions
)
72 : FIndex(FIndex
), ServerCallbacks(ServerCallbacks
), TFS(TFS
),
73 Stdlib
{std::make_shared
<StdLibSet
>()}, Tasks(Tasks
),
74 CollectInactiveRegions(CollectInactiveRegions
) {}
77 PathRef Path
, llvm::StringRef Version
, CapturedASTCtx ASTCtx
,
78 std::shared_ptr
<const include_cleaner::PragmaIncludes
> PI
) override
{
83 auto &PP
= ASTCtx
.getPreprocessor();
84 auto &CI
= ASTCtx
.getCompilerInvocation();
85 if (auto Loc
= Stdlib
->add(*CI
.getLangOpts(), PP
.getHeaderSearchInfo()))
86 indexStdlib(CI
, std::move(*Loc
));
88 // FIndex outlives the UpdateIndexCallbacks.
89 auto Task
= [FIndex(FIndex
), Path(Path
.str()), Version(Version
.str()),
90 ASTCtx(std::move(ASTCtx
)), PI(std::move(PI
))]() mutable {
91 trace::Span
Tracer("PreambleIndexing");
92 FIndex
->updatePreamble(Path
, Version
, ASTCtx
.getASTContext(),
93 ASTCtx
.getPreprocessor(), *PI
);
97 Tasks
->runAsync("Preamble indexing for:" + Path
+ Version
,
103 void indexStdlib(const CompilerInvocation
&CI
, StdLibLocation Loc
) {
104 // This task is owned by Tasks, which outlives the TUScheduler and
105 // therefore the UpdateIndexCallbacks.
106 // We must be careful that the references we capture outlive TUScheduler.
107 auto Task
= [LO(*CI
.getLangOpts()), Loc(std::move(Loc
)),
108 CI(std::make_unique
<CompilerInvocation
>(CI
)),
109 // External values that outlive ClangdServer
111 // Index outlives TUScheduler (declared first)
113 // shared_ptr extends lifetime
114 Stdlib(Stdlib
)]() mutable {
116 IF
.Symbols
= indexStandardLibrary(std::move(CI
), Loc
, *TFS
);
117 if (Stdlib
->isBest(LO
))
118 FIndex
->updatePreamble(std::move(IF
));
121 // This doesn't have a semaphore to enforce -j, but it's rare.
122 Tasks
->runAsync("IndexStdlib", std::move(Task
));
127 void onMainAST(PathRef Path
, ParsedAST
&AST
, PublishFn Publish
) override
{
129 FIndex
->updateMain(Path
, AST
);
133 ServerCallbacks
->onDiagnosticsReady(Path
, AST
.version(),
134 AST
.getDiagnostics());
135 if (CollectInactiveRegions
) {
136 ServerCallbacks
->onInactiveRegionsReady(Path
,
137 getInactiveRegions(AST
));
142 void onFailedAST(PathRef Path
, llvm::StringRef Version
,
143 std::vector
<Diag
> Diags
, PublishFn Publish
) override
{
146 [&]() { ServerCallbacks
->onDiagnosticsReady(Path
, Version
, Diags
); });
149 void onFileUpdated(PathRef File
, const TUStatus
&Status
) override
{
151 ServerCallbacks
->onFileUpdated(File
, Status
);
154 void onPreamblePublished(PathRef File
) override
{
156 ServerCallbacks
->onSemanticsMaybeChanged(File
);
161 ClangdServer::Callbacks
*ServerCallbacks
;
162 const ThreadsafeFS
&TFS
;
163 std::shared_ptr
<StdLibSet
> Stdlib
;
164 AsyncTaskRunner
*Tasks
;
165 bool CollectInactiveRegions
;
168 class DraftStoreFS
: public ThreadsafeFS
{
170 DraftStoreFS(const ThreadsafeFS
&Base
, const DraftStore
&Drafts
)
171 : Base(Base
), DirtyFiles(Drafts
) {}
174 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> viewImpl() const override
{
175 auto OFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(
176 Base
.view(std::nullopt
));
177 OFS
->pushOverlay(DirtyFiles
.asVFS());
181 const ThreadsafeFS
&Base
;
182 const DraftStore
&DirtyFiles
;
187 ClangdServer::Options
ClangdServer::optsForTest() {
188 ClangdServer::Options Opts
;
189 Opts
.UpdateDebounce
= DebouncePolicy::fixed(/*zero*/ {});
190 Opts
.StorePreamblesInMemory
= true;
191 Opts
.AsyncThreadsCount
= 4; // Consistent!
195 ClangdServer::Options::operator TUScheduler::Options() const {
196 TUScheduler::Options Opts
;
197 Opts
.AsyncThreadsCount
= AsyncThreadsCount
;
198 Opts
.RetentionPolicy
= RetentionPolicy
;
199 Opts
.StorePreamblesInMemory
= StorePreamblesInMemory
;
200 Opts
.UpdateDebounce
= UpdateDebounce
;
201 Opts
.ContextProvider
= ContextProvider
;
202 Opts
.PreambleThrottler
= PreambleThrottler
;
206 ClangdServer::ClangdServer(const GlobalCompilationDatabase
&CDB
,
207 const ThreadsafeFS
&TFS
, const Options
&Opts
,
208 Callbacks
*Callbacks
)
209 : FeatureModules(Opts
.FeatureModules
), CDB(CDB
), TFS(TFS
),
210 DynamicIdx(Opts
.BuildDynamicSymbolIndex
? new FileIndex() : nullptr),
211 ClangTidyProvider(Opts
.ClangTidyProvider
),
212 UseDirtyHeaders(Opts
.UseDirtyHeaders
),
213 LineFoldingOnly(Opts
.LineFoldingOnly
),
214 PreambleParseForwardingFunctions(Opts
.PreambleParseForwardingFunctions
),
215 ImportInsertions(Opts
.ImportInsertions
),
216 PublishInactiveRegions(Opts
.PublishInactiveRegions
),
217 WorkspaceRoot(Opts
.WorkspaceRoot
),
218 Transient(Opts
.ImplicitCancellation
? TUScheduler::InvalidateOnUpdate
219 : TUScheduler::NoInvalidation
),
220 DirtyFS(std::make_unique
<DraftStoreFS
>(TFS
, DraftMgr
)) {
221 if (Opts
.AsyncThreadsCount
!= 0)
222 IndexTasks
.emplace();
223 // Pass a callback into `WorkScheduler` to extract symbols from a newly
224 // parsed file and rebuild the file index synchronously each time an AST
226 WorkScheduler
.emplace(CDB
, TUScheduler::Options(Opts
),
227 std::make_unique
<UpdateIndexCallbacks
>(
228 DynamicIdx
.get(), Callbacks
, TFS
,
229 IndexTasks
? &*IndexTasks
: nullptr,
230 PublishInactiveRegions
));
231 // Adds an index to the stack, at higher priority than existing indexes.
232 auto AddIndex
= [&](SymbolIndex
*Idx
) {
233 if (this->Index
!= nullptr) {
234 MergedIdx
.push_back(std::make_unique
<MergedIndex
>(Idx
, this->Index
));
235 this->Index
= MergedIdx
.back().get();
240 if (Opts
.StaticIndex
)
241 AddIndex(Opts
.StaticIndex
);
242 if (Opts
.BackgroundIndex
) {
243 BackgroundIndex::Options BGOpts
;
244 BGOpts
.ThreadPoolSize
= std::max(Opts
.AsyncThreadsCount
, 1u);
245 BGOpts
.OnProgress
= [Callbacks
](BackgroundQueue::Stats S
) {
247 Callbacks
->onBackgroundIndexProgress(S
);
249 BGOpts
.ContextProvider
= Opts
.ContextProvider
;
250 BackgroundIdx
= std::make_unique
<BackgroundIndex
>(
252 BackgroundIndexStorage::createDiskBackedStorageFactory(
253 [&CDB
](llvm::StringRef File
) { return CDB
.getProjectInfo(File
); }),
255 AddIndex(BackgroundIdx
.get());
258 AddIndex(DynamicIdx
.get());
260 if (Opts
.FeatureModules
) {
261 FeatureModule::Facilities F
{
262 *this->WorkScheduler
,
266 for (auto &Mod
: *Opts
.FeatureModules
)
271 ClangdServer::~ClangdServer() {
272 // Destroying TUScheduler first shuts down request threads that might
273 // otherwise access members concurrently.
274 // (Nobody can be using TUScheduler because we're on the main thread).
275 WorkScheduler
.reset();
276 // Now requests have stopped, we can shut down feature modules.
277 if (FeatureModules
) {
278 for (auto &Mod
: *FeatureModules
)
280 for (auto &Mod
: *FeatureModules
)
281 Mod
.blockUntilIdle(Deadline::infinity());
285 void ClangdServer::addDocument(PathRef File
, llvm::StringRef Contents
,
286 llvm::StringRef Version
,
287 WantDiagnostics WantDiags
, bool ForceRebuild
) {
288 std::string ActualVersion
= DraftMgr
.addDraft(File
, Version
, Contents
);
290 Opts
.PreambleParseForwardingFunctions
= PreambleParseForwardingFunctions
;
291 Opts
.ImportInsertions
= ImportInsertions
;
293 // Compile command is set asynchronously during update, as it can be slow.
295 Inputs
.TFS
= &getHeaderFS();
296 Inputs
.Contents
= std::string(Contents
);
297 Inputs
.Version
= std::move(ActualVersion
);
298 Inputs
.ForceRebuild
= ForceRebuild
;
299 Inputs
.Opts
= std::move(Opts
);
300 Inputs
.Index
= Index
;
301 Inputs
.ClangTidyProvider
= ClangTidyProvider
;
302 Inputs
.FeatureModules
= FeatureModules
;
303 bool NewFile
= WorkScheduler
->update(File
, Inputs
, WantDiags
);
304 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
305 if (NewFile
&& BackgroundIdx
)
306 BackgroundIdx
->boostRelated(File
);
309 void ClangdServer::reparseOpenFilesIfNeeded(
310 llvm::function_ref
<bool(llvm::StringRef File
)> Filter
) {
311 // Reparse only opened files that were modified.
312 for (const Path
&FilePath
: DraftMgr
.getActiveFiles())
313 if (Filter(FilePath
))
314 if (auto Draft
= DraftMgr
.getDraft(FilePath
)) // else disappeared in race?
315 addDocument(FilePath
, *Draft
->Contents
, Draft
->Version
,
316 WantDiagnostics::Auto
);
319 std::shared_ptr
<const std::string
> ClangdServer::getDraft(PathRef File
) const {
320 auto Draft
= DraftMgr
.getDraft(File
);
323 return std::move(Draft
->Contents
);
326 std::function
<Context(PathRef
)>
327 ClangdServer::createConfiguredContextProvider(const config::Provider
*Provider
,
328 Callbacks
*Publish
) {
330 return [](llvm::StringRef
) { return Context::current().clone(); };
333 const config::Provider
*Provider
;
334 ClangdServer::Callbacks
*Publish
;
335 std::mutex PublishMu
;
337 Impl(const config::Provider
*Provider
, ClangdServer::Callbacks
*Publish
)
338 : Provider(Provider
), Publish(Publish
) {}
340 Context
operator()(llvm::StringRef File
) {
341 config::Params Params
;
342 // Don't reread config files excessively often.
343 // FIXME: when we see a config file change event, use the event timestamp?
345 std::chrono::steady_clock::now() - std::chrono::seconds(5);
346 llvm::SmallString
<256> PosixPath
;
348 assert(llvm::sys::path::is_absolute(File
));
349 llvm::sys::path::native(File
, PosixPath
, llvm::sys::path::Style::posix
);
350 Params
.Path
= PosixPath
.str();
353 llvm::StringMap
<std::vector
<Diag
>> ReportableDiagnostics
;
354 Config C
= Provider
->getConfig(Params
, [&](const llvm::SMDiagnostic
&D
) {
355 // Create the map entry even for note diagnostics we don't report.
356 // This means that when the file is parsed with no warnings, we
357 // publish an empty set of diagnostics, clearing any the client has.
358 handleDiagnostic(D
, !Publish
|| D
.getFilename().empty()
360 : &ReportableDiagnostics
[D
.getFilename()]);
362 // Blindly publish diagnostics for the (unopened) parsed config files.
363 // We must avoid reporting diagnostics for *the same file* concurrently.
364 // Source diags are published elsewhere, but those are different files.
365 if (!ReportableDiagnostics
.empty()) {
366 std::lock_guard
<std::mutex
> Lock(PublishMu
);
367 for (auto &Entry
: ReportableDiagnostics
)
368 Publish
->onDiagnosticsReady(Entry
.first(), /*Version=*/"",
371 return Context::current().derive(Config::Key
, std::move(C
));
374 void handleDiagnostic(const llvm::SMDiagnostic
&D
,
375 std::vector
<Diag
> *ClientDiagnostics
) {
376 switch (D
.getKind()) {
377 case llvm::SourceMgr::DK_Error
:
378 elog("config error at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
379 D
.getColumnNo(), D
.getMessage());
381 case llvm::SourceMgr::DK_Warning
:
382 log("config warning at {0}:{1}:{2}: {3}", D
.getFilename(),
383 D
.getLineNo(), D
.getColumnNo(), D
.getMessage());
385 case llvm::SourceMgr::DK_Note
:
386 case llvm::SourceMgr::DK_Remark
:
387 vlog("config note at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
388 D
.getColumnNo(), D
.getMessage());
389 ClientDiagnostics
= nullptr; // Don't emit notes as LSP diagnostics.
392 if (ClientDiagnostics
)
393 ClientDiagnostics
->push_back(toDiag(D
, Diag::ClangdConfig
));
398 return [I(std::make_shared
<Impl
>(Provider
, Publish
))](llvm::StringRef Path
) {
403 void ClangdServer::removeDocument(PathRef File
) {
404 DraftMgr
.removeDraft(File
);
405 WorkScheduler
->remove(File
);
408 void ClangdServer::codeComplete(PathRef File
, Position Pos
,
409 const clangd::CodeCompleteOptions
&Opts
,
410 Callback
<CodeCompleteResult
> CB
) {
411 // Copy completion options for passing them to async task handler.
412 auto CodeCompleteOpts
= Opts
;
413 if (!CodeCompleteOpts
.Index
) // Respect overridden index.
414 CodeCompleteOpts
.Index
= Index
;
416 auto Task
= [Pos
, CodeCompleteOpts
, File
= File
.str(), CB
= std::move(CB
),
417 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
419 return CB(IP
.takeError());
420 if (auto Reason
= isCancelled())
421 return CB(llvm::make_error
<CancelledError
>(Reason
));
423 std::optional
<SpeculativeFuzzyFind
> SpecFuzzyFind
;
425 // No speculation in Fallback mode, as it's supposed to be much faster
426 // without compiling.
427 vlog("Build for file {0} is not ready. Enter fallback mode.", File
);
428 } else if (CodeCompleteOpts
.Index
) {
429 SpecFuzzyFind
.emplace();
431 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
432 SpecFuzzyFind
->CachedReq
= CachedCompletionFuzzyFindRequestByFile
[File
];
435 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
436 // FIXME: Add traling new line if there is none at eof, workaround a crash,
437 // see https://github.com/clangd/clangd/issues/332
438 if (!IP
->Contents
.endswith("\n"))
439 ParseInput
.Contents
.append("\n");
440 ParseInput
.Index
= Index
;
442 CodeCompleteOpts
.MainFileSignals
= IP
->Signals
;
443 CodeCompleteOpts
.AllScopes
= Config::current().Completion
.AllScopes
;
444 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
445 // both the old and the new version in case only one of them matches.
446 CodeCompleteResult Result
= clangd::codeComplete(
447 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
448 SpecFuzzyFind
? &*SpecFuzzyFind
: nullptr);
450 clang::clangd::trace::Span
Tracer("Completion results callback");
451 CB(std::move(Result
));
453 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
454 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
455 CachedCompletionFuzzyFindRequestByFile
[File
] = *SpecFuzzyFind
->NewReq
;
457 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
458 // We don't want `codeComplete` to wait for the async call if it doesn't use
459 // the result (e.g. non-index completion, speculation fails), so that `CB`
460 // is called as soon as results are available.
463 // We use a potentially-stale preamble because latency is critical here.
464 WorkScheduler
->runWithPreamble(
465 "CodeComplete", File
,
466 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
468 : TUScheduler::StaleOrAbsent
,
472 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
473 MarkupKind DocumentationFormat
,
474 Callback
<SignatureHelp
> CB
) {
476 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
478 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
480 return CB(IP
.takeError());
482 const auto *PreambleData
= IP
->Preamble
;
484 return CB(error("Failed to parse includes"));
486 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
487 // FIXME: Add traling new line if there is none at eof, workaround a crash,
488 // see https://github.com/clangd/clangd/issues/332
489 if (!IP
->Contents
.endswith("\n"))
490 ParseInput
.Contents
.append("\n");
491 ParseInput
.Index
= Index
;
492 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
493 DocumentationFormat
));
496 // Unlike code completion, we wait for a preamble here.
497 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
501 void ClangdServer::formatFile(PathRef File
, std::optional
<Range
> Rng
,
502 Callback
<tooling::Replacements
> CB
) {
503 auto Code
= getDraft(File
);
505 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
506 ErrorCode::InvalidParams
));
507 tooling::Range RequestedRange
;
509 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
511 return CB(Begin
.takeError());
512 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
514 return CB(End
.takeError());
515 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
517 RequestedRange
= tooling::Range(0, Code
->size());
520 // Call clang-format.
521 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
522 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
523 CB
= std::move(CB
), this]() mutable {
524 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
);
525 tooling::Replacements IncludeReplaces
=
526 format::sortIncludes(Style
, Code
, Ranges
, File
);
527 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
529 return CB(Changed
.takeError());
531 CB(IncludeReplaces
.merge(format::reformat(
533 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
536 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
539 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
540 StringRef TriggerText
,
541 Callback
<std::vector
<TextEdit
>> CB
) {
542 auto Code
= getDraft(File
);
544 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
545 ErrorCode::InvalidParams
));
546 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
548 return CB(CursorPos
.takeError());
549 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
550 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
551 CB
= std::move(CB
), this]() mutable {
552 auto Style
= format::getStyle(format::DefaultFormatStyle
, File
,
553 format::DefaultFallbackStyle
, Code
,
554 TFS
.view(/*CWD=*/std::nullopt
).get());
556 return CB(Style
.takeError());
558 std::vector
<TextEdit
> Result
;
559 for (const tooling::Replacement
&R
:
560 formatIncremental(Code
, CursorPos
, TriggerText
, *Style
))
561 Result
.push_back(replacementToEdit(Code
, R
));
564 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
567 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
568 std::optional
<std::string
> NewName
,
569 const RenameOptions
&RenameOpts
,
570 Callback
<RenameResult
> CB
) {
571 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
572 NewName
= std::move(NewName
),
573 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
575 return CB(InpAST
.takeError());
576 // prepareRename is latency-sensitive: we don't query the index, as we
577 // only need main-file references
579 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
580 InpAST
->AST
, File
, /*FS=*/nullptr,
581 /*Index=*/nullptr, RenameOpts
});
583 // LSP says to return null on failure, but that will result in a generic
584 // failure message. If we send an LSP error response, clients can surface
585 // the message to users (VSCode does).
586 return CB(Results
.takeError());
590 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
593 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
594 const RenameOptions
&Opts
,
595 Callback
<RenameResult
> CB
) {
596 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
598 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
599 // Tracks number of files edited per invocation.
600 static constexpr trace::Metric
RenameFiles("rename_files",
601 trace::Metric::Distribution
);
603 return CB(InpAST
.takeError());
604 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
605 DirtyFS
->view(std::nullopt
), Index
, Opts
});
607 return CB(R
.takeError());
609 if (Opts
.WantFormat
) {
610 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
611 *InpAST
->Inputs
.TFS
);
612 llvm::Error Err
= llvm::Error::success();
613 for (auto &E
: R
->GlobalChanges
)
615 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
618 return CB(std::move(Err
));
620 RenameFiles
.record(R
->GlobalChanges
.size());
623 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
626 // May generate several candidate selections, due to SelectionTree ambiguity.
627 // vector of pointers because GCC doesn't like non-copyable Selection.
628 static llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
629 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
630 llvm::vfs::FileSystem
*FS
) {
631 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
633 return Begin
.takeError();
634 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
636 return End
.takeError();
637 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
638 SelectionTree::createEach(
639 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
640 [&](SelectionTree T
) {
641 Result
.push_back(std::make_unique
<Tweak::Selection
>(
642 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
645 assert(!Result
.empty() && "Expected at least one SelectionTree");
646 return std::move(Result
);
649 void ClangdServer::codeAction(const CodeActionInputs
&Params
,
650 Callback
<CodeActionResult
> CB
) {
651 auto Action
= [Params
, CB
= std::move(CB
),
652 FeatureModules(this->FeatureModules
)](
653 Expected
<InputsAndAST
> InpAST
) mutable {
655 return CB(InpAST
.takeError());
657 [Only(Params
.RequestedActionKinds
)](llvm::StringRef Kind
) {
660 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
661 return Kind
.consume_front(Base
) &&
662 (Kind
.empty() || Kind
.startswith("."));
666 CodeActionResult Result
;
667 Result
.Version
= InpAST
->AST
.version().str();
668 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
669 auto FindMatchedFixes
=
670 [&InpAST
](const DiagRef
&DR
) -> llvm::ArrayRef
<Fix
> {
671 for (const auto &Diag
: InpAST
->AST
.getDiagnostics())
672 if (Diag
.Range
== DR
.Range
&& Diag
.Message
== DR
.Message
)
676 for (const auto &Diag
: Params
.Diagnostics
)
677 for (const auto &Fix
: FindMatchedFixes(Diag
))
678 Result
.QuickFixes
.push_back({Diag
, Fix
});
682 auto Selections
= tweakSelection(Params
.Selection
, *InpAST
, /*FS=*/nullptr);
684 return CB(Selections
.takeError());
685 // Don't allow a tweak to fire more than once across ambiguous selections.
686 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
687 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
688 return KindAllowed(T
.kind()) && Params
.TweakFilter(T
) &&
689 !PreparedTweaks
.count(T
.id());
691 for (const auto &Sel
: *Selections
) {
692 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
693 Result
.TweakRefs
.push_back(TweakRef
{T
->id(), T
->title(), T
->kind()});
694 PreparedTweaks
.insert(T
->id());
695 TweakAvailable
.record(1, T
->id());
698 CB(std::move(Result
));
701 WorkScheduler
->runWithAST("codeAction", Params
.File
, std::move(Action
),
705 void ClangdServer::enumerateTweaks(
706 PathRef File
, Range Sel
, llvm::unique_function
<bool(const Tweak
&)> Filter
,
707 Callback
<std::vector
<TweakRef
>> CB
) {
708 auto Action
= [Sel
, CB
= std::move(CB
), Filter
= std::move(Filter
),
709 FeatureModules(this->FeatureModules
)](
710 Expected
<InputsAndAST
> InpAST
) mutable {
712 return CB(InpAST
.takeError());
713 auto Selections
= tweakSelection(Sel
, *InpAST
, /*FS=*/nullptr);
715 return CB(Selections
.takeError());
716 std::vector
<TweakRef
> Res
;
717 // Don't allow a tweak to fire more than once across ambiguous selections.
718 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
719 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
720 return Filter(T
) && !PreparedTweaks
.count(T
.id());
722 for (const auto &Sel
: *Selections
) {
723 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
724 Res
.push_back({T
->id(), T
->title(), T
->kind()});
725 PreparedTweaks
.insert(T
->id());
726 TweakAvailable
.record(1, T
->id());
733 WorkScheduler
->runWithAST("EnumerateTweaks", File
, std::move(Action
),
737 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
738 Callback
<Tweak::Effect
> CB
) {
739 // Tracks number of times a tweak has been attempted.
740 static constexpr trace::Metric
TweakAttempt(
741 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
742 // Tracks number of times a tweak has failed to produce edits.
743 static constexpr trace::Metric
TweakFailed(
744 "tweak_failed", trace::Metric::Counter
, "tweak_id");
745 TweakAttempt
.record(1, TweakID
);
746 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
748 this](Expected
<InputsAndAST
> InpAST
) mutable {
750 return CB(InpAST
.takeError());
751 auto FS
= DirtyFS
->view(std::nullopt
);
752 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
754 return CB(Selections
.takeError());
755 std::optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
756 // Try each selection, take the first one that prepare()s.
757 // If they all fail, Effect will hold get the last error.
758 for (const auto &Selection
: *Selections
) {
759 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
761 Effect
= (*T
)->apply(*Selection
);
764 Effect
= T
.takeError();
766 assert(Effect
&& "Expected at least one selection");
767 if (*Effect
&& (*Effect
)->FormatEdits
) {
768 // Format tweaks that require it centrally here.
769 for (auto &It
: (*Effect
)->ApplyEdits
) {
771 format::FormatStyle Style
=
772 getFormatStyleForFile(File
, E
.InitialCode
, TFS
);
773 if (llvm::Error Err
= reformatEdit(E
, Style
))
774 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
777 TweakFailed
.record(1, TweakID
);
779 return CB(std::move(*Effect
));
781 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
784 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
785 Callback
<std::vector
<LocatedSymbol
>> CB
) {
786 auto Action
= [Pos
, CB
= std::move(CB
),
787 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
789 return CB(InpAST
.takeError());
790 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
793 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
796 void ClangdServer::switchSourceHeader(
797 PathRef Path
, Callback
<std::optional
<clangd::Path
>> CB
) {
798 // We want to return the result as fast as possible, strategy is:
799 // 1) use the file-only heuristic, it requires some IO but it is much
800 // faster than building AST, but it only works when .h/.cc files are in
801 // the same directory.
802 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
803 // different code layout.
804 if (auto CorrespondingFile
=
805 getCorrespondingHeaderOrSource(Path
, TFS
.view(std::nullopt
)))
806 return CB(std::move(CorrespondingFile
));
807 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
808 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
810 return CB(InpAST
.takeError());
811 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
813 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
816 void ClangdServer::findDocumentHighlights(
817 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
819 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
821 return CB(InpAST
.takeError());
822 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
825 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
828 void ClangdServer::findHover(PathRef File
, Position Pos
,
829 Callback
<std::optional
<HoverInfo
>> CB
) {
830 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
831 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
833 return CB(InpAST
.takeError());
834 format::FormatStyle Style
= getFormatStyleForFile(
835 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
);
836 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
839 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
842 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
843 TypeHierarchyDirection Direction
,
844 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
845 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
846 this](Expected
<InputsAndAST
> InpAST
) mutable {
848 return CB(InpAST
.takeError());
849 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
853 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
856 void ClangdServer::superTypes(
857 const TypeHierarchyItem
&Item
,
858 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> CB
) {
859 WorkScheduler
->run("typeHierarchy/superTypes", /*Path=*/"",
860 [=, CB
= std::move(CB
)]() mutable {
861 CB(clangd::superTypes(Item
, Index
));
865 void ClangdServer::subTypes(const TypeHierarchyItem
&Item
,
866 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
868 "typeHierarchy/subTypes", /*Path=*/"",
869 [=, CB
= std::move(CB
)]() mutable { CB(clangd::subTypes(Item
, Index
)); });
872 void ClangdServer::resolveTypeHierarchy(
873 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
874 Callback
<std::optional
<TypeHierarchyItem
>> CB
) {
876 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
877 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
882 void ClangdServer::prepareCallHierarchy(
883 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
884 auto Action
= [File
= File
.str(), Pos
,
885 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
887 return CB(InpAST
.takeError());
888 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
890 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
893 void ClangdServer::incomingCalls(
894 const CallHierarchyItem
&Item
,
895 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
896 WorkScheduler
->run("Incoming Calls", "",
897 [CB
= std::move(CB
), Item
, this]() mutable {
898 CB(clangd::incomingCalls(Item
, Index
));
902 void ClangdServer::inlayHints(PathRef File
, std::optional
<Range
> RestrictRange
,
903 Callback
<std::vector
<InlayHint
>> CB
) {
904 auto Action
= [RestrictRange(std::move(RestrictRange
)),
905 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
907 return CB(InpAST
.takeError());
908 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
910 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
913 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
914 // FIXME: Do nothing for now. This will be used for indexing and potentially
915 // invalidating other caches.
918 void ClangdServer::workspaceSymbols(
919 llvm::StringRef Query
, int Limit
,
920 Callback
<std::vector
<SymbolInformation
>> CB
) {
922 "getWorkspaceSymbols", /*Path=*/"",
923 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
924 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
925 WorkspaceRoot
.value_or("")));
929 void ClangdServer::documentSymbols(llvm::StringRef File
,
930 Callback
<std::vector
<DocumentSymbol
>> CB
) {
932 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
934 return CB(InpAST
.takeError());
935 CB(clangd::getDocumentSymbols(InpAST
->AST
));
937 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
941 void ClangdServer::foldingRanges(llvm::StringRef File
,
942 Callback
<std::vector
<FoldingRange
>> CB
) {
943 auto Code
= getDraft(File
);
945 return CB(llvm::make_error
<LSPError
>(
946 "trying to compute folding ranges for non-added document",
947 ErrorCode::InvalidParams
));
948 auto Action
= [LineFoldingOnly
= LineFoldingOnly
, CB
= std::move(CB
),
949 Code
= std::move(*Code
)]() mutable {
950 CB(clangd::getFoldingRanges(Code
, LineFoldingOnly
));
952 // We want to make sure folding ranges are always available for all the open
953 // files, hence prefer runQuick to not wait for operations on other files.
954 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
957 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
958 Callback
<std::vector
<LocatedSymbol
>> CB
) {
959 auto Action
= [Pos
, CB
= std::move(CB
),
960 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
962 return CB(InpAST
.takeError());
963 CB(clangd::findType(InpAST
->AST
, Pos
, Index
));
965 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
968 void ClangdServer::findImplementations(
969 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
970 auto Action
= [Pos
, CB
= std::move(CB
),
971 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
973 return CB(InpAST
.takeError());
974 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
977 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
980 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
982 Callback
<ReferencesResult
> CB
) {
983 auto Action
= [Pos
, Limit
, AddContainer
, CB
= std::move(CB
),
984 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
986 return CB(InpAST
.takeError());
987 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
, AddContainer
));
990 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
993 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
994 Callback
<std::vector
<SymbolDetails
>> CB
) {
996 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
998 return CB(InpAST
.takeError());
999 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
1002 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
1005 void ClangdServer::semanticRanges(PathRef File
,
1006 const std::vector
<Position
> &Positions
,
1007 Callback
<std::vector
<SelectionRange
>> CB
) {
1008 auto Action
= [Positions
, CB
= std::move(CB
)](
1009 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1011 return CB(InpAST
.takeError());
1012 std::vector
<SelectionRange
> Result
;
1013 for (const auto &Pos
: Positions
) {
1014 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
1015 Result
.push_back(std::move(*Range
));
1017 return CB(Range
.takeError());
1019 CB(std::move(Result
));
1021 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
1024 void ClangdServer::documentLinks(PathRef File
,
1025 Callback
<std::vector
<DocumentLink
>> CB
) {
1027 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1029 return CB(InpAST
.takeError());
1030 CB(clangd::getDocumentLinks(InpAST
->AST
));
1032 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
1036 void ClangdServer::semanticHighlights(
1037 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
1039 auto Action
= [CB
= std::move(CB
),
1040 PublishInactiveRegions
= PublishInactiveRegions
](
1041 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1043 return CB(InpAST
.takeError());
1044 // Include inactive regions in semantic highlighting tokens only if the
1045 // client doesn't support a dedicated protocol for being informed about
1047 CB(clangd::getSemanticHighlightings(InpAST
->AST
, !PublishInactiveRegions
));
1049 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
1053 void ClangdServer::getAST(PathRef File
, std::optional
<Range
> R
,
1054 Callback
<std::optional
<ASTNode
>> CB
) {
1056 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
1058 return CB(Inputs
.takeError());
1060 // It's safe to pass in the TU, as dumpAST() does not
1061 // deserialize the preamble.
1062 auto Node
= DynTypedNode::create(
1063 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
1064 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
1065 Inputs
->AST
.getASTContext()));
1067 unsigned Start
, End
;
1068 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
1071 return CB(Offset
.takeError());
1072 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
1075 return CB(Offset
.takeError());
1076 bool Success
= SelectionTree::createEach(
1077 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
1078 [&](SelectionTree T
) {
1079 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
1080 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
1081 Inputs
->AST
.getASTContext()));
1089 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
1092 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
1093 Callback
<InputsAndAST
> Action
) {
1094 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
1097 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
1099 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1101 return CB(InpAST
.takeError());
1102 return CB(InpAST
->AST
.getDiagnostics());
1105 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
1108 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1109 return WorkScheduler
->fileStats();
1113 ClangdServer::blockUntilIdleForTest(std::optional
<double> TimeoutSeconds
) {
1114 // Order is important here: we don't want to block on A and then B,
1115 // if B might schedule work on A.
1117 #if defined(__has_feature) && \
1118 (__has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) || \
1119 __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer))
1120 if (TimeoutSeconds
.has_value())
1121 (*TimeoutSeconds
) *= 10;
1124 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1125 // and we're blocking the main thread.
1126 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1128 // TUScheduler is the only thing that starts background indexing work.
1129 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1132 // Unfortunately we don't have strict topological order between the rest of
1133 // the components. E.g. CDB broadcast triggers backrgound indexing.
1134 // This queries the CDB which may discover new work if disk has changed.
1136 // So try each one a few times in a loop.
1137 // If there are no tricky interactions then all after the first are no-ops.
1138 // Then on the last iteration, verify they're idle without waiting.
1140 // There's a small chance they're juggling work and we didn't catch them :-(
1141 for (std::optional
<double> Timeout
:
1142 {TimeoutSeconds
, TimeoutSeconds
, std::optional
<double>(0)}) {
1143 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1145 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1147 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1148 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1153 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1154 "Something scheduled work while we're blocking the main thread!");
1158 void ClangdServer::profile(MemoryTree
&MT
) const {
1160 DynamicIdx
->profile(MT
.child("dynamic_index"));
1162 BackgroundIdx
->profile(MT
.child("background_index"));
1163 WorkScheduler
->profile(MT
.child("tuscheduler"));
1165 } // namespace clangd
1166 } // namespace clang