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 "index/CanonicalIncludes.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/Optional.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"
54 #include <type_traits>
60 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
61 struct UpdateIndexCallbacks
: public ParsingCallbacks
{
62 UpdateIndexCallbacks(FileIndex
*FIndex
,
63 ClangdServer::Callbacks
*ServerCallbacks
,
64 const ThreadsafeFS
&TFS
, AsyncTaskRunner
*Tasks
)
65 : FIndex(FIndex
), ServerCallbacks(ServerCallbacks
), TFS(TFS
),
68 void onPreambleAST(PathRef Path
, llvm::StringRef Version
,
69 const CompilerInvocation
&CI
, ASTContext
&Ctx
,
71 const CanonicalIncludes
&CanonIncludes
) override
{
72 // If this preamble uses a standard library we haven't seen yet, index it.
74 if (auto Loc
= Stdlib
.add(*CI
.getLangOpts(), PP
.getHeaderSearchInfo()))
75 indexStdlib(CI
, std::move(*Loc
));
78 FIndex
->updatePreamble(Path
, Version
, Ctx
, PP
, CanonIncludes
);
81 void indexStdlib(const CompilerInvocation
&CI
, StdLibLocation Loc
) {
82 auto Task
= [this, LO(*CI
.getLangOpts()), Loc(std::move(Loc
)),
83 CI(std::make_unique
<CompilerInvocation
>(CI
))]() mutable {
85 IF
.Symbols
= indexStandardLibrary(std::move(CI
), Loc
, TFS
);
86 if (Stdlib
.isBest(LO
))
87 FIndex
->updatePreamble(std::move(IF
));
90 // This doesn't have a semaphore to enforce -j, but it's rare.
91 Tasks
->runAsync("IndexStdlib", std::move(Task
));
96 void onMainAST(PathRef Path
, ParsedAST
&AST
, PublishFn Publish
) override
{
98 FIndex
->updateMain(Path
, AST
);
100 assert(AST
.getDiagnostics() &&
101 "We issue callback only with fresh preambles");
102 std::vector
<Diag
> Diagnostics
= *AST
.getDiagnostics();
105 ServerCallbacks
->onDiagnosticsReady(Path
, AST
.version(),
106 std::move(Diagnostics
));
110 void onFailedAST(PathRef Path
, llvm::StringRef Version
,
111 std::vector
<Diag
> Diags
, PublishFn Publish
) override
{
114 [&]() { ServerCallbacks
->onDiagnosticsReady(Path
, Version
, Diags
); });
117 void onFileUpdated(PathRef File
, const TUStatus
&Status
) override
{
119 ServerCallbacks
->onFileUpdated(File
, Status
);
122 void onPreamblePublished(PathRef File
) override
{
124 ServerCallbacks
->onSemanticsMaybeChanged(File
);
129 ClangdServer::Callbacks
*ServerCallbacks
;
130 const ThreadsafeFS
&TFS
;
132 AsyncTaskRunner
*Tasks
;
135 class DraftStoreFS
: public ThreadsafeFS
{
137 DraftStoreFS(const ThreadsafeFS
&Base
, const DraftStore
&Drafts
)
138 : Base(Base
), DirtyFiles(Drafts
) {}
141 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> viewImpl() const override
{
142 auto OFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(
143 Base
.view(llvm::None
));
144 OFS
->pushOverlay(DirtyFiles
.asVFS());
148 const ThreadsafeFS
&Base
;
149 const DraftStore
&DirtyFiles
;
154 ClangdServer::Options
ClangdServer::optsForTest() {
155 ClangdServer::Options Opts
;
156 Opts
.UpdateDebounce
= DebouncePolicy::fixed(/*zero*/ {});
157 Opts
.StorePreamblesInMemory
= true;
158 Opts
.AsyncThreadsCount
= 4; // Consistent!
162 ClangdServer::Options::operator TUScheduler::Options() const {
163 TUScheduler::Options Opts
;
164 Opts
.AsyncThreadsCount
= AsyncThreadsCount
;
165 Opts
.RetentionPolicy
= RetentionPolicy
;
166 Opts
.StorePreamblesInMemory
= StorePreamblesInMemory
;
167 Opts
.UpdateDebounce
= UpdateDebounce
;
168 Opts
.ContextProvider
= ContextProvider
;
169 Opts
.PreambleThrottler
= PreambleThrottler
;
173 ClangdServer::ClangdServer(const GlobalCompilationDatabase
&CDB
,
174 const ThreadsafeFS
&TFS
, const Options
&Opts
,
175 Callbacks
*Callbacks
)
176 : FeatureModules(Opts
.FeatureModules
), CDB(CDB
), TFS(TFS
),
177 DynamicIdx(Opts
.BuildDynamicSymbolIndex
? new FileIndex() : nullptr),
178 ClangTidyProvider(Opts
.ClangTidyProvider
),
179 UseDirtyHeaders(Opts
.UseDirtyHeaders
),
180 PreambleParseForwardingFunctions(Opts
.PreambleParseForwardingFunctions
),
181 WorkspaceRoot(Opts
.WorkspaceRoot
),
182 Transient(Opts
.ImplicitCancellation
? TUScheduler::InvalidateOnUpdate
183 : TUScheduler::NoInvalidation
),
184 DirtyFS(std::make_unique
<DraftStoreFS
>(TFS
, DraftMgr
)) {
185 if (Opts
.AsyncThreadsCount
!= 0)
186 IndexTasks
.emplace();
187 // Pass a callback into `WorkScheduler` to extract symbols from a newly
188 // parsed file and rebuild the file index synchronously each time an AST
190 WorkScheduler
.emplace(CDB
, TUScheduler::Options(Opts
),
191 std::make_unique
<UpdateIndexCallbacks
>(
192 DynamicIdx
.get(), Callbacks
, TFS
,
193 IndexTasks
? IndexTasks
.getPointer() : nullptr));
194 // Adds an index to the stack, at higher priority than existing indexes.
195 auto AddIndex
= [&](SymbolIndex
*Idx
) {
196 if (this->Index
!= nullptr) {
197 MergedIdx
.push_back(std::make_unique
<MergedIndex
>(Idx
, this->Index
));
198 this->Index
= MergedIdx
.back().get();
203 if (Opts
.StaticIndex
)
204 AddIndex(Opts
.StaticIndex
);
205 if (Opts
.BackgroundIndex
) {
206 BackgroundIndex::Options BGOpts
;
207 BGOpts
.ThreadPoolSize
= std::max(Opts
.AsyncThreadsCount
, 1u);
208 BGOpts
.OnProgress
= [Callbacks
](BackgroundQueue::Stats S
) {
210 Callbacks
->onBackgroundIndexProgress(S
);
212 BGOpts
.ContextProvider
= Opts
.ContextProvider
;
213 BackgroundIdx
= std::make_unique
<BackgroundIndex
>(
215 BackgroundIndexStorage::createDiskBackedStorageFactory(
216 [&CDB
](llvm::StringRef File
) { return CDB
.getProjectInfo(File
); }),
218 AddIndex(BackgroundIdx
.get());
221 AddIndex(DynamicIdx
.get());
223 if (Opts
.FeatureModules
) {
224 FeatureModule::Facilities F
{
225 *this->WorkScheduler
,
229 for (auto &Mod
: *Opts
.FeatureModules
)
234 ClangdServer::~ClangdServer() {
235 // Destroying TUScheduler first shuts down request threads that might
236 // otherwise access members concurrently.
237 // (Nobody can be using TUScheduler because we're on the main thread).
238 WorkScheduler
.reset();
239 // Now requests have stopped, we can shut down feature modules.
240 if (FeatureModules
) {
241 for (auto &Mod
: *FeatureModules
)
243 for (auto &Mod
: *FeatureModules
)
244 Mod
.blockUntilIdle(Deadline::infinity());
248 void ClangdServer::addDocument(PathRef File
, llvm::StringRef Contents
,
249 llvm::StringRef Version
,
250 WantDiagnostics WantDiags
, bool ForceRebuild
) {
251 std::string ActualVersion
= DraftMgr
.addDraft(File
, Version
, Contents
);
253 Opts
.PreambleParseForwardingFunctions
= PreambleParseForwardingFunctions
;
255 // Compile command is set asynchronously during update, as it can be slow.
257 Inputs
.TFS
= &getHeaderFS();
258 Inputs
.Contents
= std::string(Contents
);
259 Inputs
.Version
= std::move(ActualVersion
);
260 Inputs
.ForceRebuild
= ForceRebuild
;
261 Inputs
.Opts
= std::move(Opts
);
262 Inputs
.Index
= Index
;
263 Inputs
.ClangTidyProvider
= ClangTidyProvider
;
264 Inputs
.FeatureModules
= FeatureModules
;
265 bool NewFile
= WorkScheduler
->update(File
, Inputs
, WantDiags
);
266 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
267 if (NewFile
&& BackgroundIdx
)
268 BackgroundIdx
->boostRelated(File
);
271 void ClangdServer::reparseOpenFilesIfNeeded(
272 llvm::function_ref
<bool(llvm::StringRef File
)> Filter
) {
273 // Reparse only opened files that were modified.
274 for (const Path
&FilePath
: DraftMgr
.getActiveFiles())
275 if (Filter(FilePath
))
276 if (auto Draft
= DraftMgr
.getDraft(FilePath
)) // else disappeared in race?
277 addDocument(FilePath
, *Draft
->Contents
, Draft
->Version
,
278 WantDiagnostics::Auto
);
281 std::shared_ptr
<const std::string
> ClangdServer::getDraft(PathRef File
) const {
282 auto Draft
= DraftMgr
.getDraft(File
);
285 return std::move(Draft
->Contents
);
288 std::function
<Context(PathRef
)>
289 ClangdServer::createConfiguredContextProvider(const config::Provider
*Provider
,
290 Callbacks
*Publish
) {
292 return [](llvm::StringRef
) { return Context::current().clone(); };
295 const config::Provider
*Provider
;
296 ClangdServer::Callbacks
*Publish
;
297 std::mutex PublishMu
;
299 Impl(const config::Provider
*Provider
, ClangdServer::Callbacks
*Publish
)
300 : Provider(Provider
), Publish(Publish
) {}
302 Context
operator()(llvm::StringRef File
) {
303 config::Params Params
;
304 // Don't reread config files excessively often.
305 // FIXME: when we see a config file change event, use the event timestamp?
307 std::chrono::steady_clock::now() - std::chrono::seconds(5);
308 llvm::SmallString
<256> PosixPath
;
310 assert(llvm::sys::path::is_absolute(File
));
311 llvm::sys::path::native(File
, PosixPath
, llvm::sys::path::Style::posix
);
312 Params
.Path
= PosixPath
.str();
315 llvm::StringMap
<std::vector
<Diag
>> ReportableDiagnostics
;
316 Config C
= Provider
->getConfig(Params
, [&](const llvm::SMDiagnostic
&D
) {
317 // Create the map entry even for note diagnostics we don't report.
318 // This means that when the file is parsed with no warnings, we
319 // publish an empty set of diagnostics, clearing any the client has.
320 handleDiagnostic(D
, !Publish
|| D
.getFilename().empty()
322 : &ReportableDiagnostics
[D
.getFilename()]);
324 // Blindly publish diagnostics for the (unopened) parsed config files.
325 // We must avoid reporting diagnostics for *the same file* concurrently.
326 // Source diags are published elsewhere, but those are different files.
327 if (!ReportableDiagnostics
.empty()) {
328 std::lock_guard
<std::mutex
> Lock(PublishMu
);
329 for (auto &Entry
: ReportableDiagnostics
)
330 Publish
->onDiagnosticsReady(Entry
.first(), /*Version=*/"",
331 std::move(Entry
.second
));
333 return Context::current().derive(Config::Key
, std::move(C
));
336 void handleDiagnostic(const llvm::SMDiagnostic
&D
,
337 std::vector
<Diag
> *ClientDiagnostics
) {
338 switch (D
.getKind()) {
339 case llvm::SourceMgr::DK_Error
:
340 elog("config error at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
341 D
.getColumnNo(), D
.getMessage());
343 case llvm::SourceMgr::DK_Warning
:
344 log("config warning at {0}:{1}:{2}: {3}", D
.getFilename(),
345 D
.getLineNo(), D
.getColumnNo(), D
.getMessage());
347 case llvm::SourceMgr::DK_Note
:
348 case llvm::SourceMgr::DK_Remark
:
349 vlog("config note at {0}:{1}:{2}: {3}", D
.getFilename(), D
.getLineNo(),
350 D
.getColumnNo(), D
.getMessage());
351 ClientDiagnostics
= nullptr; // Don't emit notes as LSP diagnostics.
354 if (ClientDiagnostics
)
355 ClientDiagnostics
->push_back(toDiag(D
, Diag::ClangdConfig
));
360 return [I(std::make_shared
<Impl
>(Provider
, Publish
))](llvm::StringRef Path
) {
365 void ClangdServer::removeDocument(PathRef File
) {
366 DraftMgr
.removeDraft(File
);
367 WorkScheduler
->remove(File
);
370 void ClangdServer::codeComplete(PathRef File
, Position Pos
,
371 const clangd::CodeCompleteOptions
&Opts
,
372 Callback
<CodeCompleteResult
> CB
) {
373 // Copy completion options for passing them to async task handler.
374 auto CodeCompleteOpts
= Opts
;
375 if (!CodeCompleteOpts
.Index
) // Respect overridden index.
376 CodeCompleteOpts
.Index
= Index
;
378 auto Task
= [Pos
, CodeCompleteOpts
, File
= File
.str(), CB
= std::move(CB
),
379 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
381 return CB(IP
.takeError());
382 if (auto Reason
= isCancelled())
383 return CB(llvm::make_error
<CancelledError
>(Reason
));
385 llvm::Optional
<SpeculativeFuzzyFind
> SpecFuzzyFind
;
387 // No speculation in Fallback mode, as it's supposed to be much faster
388 // without compiling.
389 vlog("Build for file {0} is not ready. Enter fallback mode.", File
);
390 } else if (CodeCompleteOpts
.Index
) {
391 SpecFuzzyFind
.emplace();
393 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
394 SpecFuzzyFind
->CachedReq
= CachedCompletionFuzzyFindRequestByFile
[File
];
397 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
398 // FIXME: Add traling new line if there is none at eof, workaround a crash,
399 // see https://github.com/clangd/clangd/issues/332
400 if (!IP
->Contents
.endswith("\n"))
401 ParseInput
.Contents
.append("\n");
402 ParseInput
.Index
= Index
;
404 CodeCompleteOpts
.MainFileSignals
= IP
->Signals
;
405 CodeCompleteOpts
.AllScopes
= Config::current().Completion
.AllScopes
;
406 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
407 // both the old and the new version in case only one of them matches.
408 CodeCompleteResult Result
= clangd::codeComplete(
409 File
, Pos
, IP
->Preamble
, ParseInput
, CodeCompleteOpts
,
410 SpecFuzzyFind
? SpecFuzzyFind
.getPointer() : nullptr);
412 clang::clangd::trace::Span
Tracer("Completion results callback");
413 CB(std::move(Result
));
415 if (SpecFuzzyFind
&& SpecFuzzyFind
->NewReq
) {
416 std::lock_guard
<std::mutex
> Lock(CachedCompletionFuzzyFindRequestMutex
);
417 CachedCompletionFuzzyFindRequestByFile
[File
] =
418 SpecFuzzyFind
->NewReq
.value();
420 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
421 // We don't want `codeComplete` to wait for the async call if it doesn't use
422 // the result (e.g. non-index completion, speculation fails), so that `CB`
423 // is called as soon as results are available.
426 // We use a potentially-stale preamble because latency is critical here.
427 WorkScheduler
->runWithPreamble(
428 "CodeComplete", File
,
429 (Opts
.RunParser
== CodeCompleteOptions::AlwaysParse
)
431 : TUScheduler::StaleOrAbsent
,
435 void ClangdServer::signatureHelp(PathRef File
, Position Pos
,
436 MarkupKind DocumentationFormat
,
437 Callback
<SignatureHelp
> CB
) {
439 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
441 this](llvm::Expected
<InputsAndPreamble
> IP
) mutable {
443 return CB(IP
.takeError());
445 const auto *PreambleData
= IP
->Preamble
;
447 return CB(error("Failed to parse includes"));
449 ParseInputs ParseInput
{IP
->Command
, &getHeaderFS(), IP
->Contents
.str()};
450 // FIXME: Add traling new line if there is none at eof, workaround a crash,
451 // see https://github.com/clangd/clangd/issues/332
452 if (!IP
->Contents
.endswith("\n"))
453 ParseInput
.Contents
.append("\n");
454 ParseInput
.Index
= Index
;
455 CB(clangd::signatureHelp(File
, Pos
, *PreambleData
, ParseInput
,
456 DocumentationFormat
));
459 // Unlike code completion, we wait for a preamble here.
460 WorkScheduler
->runWithPreamble("SignatureHelp", File
, TUScheduler::Stale
,
464 void ClangdServer::formatFile(PathRef File
, llvm::Optional
<Range
> Rng
,
465 Callback
<tooling::Replacements
> CB
) {
466 auto Code
= getDraft(File
);
468 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
469 ErrorCode::InvalidParams
));
470 tooling::Range RequestedRange
;
472 llvm::Expected
<size_t> Begin
= positionToOffset(*Code
, Rng
->start
);
474 return CB(Begin
.takeError());
475 llvm::Expected
<size_t> End
= positionToOffset(*Code
, Rng
->end
);
477 return CB(End
.takeError());
478 RequestedRange
= tooling::Range(*Begin
, *End
- *Begin
);
480 RequestedRange
= tooling::Range(0, Code
->size());
483 // Call clang-format.
484 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
485 Ranges
= std::vector
<tooling::Range
>{RequestedRange
},
486 CB
= std::move(CB
), this]() mutable {
487 format::FormatStyle Style
= getFormatStyleForFile(File
, Code
, TFS
);
488 tooling::Replacements IncludeReplaces
=
489 format::sortIncludes(Style
, Code
, Ranges
, File
);
490 auto Changed
= tooling::applyAllReplacements(Code
, IncludeReplaces
);
492 return CB(Changed
.takeError());
494 CB(IncludeReplaces
.merge(format::reformat(
496 tooling::calculateRangesAfterReplacements(IncludeReplaces
, Ranges
),
499 WorkScheduler
->runQuick("Format", File
, std::move(Action
));
502 void ClangdServer::formatOnType(PathRef File
, Position Pos
,
503 StringRef TriggerText
,
504 Callback
<std::vector
<TextEdit
>> CB
) {
505 auto Code
= getDraft(File
);
507 return CB(llvm::make_error
<LSPError
>("trying to format non-added document",
508 ErrorCode::InvalidParams
));
509 llvm::Expected
<size_t> CursorPos
= positionToOffset(*Code
, Pos
);
511 return CB(CursorPos
.takeError());
512 auto Action
= [File
= File
.str(), Code
= std::move(*Code
),
513 TriggerText
= TriggerText
.str(), CursorPos
= *CursorPos
,
514 CB
= std::move(CB
), this]() mutable {
515 auto Style
= format::getStyle(format::DefaultFormatStyle
, File
,
516 format::DefaultFallbackStyle
, Code
,
517 TFS
.view(/*CWD=*/llvm::None
).get());
519 return CB(Style
.takeError());
521 std::vector
<TextEdit
> Result
;
522 for (const tooling::Replacement
&R
:
523 formatIncremental(Code
, CursorPos
, TriggerText
, *Style
))
524 Result
.push_back(replacementToEdit(Code
, R
));
527 WorkScheduler
->runQuick("FormatOnType", File
, std::move(Action
));
530 void ClangdServer::prepareRename(PathRef File
, Position Pos
,
531 llvm::Optional
<std::string
> NewName
,
532 const RenameOptions
&RenameOpts
,
533 Callback
<RenameResult
> CB
) {
534 auto Action
= [Pos
, File
= File
.str(), CB
= std::move(CB
),
535 NewName
= std::move(NewName
),
536 RenameOpts
](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
538 return CB(InpAST
.takeError());
539 // prepareRename is latency-sensitive: we don't query the index, as we
540 // only need main-file references
542 clangd::rename({Pos
, NewName
.value_or("__clangd_rename_placeholder"),
543 InpAST
->AST
, File
, /*FS=*/nullptr,
544 /*Index=*/nullptr, RenameOpts
});
546 // LSP says to return null on failure, but that will result in a generic
547 // failure message. If we send an LSP error response, clients can surface
548 // the message to users (VSCode does).
549 return CB(Results
.takeError());
553 WorkScheduler
->runWithAST("PrepareRename", File
, std::move(Action
));
556 void ClangdServer::rename(PathRef File
, Position Pos
, llvm::StringRef NewName
,
557 const RenameOptions
&Opts
,
558 Callback
<RenameResult
> CB
) {
559 auto Action
= [File
= File
.str(), NewName
= NewName
.str(), Pos
, Opts
,
561 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
562 // Tracks number of files edited per invocation.
563 static constexpr trace::Metric
RenameFiles("rename_files",
564 trace::Metric::Distribution
);
566 return CB(InpAST
.takeError());
567 auto R
= clangd::rename({Pos
, NewName
, InpAST
->AST
, File
,
568 DirtyFS
->view(llvm::None
), Index
, Opts
});
570 return CB(R
.takeError());
572 if (Opts
.WantFormat
) {
573 auto Style
= getFormatStyleForFile(File
, InpAST
->Inputs
.Contents
,
574 *InpAST
->Inputs
.TFS
);
575 llvm::Error Err
= llvm::Error::success();
576 for (auto &E
: R
->GlobalChanges
)
578 llvm::joinErrors(reformatEdit(E
.getValue(), Style
), std::move(Err
));
581 return CB(std::move(Err
));
583 RenameFiles
.record(R
->GlobalChanges
.size());
586 WorkScheduler
->runWithAST("Rename", File
, std::move(Action
));
589 // May generate several candidate selections, due to SelectionTree ambiguity.
590 // vector of pointers because GCC doesn't like non-copyable Selection.
591 static llvm::Expected
<std::vector
<std::unique_ptr
<Tweak::Selection
>>>
592 tweakSelection(const Range
&Sel
, const InputsAndAST
&AST
,
593 llvm::vfs::FileSystem
*FS
) {
594 auto Begin
= positionToOffset(AST
.Inputs
.Contents
, Sel
.start
);
596 return Begin
.takeError();
597 auto End
= positionToOffset(AST
.Inputs
.Contents
, Sel
.end
);
599 return End
.takeError();
600 std::vector
<std::unique_ptr
<Tweak::Selection
>> Result
;
601 SelectionTree::createEach(
602 AST
.AST
.getASTContext(), AST
.AST
.getTokens(), *Begin
, *End
,
603 [&](SelectionTree T
) {
604 Result
.push_back(std::make_unique
<Tweak::Selection
>(
605 AST
.Inputs
.Index
, AST
.AST
, *Begin
, *End
, std::move(T
), FS
));
608 assert(!Result
.empty() && "Expected at least one SelectionTree");
609 return std::move(Result
);
612 void ClangdServer::enumerateTweaks(
613 PathRef File
, Range Sel
, llvm::unique_function
<bool(const Tweak
&)> Filter
,
614 Callback
<std::vector
<TweakRef
>> CB
) {
615 // Tracks number of times a tweak has been offered.
616 static constexpr trace::Metric
TweakAvailable(
617 "tweak_available", trace::Metric::Counter
, "tweak_id");
618 auto Action
= [Sel
, CB
= std::move(CB
), Filter
= std::move(Filter
),
619 FeatureModules(this->FeatureModules
)](
620 Expected
<InputsAndAST
> InpAST
) mutable {
622 return CB(InpAST
.takeError());
623 auto Selections
= tweakSelection(Sel
, *InpAST
, /*FS=*/nullptr);
625 return CB(Selections
.takeError());
626 std::vector
<TweakRef
> Res
;
627 // Don't allow a tweak to fire more than once across ambiguous selections.
628 llvm::DenseSet
<llvm::StringRef
> PreparedTweaks
;
629 auto DeduplicatingFilter
= [&](const Tweak
&T
) {
630 return Filter(T
) && !PreparedTweaks
.count(T
.id());
632 for (const auto &Sel
: *Selections
) {
633 for (auto &T
: prepareTweaks(*Sel
, DeduplicatingFilter
, FeatureModules
)) {
634 Res
.push_back({T
->id(), T
->title(), T
->kind()});
635 PreparedTweaks
.insert(T
->id());
636 TweakAvailable
.record(1, T
->id());
643 WorkScheduler
->runWithAST("EnumerateTweaks", File
, std::move(Action
),
647 void ClangdServer::applyTweak(PathRef File
, Range Sel
, StringRef TweakID
,
648 Callback
<Tweak::Effect
> CB
) {
649 // Tracks number of times a tweak has been attempted.
650 static constexpr trace::Metric
TweakAttempt(
651 "tweak_attempt", trace::Metric::Counter
, "tweak_id");
652 // Tracks number of times a tweak has failed to produce edits.
653 static constexpr trace::Metric
TweakFailed(
654 "tweak_failed", trace::Metric::Counter
, "tweak_id");
655 TweakAttempt
.record(1, TweakID
);
656 auto Action
= [File
= File
.str(), Sel
, TweakID
= TweakID
.str(),
658 this](Expected
<InputsAndAST
> InpAST
) mutable {
660 return CB(InpAST
.takeError());
661 auto FS
= DirtyFS
->view(llvm::None
);
662 auto Selections
= tweakSelection(Sel
, *InpAST
, FS
.get());
664 return CB(Selections
.takeError());
665 llvm::Optional
<llvm::Expected
<Tweak::Effect
>> Effect
;
666 // Try each selection, take the first one that prepare()s.
667 // If they all fail, Effect will hold get the last error.
668 for (const auto &Selection
: *Selections
) {
669 auto T
= prepareTweak(TweakID
, *Selection
, FeatureModules
);
671 Effect
= (*T
)->apply(*Selection
);
674 Effect
= T
.takeError();
676 assert(Effect
&& "Expected at least one selection");
677 if (*Effect
&& (*Effect
)->FormatEdits
) {
678 // Format tweaks that require it centrally here.
679 for (auto &It
: (*Effect
)->ApplyEdits
) {
681 format::FormatStyle Style
=
682 getFormatStyleForFile(File
, E
.InitialCode
, TFS
);
683 if (llvm::Error Err
= reformatEdit(E
, Style
))
684 elog("Failed to format {0}: {1}", It
.first(), std::move(Err
));
687 TweakFailed
.record(1, TweakID
);
689 return CB(std::move(*Effect
));
691 WorkScheduler
->runWithAST("ApplyTweak", File
, std::move(Action
));
694 void ClangdServer::locateSymbolAt(PathRef File
, Position Pos
,
695 Callback
<std::vector
<LocatedSymbol
>> CB
) {
696 auto Action
= [Pos
, CB
= std::move(CB
),
697 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
699 return CB(InpAST
.takeError());
700 CB(clangd::locateSymbolAt(InpAST
->AST
, Pos
, Index
));
703 WorkScheduler
->runWithAST("Definitions", File
, std::move(Action
));
706 void ClangdServer::switchSourceHeader(
707 PathRef Path
, Callback
<llvm::Optional
<clangd::Path
>> CB
) {
708 // We want to return the result as fast as possible, strategy is:
709 // 1) use the file-only heuristic, it requires some IO but it is much
710 // faster than building AST, but it only works when .h/.cc files are in
711 // the same directory.
712 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports
713 // different code layout.
714 if (auto CorrespondingFile
=
715 getCorrespondingHeaderOrSource(Path
, TFS
.view(llvm::None
)))
716 return CB(std::move(CorrespondingFile
));
717 auto Action
= [Path
= Path
.str(), CB
= std::move(CB
),
718 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
720 return CB(InpAST
.takeError());
721 CB(getCorrespondingHeaderOrSource(Path
, InpAST
->AST
, Index
));
723 WorkScheduler
->runWithAST("SwitchHeaderSource", Path
, std::move(Action
));
726 void ClangdServer::findDocumentHighlights(
727 PathRef File
, Position Pos
, Callback
<std::vector
<DocumentHighlight
>> CB
) {
729 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
731 return CB(InpAST
.takeError());
732 CB(clangd::findDocumentHighlights(InpAST
->AST
, Pos
));
735 WorkScheduler
->runWithAST("Highlights", File
, std::move(Action
), Transient
);
738 void ClangdServer::findHover(PathRef File
, Position Pos
,
739 Callback
<llvm::Optional
<HoverInfo
>> CB
) {
740 auto Action
= [File
= File
.str(), Pos
, CB
= std::move(CB
),
741 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
743 return CB(InpAST
.takeError());
744 format::FormatStyle Style
= getFormatStyleForFile(
745 File
, InpAST
->Inputs
.Contents
, *InpAST
->Inputs
.TFS
);
746 CB(clangd::getHover(InpAST
->AST
, Pos
, std::move(Style
), Index
));
749 WorkScheduler
->runWithAST("Hover", File
, std::move(Action
), Transient
);
752 void ClangdServer::typeHierarchy(PathRef File
, Position Pos
, int Resolve
,
753 TypeHierarchyDirection Direction
,
754 Callback
<Optional
<TypeHierarchyItem
>> CB
) {
755 auto Action
= [File
= File
.str(), Pos
, Resolve
, Direction
, CB
= std::move(CB
),
756 this](Expected
<InputsAndAST
> InpAST
) mutable {
758 return CB(InpAST
.takeError());
759 CB(clangd::getTypeHierarchy(InpAST
->AST
, Pos
, Resolve
, Direction
, Index
,
763 WorkScheduler
->runWithAST("TypeHierarchy", File
, std::move(Action
));
766 void ClangdServer::resolveTypeHierarchy(
767 TypeHierarchyItem Item
, int Resolve
, TypeHierarchyDirection Direction
,
768 Callback
<llvm::Optional
<TypeHierarchyItem
>> CB
) {
770 "Resolve Type Hierarchy", "", [=, CB
= std::move(CB
)]() mutable {
771 clangd::resolveTypeHierarchy(Item
, Resolve
, Direction
, Index
);
776 void ClangdServer::prepareCallHierarchy(
777 PathRef File
, Position Pos
, Callback
<std::vector
<CallHierarchyItem
>> CB
) {
778 auto Action
= [File
= File
.str(), Pos
,
779 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
781 return CB(InpAST
.takeError());
782 CB(clangd::prepareCallHierarchy(InpAST
->AST
, Pos
, File
));
784 WorkScheduler
->runWithAST("CallHierarchy", File
, std::move(Action
));
787 void ClangdServer::incomingCalls(
788 const CallHierarchyItem
&Item
,
789 Callback
<std::vector
<CallHierarchyIncomingCall
>> CB
) {
790 WorkScheduler
->run("Incoming Calls", "",
791 [CB
= std::move(CB
), Item
, this]() mutable {
792 CB(clangd::incomingCalls(Item
, Index
));
796 void ClangdServer::inlayHints(PathRef File
, llvm::Optional
<Range
> RestrictRange
,
797 Callback
<std::vector
<InlayHint
>> CB
) {
798 auto Action
= [RestrictRange(std::move(RestrictRange
)),
799 CB
= std::move(CB
)](Expected
<InputsAndAST
> InpAST
) mutable {
801 return CB(InpAST
.takeError());
802 CB(clangd::inlayHints(InpAST
->AST
, std::move(RestrictRange
)));
804 WorkScheduler
->runWithAST("InlayHints", File
, std::move(Action
), Transient
);
807 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
808 // FIXME: Do nothing for now. This will be used for indexing and potentially
809 // invalidating other caches.
812 void ClangdServer::workspaceSymbols(
813 llvm::StringRef Query
, int Limit
,
814 Callback
<std::vector
<SymbolInformation
>> CB
) {
816 "getWorkspaceSymbols", /*Path=*/"",
817 [Query
= Query
.str(), Limit
, CB
= std::move(CB
), this]() mutable {
818 CB(clangd::getWorkspaceSymbols(Query
, Limit
, Index
,
819 WorkspaceRoot
.value_or("")));
823 void ClangdServer::documentSymbols(llvm::StringRef File
,
824 Callback
<std::vector
<DocumentSymbol
>> CB
) {
826 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
828 return CB(InpAST
.takeError());
829 CB(clangd::getDocumentSymbols(InpAST
->AST
));
831 WorkScheduler
->runWithAST("DocumentSymbols", File
, std::move(Action
),
835 void ClangdServer::foldingRanges(llvm::StringRef File
,
836 Callback
<std::vector
<FoldingRange
>> CB
) {
837 auto Code
= getDraft(File
);
839 return CB(llvm::make_error
<LSPError
>(
840 "trying to compute folding ranges for non-added document",
841 ErrorCode::InvalidParams
));
842 auto Action
= [CB
= std::move(CB
), Code
= std::move(*Code
)]() mutable {
843 CB(clangd::getFoldingRanges(Code
));
845 // We want to make sure folding ranges are always available for all the open
846 // files, hence prefer runQuick to not wait for operations on other files.
847 WorkScheduler
->runQuick("FoldingRanges", File
, std::move(Action
));
850 void ClangdServer::findType(llvm::StringRef File
, Position Pos
,
851 Callback
<std::vector
<LocatedSymbol
>> CB
) {
853 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
855 return CB(InpAST
.takeError());
856 CB(clangd::findType(InpAST
->AST
, Pos
));
858 WorkScheduler
->runWithAST("FindType", File
, std::move(Action
));
861 void ClangdServer::findImplementations(
862 PathRef File
, Position Pos
, Callback
<std::vector
<LocatedSymbol
>> CB
) {
863 auto Action
= [Pos
, CB
= std::move(CB
),
864 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
866 return CB(InpAST
.takeError());
867 CB(clangd::findImplementations(InpAST
->AST
, Pos
, Index
));
870 WorkScheduler
->runWithAST("Implementations", File
, std::move(Action
));
873 void ClangdServer::findReferences(PathRef File
, Position Pos
, uint32_t Limit
,
874 Callback
<ReferencesResult
> CB
) {
875 auto Action
= [Pos
, Limit
, CB
= std::move(CB
),
876 this](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
878 return CB(InpAST
.takeError());
879 CB(clangd::findReferences(InpAST
->AST
, Pos
, Limit
, Index
));
882 WorkScheduler
->runWithAST("References", File
, std::move(Action
));
885 void ClangdServer::symbolInfo(PathRef File
, Position Pos
,
886 Callback
<std::vector
<SymbolDetails
>> CB
) {
888 [Pos
, CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
890 return CB(InpAST
.takeError());
891 CB(clangd::getSymbolInfo(InpAST
->AST
, Pos
));
894 WorkScheduler
->runWithAST("SymbolInfo", File
, std::move(Action
));
897 void ClangdServer::semanticRanges(PathRef File
,
898 const std::vector
<Position
> &Positions
,
899 Callback
<std::vector
<SelectionRange
>> CB
) {
900 auto Action
= [Positions
, CB
= std::move(CB
)](
901 llvm::Expected
<InputsAndAST
> InpAST
) mutable {
903 return CB(InpAST
.takeError());
904 std::vector
<SelectionRange
> Result
;
905 for (const auto &Pos
: Positions
) {
906 if (auto Range
= clangd::getSemanticRanges(InpAST
->AST
, Pos
))
907 Result
.push_back(std::move(*Range
));
909 return CB(Range
.takeError());
911 CB(std::move(Result
));
913 WorkScheduler
->runWithAST("SemanticRanges", File
, std::move(Action
));
916 void ClangdServer::documentLinks(PathRef File
,
917 Callback
<std::vector
<DocumentLink
>> CB
) {
919 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
921 return CB(InpAST
.takeError());
922 CB(clangd::getDocumentLinks(InpAST
->AST
));
924 WorkScheduler
->runWithAST("DocumentLinks", File
, std::move(Action
),
928 void ClangdServer::semanticHighlights(
929 PathRef File
, Callback
<std::vector
<HighlightingToken
>> CB
) {
931 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
933 return CB(InpAST
.takeError());
934 CB(clangd::getSemanticHighlightings(InpAST
->AST
));
936 WorkScheduler
->runWithAST("SemanticHighlights", File
, std::move(Action
),
940 void ClangdServer::getAST(PathRef File
, llvm::Optional
<Range
> R
,
941 Callback
<llvm::Optional
<ASTNode
>> CB
) {
943 [R
, CB(std::move(CB
))](llvm::Expected
<InputsAndAST
> Inputs
) mutable {
945 return CB(Inputs
.takeError());
947 // It's safe to pass in the TU, as dumpAST() does not
948 // deserialize the preamble.
949 auto Node
= DynTypedNode::create(
950 *Inputs
->AST
.getASTContext().getTranslationUnitDecl());
951 return CB(dumpAST(Node
, Inputs
->AST
.getTokens(),
952 Inputs
->AST
.getASTContext()));
955 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->start
))
958 return CB(Offset
.takeError());
959 if (auto Offset
= positionToOffset(Inputs
->Inputs
.Contents
, R
->end
))
962 return CB(Offset
.takeError());
963 bool Success
= SelectionTree::createEach(
964 Inputs
->AST
.getASTContext(), Inputs
->AST
.getTokens(), Start
, End
,
965 [&](SelectionTree T
) {
966 if (const SelectionTree::Node
*N
= T
.commonAncestor()) {
967 CB(dumpAST(N
->ASTNode
, Inputs
->AST
.getTokens(),
968 Inputs
->AST
.getASTContext()));
976 WorkScheduler
->runWithAST("GetAST", File
, std::move(Action
));
979 void ClangdServer::customAction(PathRef File
, llvm::StringRef Name
,
980 Callback
<InputsAndAST
> Action
) {
981 WorkScheduler
->runWithAST(Name
, File
, std::move(Action
));
984 void ClangdServer::diagnostics(PathRef File
, Callback
<std::vector
<Diag
>> CB
) {
986 [CB
= std::move(CB
)](llvm::Expected
<InputsAndAST
> InpAST
) mutable {
988 return CB(InpAST
.takeError());
989 if (auto Diags
= InpAST
->AST
.getDiagnostics())
991 // FIXME: Use ServerCancelled error once it is settled in LSP-3.17.
992 return CB(llvm::make_error
<LSPError
>("server is busy parsing includes",
993 ErrorCode::InternalError
));
996 WorkScheduler
->runWithAST("Diagnostics", File
, std::move(Action
));
999 llvm::StringMap
<TUScheduler::FileStats
> ClangdServer::fileStats() const {
1000 return WorkScheduler
->fileStats();
1004 ClangdServer::blockUntilIdleForTest(llvm::Optional
<double> TimeoutSeconds
) {
1005 // Order is important here: we don't want to block on A and then B,
1006 // if B might schedule work on A.
1008 // Nothing else can schedule work on TUScheduler, because it's not threadsafe
1009 // and we're blocking the main thread.
1010 if (!WorkScheduler
->blockUntilIdle(timeoutSeconds(TimeoutSeconds
)))
1012 // TUScheduler is the only thing that starts background indexing work.
1013 if (IndexTasks
&& !IndexTasks
->wait(timeoutSeconds(TimeoutSeconds
)))
1016 // Unfortunately we don't have strict topological order between the rest of
1017 // the components. E.g. CDB broadcast triggers backrgound indexing.
1018 // This queries the CDB which may discover new work if disk has changed.
1020 // So try each one a few times in a loop.
1021 // If there are no tricky interactions then all after the first are no-ops.
1022 // Then on the last iteration, verify they're idle without waiting.
1024 // There's a small chance they're juggling work and we didn't catch them :-(
1025 for (llvm::Optional
<double> Timeout
:
1026 {TimeoutSeconds
, TimeoutSeconds
, llvm::Optional
<double>(0)}) {
1027 if (!CDB
.blockUntilIdle(timeoutSeconds(Timeout
)))
1029 if (BackgroundIdx
&& !BackgroundIdx
->blockUntilIdleForTest(Timeout
))
1031 if (FeatureModules
&& llvm::any_of(*FeatureModules
, [&](FeatureModule
&M
) {
1032 return !M
.blockUntilIdle(timeoutSeconds(Timeout
));
1037 assert(WorkScheduler
->blockUntilIdle(Deadline::zero()) &&
1038 "Something scheduled work while we're blocking the main thread!");
1042 void ClangdServer::profile(MemoryTree
&MT
) const {
1044 DynamicIdx
->profile(MT
.child("dynamic_index"));
1046 BackgroundIdx
->profile(MT
.child("background_index"));
1047 WorkScheduler
->profile(MT
.child("tuscheduler"));
1049 } // namespace clangd
1050 } // namespace clang