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 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
455 // both the old and the new version in case only one of them matches.
456 CodeCompleteResult Result
= clangd::codeComplete(
457 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
458 SpecFuzzyFind
? &*SpecFuzzyFind
: nullptr);
460 clang::clangd::trace::Span
Tracer("Completion results callback");
461 CB(std::move(Result
));
463 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
464 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
465 CachedCompletionFuzzyFindRequestByFile
[File
] = *SpecFuzzyFind
->NewReq
;
467 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
468 // We don't want `codeComplete` to wait for the async call if it doesn't use
469 // the result (e.g. non-index completion, speculation fails), so that `CB`
470 // is called as soon as results are available.
473 // We use a potentially-stale preamble because latency is critical here.
474 WorkScheduler
->runWithPreamble(
475 "CodeComplete", File
,
476 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
478 : TUScheduler::StaleOrAbsent
,
482 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
483 MarkupKind DocumentationFormat
,
484 Callback
<SignatureHelp
> CB
) {
486 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
488 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
490 return CB(IP
.takeError());
492 const auto *PreambleData
= IP
->Preamble
;
494 return CB(error("Failed to parse includes"));
496 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
497 // FIXME: Add traling new line if there is none at eof, workaround a crash,
498 // see https://github.com/clangd/clangd/issues/332
499 if (!IP
->Contents
.ends_with("\n"))
500 ParseInput
.Contents
.append("\n");
501 ParseInput
.Index
= Index
;
502 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
503 DocumentationFormat
));
506 // Unlike code completion, we wait for a preamble here.
507 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
511 void ClangdServer::formatFile(PathRef File
, std::optional
<Range
> Rng
,
512 Callback
<tooling::Replacements
> CB
) {
513 auto Code
= getDraft(File
);
515 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
516 ErrorCode::InvalidParams
));
517 tooling::Range RequestedRange
;
519 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
521 return CB(Begin
.takeError());
522 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
524 return CB(End
.takeError());
525 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
527 RequestedRange
= tooling::Range(0, Code
->size());
530 // Call clang-format.
531 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
532 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
533 CB
= std::move(CB
), this]() mutable {
534 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
, true);
535 tooling::Replacements IncludeReplaces
=
536 format::sortIncludes(Style
, Code
, Ranges
, File
);
537 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
539 return CB(Changed
.takeError());
541 CB(IncludeReplaces
.merge(format::reformat(
543 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
546 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
549 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
550 StringRef TriggerText
,
551 Callback
<std::vector
<TextEdit
>> CB
) {
552 auto Code
= getDraft(File
);
554 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
555 ErrorCode::InvalidParams
));
556 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
558 return CB(CursorPos
.takeError());
559 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
560 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
561 CB
= std::move(CB
), this]() mutable {
562 auto Style
= getFormatStyleForFile(File
, Code
, TFS
, false);
563 std::vector
<TextEdit
> Result
;
564 for (const tooling::Replacement
&R
:
565 formatIncremental(Code
, CursorPos
, TriggerText
, Style
))
566 Result
.push_back(replacementToEdit(Code
, R
));
569 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
572 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
573 std::optional
<std::string
> NewName
,
574 const RenameOptions
&RenameOpts
,
575 Callback
<RenameResult
> CB
) {
576 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
577 NewName
= std::move(NewName
),
578 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
580 return CB(InpAST
.takeError());
581 // prepareRename is latency-sensitive: we don't query the index, as we
582 // only need main-file references
584 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
585 InpAST
->AST
, File
, /*FS=*/nullptr,
586 /*Index=*/nullptr, RenameOpts
});
588 // LSP says to return null on failure, but that will result in a generic
589 // failure message. If we send an LSP error response, clients can surface
590 // the message to users (VSCode does).
591 return CB(Results
.takeError());
595 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
598 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
599 const RenameOptions
&Opts
,
600 Callback
<RenameResult
> CB
) {
601 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
603 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
604 // Tracks number of files edited per invocation.
605 static constexpr trace::Metric
RenameFiles("rename_files",
606 trace::Metric::Distribution
);
608 return CB(InpAST
.takeError());
609 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
610 DirtyFS
->view(std::nullopt
), Index
, Opts
});
612 return CB(R
.takeError());
614 if (Opts
.WantFormat
) {
615 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
616 *InpAST
->Inputs
.TFS
, false);
617 llvm::Error Err
= llvm::Error::success();
618 for (auto &E
: R
->GlobalChanges
)
620 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
623 return CB(std::move(Err
));
625 RenameFiles
.record(R
->GlobalChanges
.size());
628 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
632 // May generate several candidate selections, due to SelectionTree ambiguity.
633 // vector of pointers because GCC doesn't like non-copyable Selection.
634 llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
635 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
636 llvm::vfs::FileSystem
*FS
) {
637 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
639 return Begin
.takeError();
640 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
642 return End
.takeError();
643 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
644 SelectionTree::createEach(
645 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
646 [&](SelectionTree T
) {
647 Result
.push_back(std::make_unique
<Tweak::Selection
>(
648 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
651 assert(!Result
.empty() && "Expected at least one SelectionTree");
652 return std::move(Result
);
655 // Some fixes may perform local renaming, we want to convert those to clangd
656 // rename commands, such that we can leverage the index for more accurate
658 std::optional
<ClangdServer::CodeActionResult::Rename
>
659 tryConvertToRename(const Diag
*Diag
, const Fix
&Fix
) {
660 bool IsClangTidyRename
= Diag
->Source
== Diag::ClangTidy
&&
661 Diag
->Name
== "readability-identifier-naming" &&
663 if (IsClangTidyRename
&& Diag
->InsideMainFile
) {
664 ClangdServer::CodeActionResult::Rename R
;
665 R
.NewName
= Fix
.Edits
.front().newText
;
666 R
.FixMessage
= Fix
.Message
;
667 R
.Diag
= {Diag
->Range
, Diag
->Message
};
676 void ClangdServer::codeAction(const CodeActionInputs
&Params
,
677 Callback
<CodeActionResult
> CB
) {
678 auto Action
= [Params
, CB
= std::move(CB
),
679 FeatureModules(this->FeatureModules
)](
680 Expected
<InputsAndAST
> InpAST
) mutable {
682 return CB(InpAST
.takeError());
684 [Only(Params
.RequestedActionKinds
)](llvm::StringRef Kind
) {
687 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
688 return Kind
.consume_front(Base
) &&
689 (Kind
.empty() || Kind
.starts_with("."));
693 CodeActionResult Result
;
694 Result
.Version
= InpAST
->AST
.version().str();
695 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
696 auto FindMatchedDiag
= [&InpAST
](const DiagRef
&DR
) -> const Diag
* {
697 for (const auto &Diag
: InpAST
->AST
.getDiagnostics())
698 if (Diag
.Range
== DR
.Range
&& Diag
.Message
== DR
.Message
)
702 for (const auto &DiagRef
: Params
.Diagnostics
) {
703 if (const auto *Diag
= FindMatchedDiag(DiagRef
))
704 for (const auto &Fix
: Diag
->Fixes
) {
705 if (auto Rename
= tryConvertToRename(Diag
, Fix
)) {
706 Result
.Renames
.emplace_back(std::move(*Rename
));
708 Result
.QuickFixes
.push_back({DiagRef
, Fix
});
715 auto Selections
= tweakSelection(Params
.Selection
, *InpAST
, /*FS=*/nullptr);
717 return CB(Selections
.takeError());
718 // Don't allow a tweak to fire more than once across ambiguous selections.
719 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
720 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
721 return KindAllowed(T
.kind()) && Params
.TweakFilter(T
) &&
722 !PreparedTweaks
.count(T
.id());
724 for (const auto &Sel
: *Selections
) {
725 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
726 Result
.TweakRefs
.push_back(TweakRef
{T
->id(), T
->title(), T
->kind()});
727 PreparedTweaks
.insert(T
->id());
728 TweakAvailable
.record(1, T
->id());
731 CB(std::move(Result
));
734 WorkScheduler
->runWithAST("codeAction", Params
.File
, std::move(Action
),
738 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
739 Callback
<Tweak::Effect
> CB
) {
740 // Tracks number of times a tweak has been attempted.
741 static constexpr trace::Metric
TweakAttempt(
742 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
743 // Tracks number of times a tweak has failed to produce edits.
744 static constexpr trace::Metric
TweakFailed(
745 "tweak_failed", trace::Metric::Counter
, "tweak_id");
746 TweakAttempt
.record(1, TweakID
);
747 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
749 this](Expected
<InputsAndAST
> InpAST
) mutable {
751 return CB(InpAST
.takeError());
752 auto FS
= DirtyFS
->view(std::nullopt
);
753 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
755 return CB(Selections
.takeError());
756 std::optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
757 // Try each selection, take the first one that prepare()s.
758 // If they all fail, Effect will hold get the last error.
759 for (const auto &Selection
: *Selections
) {
760 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
762 Effect
= (*T
)->apply(*Selection
);
765 Effect
= T
.takeError();
767 assert(Effect
&& "Expected at least one selection");
768 if (*Effect
&& (*Effect
)->FormatEdits
) {
769 // Format tweaks that require it centrally here.
770 for (auto &It
: (*Effect
)->ApplyEdits
) {
772 format::FormatStyle Style
=
773 getFormatStyleForFile(File
, E
.InitialCode
, TFS
, false);
774 if (llvm::Error Err
= reformatEdit(E
, Style
))
775 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
778 TweakFailed
.record(1, TweakID
);
780 return CB(std::move(*Effect
));
782 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
785 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
786 Callback
<std::vector
<LocatedSymbol
>> CB
) {
787 auto Action
= [Pos
, CB
= std::move(CB
),
788 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
790 return CB(InpAST
.takeError());
791 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
794 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
797 void ClangdServer::switchSourceHeader(
798 PathRef Path
, Callback
<std::optional
<clangd::Path
>> CB
) {
799 // We want to return the result as fast as possible, strategy is:
800 // 1) use the file-only heuristic, it requires some IO but it is much
801 // faster than building AST, but it only works when .h/.cc files are in
802 // the same directory.
803 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
804 // different code layout.
805 if (auto CorrespondingFile
=
806 getCorrespondingHeaderOrSource(Path
, TFS
.view(std::nullopt
)))
807 return CB(std::move(CorrespondingFile
));
808 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
809 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
811 return CB(InpAST
.takeError());
812 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
814 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
817 void ClangdServer::findDocumentHighlights(
818 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
820 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
822 return CB(InpAST
.takeError());
823 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
826 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
829 void ClangdServer::findHover(PathRef File
, Position Pos
,
830 Callback
<std::optional
<HoverInfo
>> CB
) {
831 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
832 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
834 return CB(InpAST
.takeError());
835 format::FormatStyle Style
= getFormatStyleForFile(
836 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
, false);
837 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
840 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
843 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
844 TypeHierarchyDirection Direction
,
845 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
846 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
847 this](Expected
<InputsAndAST
> InpAST
) mutable {
849 return CB(InpAST
.takeError());
850 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
854 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
857 void ClangdServer::superTypes(
858 const TypeHierarchyItem
&Item
,
859 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> CB
) {
860 WorkScheduler
->run("typeHierarchy/superTypes", /*Path=*/"",
861 [=, CB
= std::move(CB
)]() mutable {
862 CB(clangd::superTypes(Item
, Index
));
866 void ClangdServer::subTypes(const TypeHierarchyItem
&Item
,
867 Callback
<std::vector
<TypeHierarchyItem
>> CB
) {
869 "typeHierarchy/subTypes", /*Path=*/"",
870 [=, CB
= std::move(CB
)]() mutable { CB(clangd::subTypes(Item
, Index
)); });
873 void ClangdServer::resolveTypeHierarchy(
874 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
875 Callback
<std::optional
<TypeHierarchyItem
>> CB
) {
877 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
878 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
883 void ClangdServer::prepareCallHierarchy(
884 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
885 auto Action
= [File
= File
.str(), Pos
,
886 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
888 return CB(InpAST
.takeError());
889 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
891 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
894 void ClangdServer::incomingCalls(
895 const CallHierarchyItem
&Item
,
896 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
897 WorkScheduler
->run("Incoming Calls", "",
898 [CB
= std::move(CB
), Item
, this]() mutable {
899 CB(clangd::incomingCalls(Item
, Index
));
903 void ClangdServer::inlayHints(PathRef File
, std::optional
<Range
> RestrictRange
,
904 Callback
<std::vector
<InlayHint
>> CB
) {
905 auto Action
= [RestrictRange(std::move(RestrictRange
)),
906 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
908 return CB(InpAST
.takeError());
909 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
911 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
914 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
915 // FIXME: Do nothing for now. This will be used for indexing and potentially
916 // invalidating other caches.
919 void ClangdServer::workspaceSymbols(
920 llvm::StringRef Query
, int Limit
,
921 Callback
<std::vector
<SymbolInformation
>> CB
) {
923 "getWorkspaceSymbols", /*Path=*/"",
924 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
925 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
926 WorkspaceRoot
.value_or("")));
930 void ClangdServer::documentSymbols(llvm::StringRef File
,
931 Callback
<std::vector
<DocumentSymbol
>> CB
) {
933 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
935 return CB(InpAST
.takeError());
936 CB(clangd::getDocumentSymbols(InpAST
->AST
));
938 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
942 void ClangdServer::foldingRanges(llvm::StringRef File
,
943 Callback
<std::vector
<FoldingRange
>> CB
) {
944 auto Code
= getDraft(File
);
946 return CB(llvm::make_error
<LSPError
>(
947 "trying to compute folding ranges for non-added document",
948 ErrorCode::InvalidParams
));
949 auto Action
= [LineFoldingOnly
= LineFoldingOnly
, CB
= std::move(CB
),
950 Code
= std::move(*Code
)]() mutable {
951 CB(clangd::getFoldingRanges(Code
, LineFoldingOnly
));
953 // We want to make sure folding ranges are always available for all the open
954 // files, hence prefer runQuick to not wait for operations on other files.
955 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
958 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
959 Callback
<std::vector
<LocatedSymbol
>> CB
) {
960 auto Action
= [Pos
, CB
= std::move(CB
),
961 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
963 return CB(InpAST
.takeError());
964 CB(clangd::findType(InpAST
->AST
, Pos
, Index
));
966 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
969 void ClangdServer::findImplementations(
970 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
971 auto Action
= [Pos
, CB
= std::move(CB
),
972 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
974 return CB(InpAST
.takeError());
975 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
978 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
981 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
983 Callback
<ReferencesResult
> CB
) {
984 auto Action
= [Pos
, Limit
, AddContainer
, CB
= std::move(CB
),
985 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
987 return CB(InpAST
.takeError());
988 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
, AddContainer
));
991 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
994 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
995 Callback
<std::vector
<SymbolDetails
>> CB
) {
997 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
999 return CB(InpAST
.takeError());
1000 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
1003 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
1006 void ClangdServer::semanticRanges(PathRef File
,
1007 const std::vector
<Position
> &Positions
,
1008 Callback
<std::vector
<SelectionRange
>> CB
) {
1009 auto Action
= [Positions
, CB
= std::move(CB
)](
1010 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1012 return CB(InpAST
.takeError());
1013 std::vector
<SelectionRange
> Result
;
1014 for (const auto &Pos
: Positions
) {
1015 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
1016 Result
.push_back(std::move(*Range
));
1018 return CB(Range
.takeError());
1020 CB(std::move(Result
));
1022 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
1025 void ClangdServer::documentLinks(PathRef File
,
1026 Callback
<std::vector
<DocumentLink
>> CB
) {
1028 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1030 return CB(InpAST
.takeError());
1031 CB(clangd::getDocumentLinks(InpAST
->AST
));
1033 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
1037 void ClangdServer::semanticHighlights(
1038 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
1040 auto Action
= [CB
= std::move(CB
),
1041 PublishInactiveRegions
= PublishInactiveRegions
](
1042 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1044 return CB(InpAST
.takeError());
1045 // Include inactive regions in semantic highlighting tokens only if the
1046 // client doesn't support a dedicated protocol for being informed about
1048 CB(clangd::getSemanticHighlightings(InpAST
->AST
, !PublishInactiveRegions
));
1050 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
1054 void ClangdServer::getAST(PathRef File
, std::optional
<Range
> R
,
1055 Callback
<std::optional
<ASTNode
>> CB
) {
1057 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
1059 return CB(Inputs
.takeError());
1061 // It's safe to pass in the TU, as dumpAST() does not
1062 // deserialize the preamble.
1063 auto Node
= DynTypedNode::create(
1064 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
1065 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
1066 Inputs
->AST
.getASTContext()));
1068 unsigned Start
, End
;
1069 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
1072 return CB(Offset
.takeError());
1073 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
1076 return CB(Offset
.takeError());
1077 bool Success
= SelectionTree::createEach(
1078 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
1079 [&](SelectionTree T
) {
1080 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
1081 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
1082 Inputs
->AST
.getASTContext()));
1090 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
1093 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
1094 Callback
<InputsAndAST
> Action
) {
1095 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
1098 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
1100 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
1102 return CB(InpAST
.takeError());
1103 return CB(InpAST
->AST
.getDiagnostics());
1106 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
1109 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1110 return WorkScheduler
->fileStats();
1114 ClangdServer::blockUntilIdleForTest(std::optional
<double> TimeoutSeconds
) {
1115 // Order is important here: we don't want to block on A and then B,
1116 // if B might schedule work on A.
1118 #if defined(__has_feature) && \
1119 (__has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer) || \
1120 __has_feature(memory_sanitizer) || __has_feature(thread_sanitizer))
1121 if (TimeoutSeconds
.has_value())
1122 (*TimeoutSeconds
) *= 10;
1125 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1126 // and we're blocking the main thread.
1127 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1129 // TUScheduler is the only thing that starts background indexing work.
1130 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1133 // Unfortunately we don't have strict topological order between the rest of
1134 // the components. E.g. CDB broadcast triggers backrgound indexing.
1135 // This queries the CDB which may discover new work if disk has changed.
1137 // So try each one a few times in a loop.
1138 // If there are no tricky interactions then all after the first are no-ops.
1139 // Then on the last iteration, verify they're idle without waiting.
1141 // There's a small chance they're juggling work and we didn't catch them :-(
1142 for (std::optional
<double> Timeout
:
1143 {TimeoutSeconds
, TimeoutSeconds
, std::optional
<double>(0)}) {
1144 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1146 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1148 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1149 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1154 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1155 "Something scheduled work while we're blocking the main thread!");
1159 void ClangdServer::profile(MemoryTree
&MT
) const {
1161 DynamicIdx
->profile(MT
.child("dynamic_index"));
1163 BackgroundIdx
->profile(MT
.child("background_index"));
1164 WorkScheduler
->profile(MT
.child("tuscheduler"));
1166 } // namespace clangd
1167 } // namespace clang