1 //===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===// 2 // 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 6 // 7 //===-------------------------------------------------------------------===// 8 9 #include "ClangdServer.h" 10 #include "CodeComplete.h" 11 #include "Config.h" 12 #include "Diagnostics.h" 13 #include "DumpAST.h" 14 #include "FindSymbols.h" 15 #include "Format.h" 16 #include "HeaderSourceSwitch.h" 17 #include "Headers.h" 18 #include "InlayHints.h" 19 #include "ParsedAST.h" 20 #include "Preamble.h" 21 #include "Protocol.h" 22 #include "SemanticHighlighting.h" 23 #include "SemanticSelection.h" 24 #include "SourceCode.h" 25 #include "TUScheduler.h" 26 #include "XRefs.h" 27 #include "index/CanonicalIncludes.h" 28 #include "index/FileIndex.h" 29 #include "index/Merge.h" 30 #include "refactor/Rename.h" 31 #include "refactor/Tweak.h" 32 #include "support/Logger.h" 33 #include "support/Markup.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/Frontend/CompilerInstance.h" 39 #include "clang/Frontend/CompilerInvocation.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/Optional.h" 45 #include "llvm/ADT/STLExtras.h" 46 #include "llvm/ADT/ScopeExit.h" 47 #include "llvm/ADT/StringExtras.h" 48 #include "llvm/ADT/StringRef.h" 49 #include "llvm/Support/Errc.h" 50 #include "llvm/Support/Error.h" 51 #include "llvm/Support/FileSystem.h" 52 #include "llvm/Support/Path.h" 53 #include "llvm/Support/ScopedPrinter.h" 54 #include "llvm/Support/raw_ostream.h" 55 #include <algorithm> 56 #include <chrono> 57 #include <future> 58 #include <memory> 59 #include <mutex> 60 #include <string> 61 #include <type_traits> 62 63 namespace clang { 64 namespace clangd { 65 namespace { 66 67 // Update the FileIndex with new ASTs and plumb the diagnostics responses. 68 struct UpdateIndexCallbacks : public ParsingCallbacks { 69 UpdateIndexCallbacks(FileIndex *FIndex, 70 ClangdServer::Callbacks *ServerCallbacks) 71 : FIndex(FIndex), ServerCallbacks(ServerCallbacks) {} 72 73 void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx, 74 Preprocessor &PP, 75 const CanonicalIncludes &CanonIncludes) override { 76 if (FIndex) 77 FIndex->updatePreamble(Path, Version, Ctx, PP, CanonIncludes); 78 } 79 80 void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override { 81 if (FIndex) 82 FIndex->updateMain(Path, AST); 83 84 assert(AST.getDiagnostics().hasValue() && 85 "We issue callback only with fresh preambles"); 86 std::vector<Diag> Diagnostics = *AST.getDiagnostics(); 87 if (ServerCallbacks) 88 Publish([&]() { 89 ServerCallbacks->onDiagnosticsReady(Path, AST.version(), 90 std::move(Diagnostics)); 91 }); 92 } 93 94 void onFailedAST(PathRef Path, llvm::StringRef Version, 95 std::vector<Diag> Diags, PublishFn Publish) override { 96 if (ServerCallbacks) 97 Publish( 98 [&]() { ServerCallbacks->onDiagnosticsReady(Path, Version, Diags); }); 99 } 100 101 void onFileUpdated(PathRef File, const TUStatus &Status) override { 102 if (ServerCallbacks) 103 ServerCallbacks->onFileUpdated(File, Status); 104 } 105 106 void onPreamblePublished(PathRef File) override { 107 if (ServerCallbacks) 108 ServerCallbacks->onSemanticsMaybeChanged(File); 109 } 110 111 private: 112 FileIndex *FIndex; 113 ClangdServer::Callbacks *ServerCallbacks; 114 }; 115 116 class DraftStoreFS : public ThreadsafeFS { 117 public: 118 DraftStoreFS(const ThreadsafeFS &Base, const DraftStore &Drafts) 119 : Base(Base), DirtyFiles(Drafts) {} 120 121 private: 122 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override { 123 auto OFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>( 124 Base.view(llvm::None)); 125 OFS->pushOverlay(DirtyFiles.asVFS()); 126 return OFS; 127 } 128 129 const ThreadsafeFS &Base; 130 const DraftStore &DirtyFiles; 131 }; 132 133 } // namespace 134 135 ClangdServer::Options ClangdServer::optsForTest() { 136 ClangdServer::Options Opts; 137 Opts.UpdateDebounce = DebouncePolicy::fixed(/*zero*/ {}); 138 Opts.StorePreamblesInMemory = true; 139 Opts.AsyncThreadsCount = 4; // Consistent! 140 return Opts; 141 } 142 143 ClangdServer::Options::operator TUScheduler::Options() const { 144 TUScheduler::Options Opts; 145 Opts.AsyncThreadsCount = AsyncThreadsCount; 146 Opts.RetentionPolicy = RetentionPolicy; 147 Opts.StorePreamblesInMemory = StorePreamblesInMemory; 148 Opts.UpdateDebounce = UpdateDebounce; 149 Opts.ContextProvider = ContextProvider; 150 return Opts; 151 } 152 153 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, 154 const ThreadsafeFS &TFS, const Options &Opts, 155 Callbacks *Callbacks) 156 : FeatureModules(Opts.FeatureModules), CDB(CDB), TFS(TFS), 157 DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr), 158 ClangTidyProvider(Opts.ClangTidyProvider), 159 WorkspaceRoot(Opts.WorkspaceRoot), 160 Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate 161 : TUScheduler::NoInvalidation), 162 DirtyFS(std::make_unique<DraftStoreFS>(TFS, DraftMgr)) { 163 // Pass a callback into `WorkScheduler` to extract symbols from a newly 164 // parsed file and rebuild the file index synchronously each time an AST 165 // is parsed. 166 WorkScheduler.emplace( 167 CDB, TUScheduler::Options(Opts), 168 std::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(), Callbacks)); 169 // Adds an index to the stack, at higher priority than existing indexes. 170 auto AddIndex = [&](SymbolIndex *Idx) { 171 if (this->Index != nullptr) { 172 MergedIdx.push_back(std::make_unique<MergedIndex>(Idx, this->Index)); 173 this->Index = MergedIdx.back().get(); 174 } else { 175 this->Index = Idx; 176 } 177 }; 178 if (Opts.StaticIndex) 179 AddIndex(Opts.StaticIndex); 180 if (Opts.BackgroundIndex) { 181 BackgroundIndex::Options BGOpts; 182 BGOpts.ThreadPoolSize = std::max(Opts.AsyncThreadsCount, 1u); 183 BGOpts.OnProgress = [Callbacks](BackgroundQueue::Stats S) { 184 if (Callbacks) 185 Callbacks->onBackgroundIndexProgress(S); 186 }; 187 BGOpts.ContextProvider = Opts.ContextProvider; 188 BackgroundIdx = std::make_unique<BackgroundIndex>( 189 TFS, CDB, 190 BackgroundIndexStorage::createDiskBackedStorageFactory( 191 [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }), 192 std::move(BGOpts)); 193 AddIndex(BackgroundIdx.get()); 194 } 195 if (DynamicIdx) 196 AddIndex(DynamicIdx.get()); 197 198 if (Opts.FeatureModules) { 199 FeatureModule::Facilities F{ 200 *this->WorkScheduler, 201 this->Index, 202 this->TFS, 203 }; 204 for (auto &Mod : *Opts.FeatureModules) 205 Mod.initialize(F); 206 } 207 } 208 209 ClangdServer::~ClangdServer() { 210 // Destroying TUScheduler first shuts down request threads that might 211 // otherwise access members concurrently. 212 // (Nobody can be using TUScheduler because we're on the main thread). 213 WorkScheduler.reset(); 214 // Now requests have stopped, we can shut down feature modules. 215 if (FeatureModules) { 216 for (auto &Mod : *FeatureModules) 217 Mod.stop(); 218 for (auto &Mod : *FeatureModules) 219 Mod.blockUntilIdle(Deadline::infinity()); 220 } 221 } 222 223 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, 224 llvm::StringRef Version, 225 WantDiagnostics WantDiags, bool ForceRebuild) { 226 std::string ActualVersion = DraftMgr.addDraft(File, Version, Contents); 227 ParseOptions Opts; 228 229 // Compile command is set asynchronously during update, as it can be slow. 230 ParseInputs Inputs; 231 Inputs.TFS = &TFS; 232 Inputs.Contents = std::string(Contents); 233 Inputs.Version = std::move(ActualVersion); 234 Inputs.ForceRebuild = ForceRebuild; 235 Inputs.Opts = std::move(Opts); 236 Inputs.Index = Index; 237 Inputs.ClangTidyProvider = ClangTidyProvider; 238 Inputs.FeatureModules = FeatureModules; 239 bool NewFile = WorkScheduler->update(File, Inputs, WantDiags); 240 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. 241 if (NewFile && BackgroundIdx) 242 BackgroundIdx->boostRelated(File); 243 } 244 245 void ClangdServer::reparseOpenFilesIfNeeded( 246 llvm::function_ref<bool(llvm::StringRef File)> Filter) { 247 // Reparse only opened files that were modified. 248 for (const Path &FilePath : DraftMgr.getActiveFiles()) 249 if (Filter(FilePath)) 250 if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race? 251 addDocument(FilePath, *Draft->Contents, Draft->Version, 252 WantDiagnostics::Auto); 253 } 254 255 std::shared_ptr<const std::string> ClangdServer::getDraft(PathRef File) const { 256 auto Draft = DraftMgr.getDraft(File); 257 if (!Draft) 258 return nullptr; 259 return std::move(Draft->Contents); 260 } 261 262 std::function<Context(PathRef)> 263 ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, 264 Callbacks *Publish) { 265 if (!Provider) 266 return [](llvm::StringRef) { return Context::current().clone(); }; 267 268 struct Impl { 269 const config::Provider *Provider; 270 ClangdServer::Callbacks *Publish; 271 std::mutex PublishMu; 272 273 Impl(const config::Provider *Provider, ClangdServer::Callbacks *Publish) 274 : Provider(Provider), Publish(Publish) {} 275 276 Context operator()(llvm::StringRef File) { 277 config::Params Params; 278 // Don't reread config files excessively often. 279 // FIXME: when we see a config file change event, use the event timestamp? 280 Params.FreshTime = 281 std::chrono::steady_clock::now() - std::chrono::seconds(5); 282 llvm::SmallString<256> PosixPath; 283 if (!File.empty()) { 284 assert(llvm::sys::path::is_absolute(File)); 285 llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix); 286 Params.Path = PosixPath.str(); 287 } 288 289 llvm::StringMap<std::vector<Diag>> ReportableDiagnostics; 290 Config C = Provider->getConfig(Params, [&](const llvm::SMDiagnostic &D) { 291 // Create the map entry even for note diagnostics we don't report. 292 // This means that when the file is parsed with no warnings, we 293 // publish an empty set of diagnostics, clearing any the client has. 294 handleDiagnostic(D, !Publish || D.getFilename().empty() 295 ? nullptr 296 : &ReportableDiagnostics[D.getFilename()]); 297 }); 298 // Blindly publish diagnostics for the (unopened) parsed config files. 299 // We must avoid reporting diagnostics for *the same file* concurrently. 300 // Source diags are published elsewhere, but those are different files. 301 if (!ReportableDiagnostics.empty()) { 302 std::lock_guard<std::mutex> Lock(PublishMu); 303 for (auto &Entry : ReportableDiagnostics) 304 Publish->onDiagnosticsReady(Entry.first(), /*Version=*/"", 305 std::move(Entry.second)); 306 } 307 return Context::current().derive(Config::Key, std::move(C)); 308 } 309 310 void handleDiagnostic(const llvm::SMDiagnostic &D, 311 std::vector<Diag> *ClientDiagnostics) { 312 switch (D.getKind()) { 313 case llvm::SourceMgr::DK_Error: 314 elog("config error at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), 315 D.getColumnNo(), D.getMessage()); 316 break; 317 case llvm::SourceMgr::DK_Warning: 318 log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), 319 D.getLineNo(), D.getColumnNo(), D.getMessage()); 320 break; 321 case llvm::SourceMgr::DK_Note: 322 case llvm::SourceMgr::DK_Remark: 323 vlog("config note at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), 324 D.getColumnNo(), D.getMessage()); 325 ClientDiagnostics = nullptr; // Don't emit notes as LSP diagnostics. 326 break; 327 } 328 if (ClientDiagnostics) 329 ClientDiagnostics->push_back(toDiag(D, Diag::ClangdConfig)); 330 } 331 }; 332 333 // Copyable wrapper. 334 return [I(std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) { 335 return (*I)(Path); 336 }; 337 } 338 339 void ClangdServer::removeDocument(PathRef File) { 340 DraftMgr.removeDraft(File); 341 WorkScheduler->remove(File); 342 } 343 344 void ClangdServer::codeComplete(PathRef File, Position Pos, 345 const clangd::CodeCompleteOptions &Opts, 346 Callback<CodeCompleteResult> CB) { 347 // Copy completion options for passing them to async task handler. 348 auto CodeCompleteOpts = Opts; 349 if (!CodeCompleteOpts.Index) // Respect overridden index. 350 CodeCompleteOpts.Index = Index; 351 352 auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB), 353 this](llvm::Expected<InputsAndPreamble> IP) mutable { 354 if (!IP) 355 return CB(IP.takeError()); 356 if (auto Reason = isCancelled()) 357 return CB(llvm::make_error<CancelledError>(Reason)); 358 359 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; 360 if (!IP->Preamble) { 361 // No speculation in Fallback mode, as it's supposed to be much faster 362 // without compiling. 363 vlog("Build for file {0} is not ready. Enter fallback mode.", File); 364 } else if (CodeCompleteOpts.Index) { 365 SpecFuzzyFind.emplace(); 366 { 367 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); 368 SpecFuzzyFind->CachedReq = CachedCompletionFuzzyFindRequestByFile[File]; 369 } 370 } 371 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()}; 372 ParseInput.Index = Index; 373 374 CodeCompleteOpts.MainFileSignals = IP->Signals; 375 CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; 376 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check 377 // both the old and the new version in case only one of them matches. 378 CodeCompleteResult Result = clangd::codeComplete( 379 File, Pos, IP->Preamble, ParseInput, CodeCompleteOpts, 380 SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); 381 { 382 clang::clangd::trace::Span Tracer("Completion results callback"); 383 CB(std::move(Result)); 384 } 385 if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) { 386 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); 387 CachedCompletionFuzzyFindRequestByFile[File] = 388 SpecFuzzyFind->NewReq.getValue(); 389 } 390 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes. 391 // We don't want `codeComplete` to wait for the async call if it doesn't use 392 // the result (e.g. non-index completion, speculation fails), so that `CB` 393 // is called as soon as results are available. 394 }; 395 396 // We use a potentially-stale preamble because latency is critical here. 397 WorkScheduler->runWithPreamble( 398 "CodeComplete", File, 399 (Opts.RunParser == CodeCompleteOptions::AlwaysParse) 400 ? TUScheduler::Stale 401 : TUScheduler::StaleOrAbsent, 402 std::move(Task)); 403 } 404 405 void ClangdServer::signatureHelp(PathRef File, Position Pos, 406 MarkupKind DocumentationFormat, 407 Callback<SignatureHelp> CB) { 408 409 auto Action = [Pos, File = File.str(), CB = std::move(CB), 410 DocumentationFormat, 411 this](llvm::Expected<InputsAndPreamble> IP) mutable { 412 if (!IP) 413 return CB(IP.takeError()); 414 415 const auto *PreambleData = IP->Preamble; 416 if (!PreambleData) 417 return CB(error("Failed to parse includes")); 418 419 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()}; 420 ParseInput.Index = Index; 421 CB(clangd::signatureHelp(File, Pos, *PreambleData, ParseInput, 422 DocumentationFormat)); 423 }; 424 425 // Unlike code completion, we wait for a preamble here. 426 WorkScheduler->runWithPreamble("SignatureHelp", File, TUScheduler::Stale, 427 std::move(Action)); 428 } 429 430 void ClangdServer::formatFile(PathRef File, llvm::Optional<Range> Rng, 431 Callback<tooling::Replacements> CB) { 432 auto Code = getDraft(File); 433 if (!Code) 434 return CB(llvm::make_error<LSPError>("trying to format non-added document", 435 ErrorCode::InvalidParams)); 436 tooling::Range RequestedRange; 437 if (Rng) { 438 llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start); 439 if (!Begin) 440 return CB(Begin.takeError()); 441 llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end); 442 if (!End) 443 return CB(End.takeError()); 444 RequestedRange = tooling::Range(*Begin, *End - *Begin); 445 } else { 446 RequestedRange = tooling::Range(0, Code->size()); 447 } 448 449 // Call clang-format. 450 auto Action = [File = File.str(), Code = std::move(*Code), 451 Ranges = std::vector<tooling::Range>{RequestedRange}, 452 CB = std::move(CB), this]() mutable { 453 format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS); 454 tooling::Replacements IncludeReplaces = 455 format::sortIncludes(Style, Code, Ranges, File); 456 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); 457 if (!Changed) 458 return CB(Changed.takeError()); 459 460 CB(IncludeReplaces.merge(format::reformat( 461 Style, *Changed, 462 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), 463 File))); 464 }; 465 WorkScheduler->runQuick("Format", File, std::move(Action)); 466 } 467 468 void ClangdServer::formatOnType(PathRef File, Position Pos, 469 StringRef TriggerText, 470 Callback<std::vector<TextEdit>> CB) { 471 auto Code = getDraft(File); 472 if (!Code) 473 return CB(llvm::make_error<LSPError>("trying to format non-added document", 474 ErrorCode::InvalidParams)); 475 llvm::Expected<size_t> CursorPos = positionToOffset(*Code, Pos); 476 if (!CursorPos) 477 return CB(CursorPos.takeError()); 478 auto Action = [File = File.str(), Code = std::move(*Code), 479 TriggerText = TriggerText.str(), CursorPos = *CursorPos, 480 CB = std::move(CB), this]() mutable { 481 auto Style = format::getStyle(format::DefaultFormatStyle, File, 482 format::DefaultFallbackStyle, Code, 483 TFS.view(/*CWD=*/llvm::None).get()); 484 if (!Style) 485 return CB(Style.takeError()); 486 487 std::vector<TextEdit> Result; 488 for (const tooling::Replacement &R : 489 formatIncremental(Code, CursorPos, TriggerText, *Style)) 490 Result.push_back(replacementToEdit(Code, R)); 491 return CB(Result); 492 }; 493 WorkScheduler->runQuick("FormatOnType", File, std::move(Action)); 494 } 495 496 void ClangdServer::prepareRename(PathRef File, Position Pos, 497 llvm::Optional<std::string> NewName, 498 const RenameOptions &RenameOpts, 499 Callback<RenameResult> CB) { 500 auto Action = [Pos, File = File.str(), CB = std::move(CB), 501 NewName = std::move(NewName), 502 RenameOpts](llvm::Expected<InputsAndAST> InpAST) mutable { 503 if (!InpAST) 504 return CB(InpAST.takeError()); 505 // prepareRename is latency-sensitive: we don't query the index, as we 506 // only need main-file references 507 auto Results = 508 clangd::rename({Pos, NewName.getValueOr("__clangd_rename_placeholder"), 509 InpAST->AST, File, /*FS=*/nullptr, 510 /*Index=*/nullptr, RenameOpts}); 511 if (!Results) { 512 // LSP says to return null on failure, but that will result in a generic 513 // failure message. If we send an LSP error response, clients can surface 514 // the message to users (VSCode does). 515 return CB(Results.takeError()); 516 } 517 return CB(*Results); 518 }; 519 WorkScheduler->runWithAST("PrepareRename", File, std::move(Action)); 520 } 521 522 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, 523 const RenameOptions &Opts, 524 Callback<RenameResult> CB) { 525 auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts, 526 CB = std::move(CB), 527 this](llvm::Expected<InputsAndAST> InpAST) mutable { 528 // Tracks number of files edited per invocation. 529 static constexpr trace::Metric RenameFiles("rename_files", 530 trace::Metric::Distribution); 531 if (!InpAST) 532 return CB(InpAST.takeError()); 533 auto R = clangd::rename({Pos, NewName, InpAST->AST, File, 534 DirtyFS->view(llvm::None), Index, Opts}); 535 if (!R) 536 return CB(R.takeError()); 537 538 if (Opts.WantFormat) { 539 auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, 540 *InpAST->Inputs.TFS); 541 llvm::Error Err = llvm::Error::success(); 542 for (auto &E : R->GlobalChanges) 543 Err = 544 llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); 545 546 if (Err) 547 return CB(std::move(Err)); 548 } 549 RenameFiles.record(R->GlobalChanges.size()); 550 return CB(*R); 551 }; 552 WorkScheduler->runWithAST("Rename", File, std::move(Action)); 553 } 554 555 // May generate several candidate selections, due to SelectionTree ambiguity. 556 // vector of pointers because GCC doesn't like non-copyable Selection. 557 static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>> 558 tweakSelection(const Range &Sel, const InputsAndAST &AST, 559 llvm::vfs::FileSystem *FS) { 560 auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start); 561 if (!Begin) 562 return Begin.takeError(); 563 auto End = positionToOffset(AST.Inputs.Contents, Sel.end); 564 if (!End) 565 return End.takeError(); 566 std::vector<std::unique_ptr<Tweak::Selection>> Result; 567 SelectionTree::createEach( 568 AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End, 569 [&](SelectionTree T) { 570 Result.push_back(std::make_unique<Tweak::Selection>( 571 AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T), FS)); 572 return false; 573 }); 574 assert(!Result.empty() && "Expected at least one SelectionTree"); 575 return std::move(Result); 576 } 577 578 void ClangdServer::enumerateTweaks( 579 PathRef File, Range Sel, llvm::unique_function<bool(const Tweak &)> Filter, 580 Callback<std::vector<TweakRef>> CB) { 581 // Tracks number of times a tweak has been offered. 582 static constexpr trace::Metric TweakAvailable( 583 "tweak_available", trace::Metric::Counter, "tweak_id"); 584 auto Action = [Sel, CB = std::move(CB), Filter = std::move(Filter), 585 FeatureModules(this->FeatureModules)]( 586 Expected<InputsAndAST> InpAST) mutable { 587 if (!InpAST) 588 return CB(InpAST.takeError()); 589 auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr); 590 if (!Selections) 591 return CB(Selections.takeError()); 592 std::vector<TweakRef> Res; 593 // Don't allow a tweak to fire more than once across ambiguous selections. 594 llvm::DenseSet<llvm::StringRef> PreparedTweaks; 595 auto DeduplicatingFilter = [&](const Tweak &T) { 596 return Filter(T) && !PreparedTweaks.count(T.id()); 597 }; 598 for (const auto &Sel : *Selections) { 599 for (auto &T : prepareTweaks(*Sel, DeduplicatingFilter, FeatureModules)) { 600 Res.push_back({T->id(), T->title(), T->kind()}); 601 PreparedTweaks.insert(T->id()); 602 TweakAvailable.record(1, T->id()); 603 } 604 } 605 606 CB(std::move(Res)); 607 }; 608 609 WorkScheduler->runWithAST("EnumerateTweaks", File, std::move(Action), 610 Transient); 611 } 612 613 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, 614 Callback<Tweak::Effect> CB) { 615 // Tracks number of times a tweak has been attempted. 616 static constexpr trace::Metric TweakAttempt( 617 "tweak_attempt", trace::Metric::Counter, "tweak_id"); 618 // Tracks number of times a tweak has failed to produce edits. 619 static constexpr trace::Metric TweakFailed( 620 "tweak_failed", trace::Metric::Counter, "tweak_id"); 621 TweakAttempt.record(1, TweakID); 622 auto Action = [File = File.str(), Sel, TweakID = TweakID.str(), 623 CB = std::move(CB), 624 this](Expected<InputsAndAST> InpAST) mutable { 625 if (!InpAST) 626 return CB(InpAST.takeError()); 627 auto FS = DirtyFS->view(llvm::None); 628 auto Selections = tweakSelection(Sel, *InpAST, FS.get()); 629 if (!Selections) 630 return CB(Selections.takeError()); 631 llvm::Optional<llvm::Expected<Tweak::Effect>> Effect; 632 // Try each selection, take the first one that prepare()s. 633 // If they all fail, Effect will hold get the last error. 634 for (const auto &Selection : *Selections) { 635 auto T = prepareTweak(TweakID, *Selection, FeatureModules); 636 if (T) { 637 Effect = (*T)->apply(*Selection); 638 break; 639 } 640 Effect = T.takeError(); 641 } 642 assert(Effect.hasValue() && "Expected at least one selection"); 643 if (*Effect && (*Effect)->FormatEdits) { 644 // Format tweaks that require it centrally here. 645 for (auto &It : (*Effect)->ApplyEdits) { 646 Edit &E = It.second; 647 format::FormatStyle Style = 648 getFormatStyleForFile(File, E.InitialCode, TFS); 649 if (llvm::Error Err = reformatEdit(E, Style)) 650 elog("Failed to format {0}: {1}", It.first(), std::move(Err)); 651 } 652 } else { 653 TweakFailed.record(1, TweakID); 654 } 655 return CB(std::move(*Effect)); 656 }; 657 WorkScheduler->runWithAST("ApplyTweak", File, std::move(Action)); 658 } 659 660 void ClangdServer::locateSymbolAt(PathRef File, Position Pos, 661 Callback<std::vector<LocatedSymbol>> CB) { 662 auto Action = [Pos, CB = std::move(CB), 663 this](llvm::Expected<InputsAndAST> InpAST) mutable { 664 if (!InpAST) 665 return CB(InpAST.takeError()); 666 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index)); 667 }; 668 669 WorkScheduler->runWithAST("Definitions", File, std::move(Action)); 670 } 671 672 void ClangdServer::switchSourceHeader( 673 PathRef Path, Callback<llvm::Optional<clangd::Path>> CB) { 674 // We want to return the result as fast as possible, strategy is: 675 // 1) use the file-only heuristic, it requires some IO but it is much 676 // faster than building AST, but it only works when .h/.cc files are in 677 // the same directory. 678 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports 679 // different code layout. 680 if (auto CorrespondingFile = 681 getCorrespondingHeaderOrSource(Path, TFS.view(llvm::None))) 682 return CB(std::move(CorrespondingFile)); 683 auto Action = [Path = Path.str(), CB = std::move(CB), 684 this](llvm::Expected<InputsAndAST> InpAST) mutable { 685 if (!InpAST) 686 return CB(InpAST.takeError()); 687 CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index)); 688 }; 689 WorkScheduler->runWithAST("SwitchHeaderSource", Path, std::move(Action)); 690 } 691 692 void ClangdServer::findDocumentHighlights( 693 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) { 694 auto Action = 695 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 696 if (!InpAST) 697 return CB(InpAST.takeError()); 698 CB(clangd::findDocumentHighlights(InpAST->AST, Pos)); 699 }; 700 701 WorkScheduler->runWithAST("Highlights", File, std::move(Action), Transient); 702 } 703 704 void ClangdServer::findHover(PathRef File, Position Pos, 705 Callback<llvm::Optional<HoverInfo>> CB) { 706 auto Action = [File = File.str(), Pos, CB = std::move(CB), 707 this](llvm::Expected<InputsAndAST> InpAST) mutable { 708 if (!InpAST) 709 return CB(InpAST.takeError()); 710 format::FormatStyle Style = getFormatStyleForFile( 711 File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS); 712 CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index)); 713 }; 714 715 WorkScheduler->runWithAST("Hover", File, std::move(Action), Transient); 716 } 717 718 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve, 719 TypeHierarchyDirection Direction, 720 Callback<Optional<TypeHierarchyItem>> CB) { 721 auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB), 722 this](Expected<InputsAndAST> InpAST) mutable { 723 if (!InpAST) 724 return CB(InpAST.takeError()); 725 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index, 726 File)); 727 }; 728 729 WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action)); 730 } 731 732 void ClangdServer::resolveTypeHierarchy( 733 TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, 734 Callback<llvm::Optional<TypeHierarchyItem>> CB) { 735 WorkScheduler->run( 736 "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { 737 clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); 738 CB(Item); 739 }); 740 } 741 742 void ClangdServer::prepareCallHierarchy( 743 PathRef File, Position Pos, Callback<std::vector<CallHierarchyItem>> CB) { 744 auto Action = [File = File.str(), Pos, 745 CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable { 746 if (!InpAST) 747 return CB(InpAST.takeError()); 748 CB(clangd::prepareCallHierarchy(InpAST->AST, Pos, File)); 749 }; 750 WorkScheduler->runWithAST("CallHierarchy", File, std::move(Action)); 751 } 752 753 void ClangdServer::incomingCalls( 754 const CallHierarchyItem &Item, 755 Callback<std::vector<CallHierarchyIncomingCall>> CB) { 756 WorkScheduler->run("Incoming Calls", "", 757 [CB = std::move(CB), Item, this]() mutable { 758 CB(clangd::incomingCalls(Item, Index)); 759 }); 760 } 761 762 void ClangdServer::inlayHints(PathRef File, 763 Callback<std::vector<InlayHint>> CB) { 764 auto Action = [CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable { 765 if (!InpAST) 766 return CB(InpAST.takeError()); 767 CB(clangd::inlayHints(InpAST->AST)); 768 }; 769 WorkScheduler->runWithAST("InlayHints", File, std::move(Action)); 770 } 771 772 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { 773 // FIXME: Do nothing for now. This will be used for indexing and potentially 774 // invalidating other caches. 775 } 776 777 void ClangdServer::workspaceSymbols( 778 llvm::StringRef Query, int Limit, 779 Callback<std::vector<SymbolInformation>> CB) { 780 WorkScheduler->run( 781 "getWorkspaceSymbols", /*Path=*/"", 782 [Query = Query.str(), Limit, CB = std::move(CB), this]() mutable { 783 CB(clangd::getWorkspaceSymbols(Query, Limit, Index, 784 WorkspaceRoot.getValueOr(""))); 785 }); 786 } 787 788 void ClangdServer::documentSymbols(llvm::StringRef File, 789 Callback<std::vector<DocumentSymbol>> CB) { 790 auto Action = 791 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 792 if (!InpAST) 793 return CB(InpAST.takeError()); 794 CB(clangd::getDocumentSymbols(InpAST->AST)); 795 }; 796 WorkScheduler->runWithAST("DocumentSymbols", File, std::move(Action), 797 Transient); 798 } 799 800 void ClangdServer::foldingRanges(llvm::StringRef File, 801 Callback<std::vector<FoldingRange>> CB) { 802 auto Action = 803 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 804 if (!InpAST) 805 return CB(InpAST.takeError()); 806 CB(clangd::getFoldingRanges(InpAST->AST)); 807 }; 808 WorkScheduler->runWithAST("FoldingRanges", File, std::move(Action), 809 Transient); 810 } 811 812 void ClangdServer::findImplementations( 813 PathRef File, Position Pos, Callback<std::vector<LocatedSymbol>> CB) { 814 auto Action = [Pos, CB = std::move(CB), 815 this](llvm::Expected<InputsAndAST> InpAST) mutable { 816 if (!InpAST) 817 return CB(InpAST.takeError()); 818 CB(clangd::findImplementations(InpAST->AST, Pos, Index)); 819 }; 820 821 WorkScheduler->runWithAST("Implementations", File, std::move(Action)); 822 } 823 824 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, 825 Callback<ReferencesResult> CB) { 826 auto Action = [Pos, Limit, CB = std::move(CB), 827 this](llvm::Expected<InputsAndAST> InpAST) mutable { 828 if (!InpAST) 829 return CB(InpAST.takeError()); 830 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index)); 831 }; 832 833 WorkScheduler->runWithAST("References", File, std::move(Action)); 834 } 835 836 void ClangdServer::symbolInfo(PathRef File, Position Pos, 837 Callback<std::vector<SymbolDetails>> CB) { 838 auto Action = 839 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 840 if (!InpAST) 841 return CB(InpAST.takeError()); 842 CB(clangd::getSymbolInfo(InpAST->AST, Pos)); 843 }; 844 845 WorkScheduler->runWithAST("SymbolInfo", File, std::move(Action)); 846 } 847 848 void ClangdServer::semanticRanges(PathRef File, 849 const std::vector<Position> &Positions, 850 Callback<std::vector<SelectionRange>> CB) { 851 auto Action = [Positions, CB = std::move(CB)]( 852 llvm::Expected<InputsAndAST> InpAST) mutable { 853 if (!InpAST) 854 return CB(InpAST.takeError()); 855 std::vector<SelectionRange> Result; 856 for (const auto &Pos : Positions) { 857 if (auto Range = clangd::getSemanticRanges(InpAST->AST, Pos)) 858 Result.push_back(std::move(*Range)); 859 else 860 return CB(Range.takeError()); 861 } 862 CB(std::move(Result)); 863 }; 864 WorkScheduler->runWithAST("SemanticRanges", File, std::move(Action)); 865 } 866 867 void ClangdServer::documentLinks(PathRef File, 868 Callback<std::vector<DocumentLink>> CB) { 869 auto Action = 870 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 871 if (!InpAST) 872 return CB(InpAST.takeError()); 873 CB(clangd::getDocumentLinks(InpAST->AST)); 874 }; 875 WorkScheduler->runWithAST("DocumentLinks", File, std::move(Action), 876 Transient); 877 } 878 879 void ClangdServer::semanticHighlights( 880 PathRef File, Callback<std::vector<HighlightingToken>> CB) { 881 auto Action = 882 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 883 if (!InpAST) 884 return CB(InpAST.takeError()); 885 CB(clangd::getSemanticHighlightings(InpAST->AST)); 886 }; 887 WorkScheduler->runWithAST("SemanticHighlights", File, std::move(Action), 888 Transient); 889 } 890 891 void ClangdServer::getAST(PathRef File, llvm::Optional<Range> R, 892 Callback<llvm::Optional<ASTNode>> CB) { 893 auto Action = 894 [R, CB(std::move(CB))](llvm::Expected<InputsAndAST> Inputs) mutable { 895 if (!Inputs) 896 return CB(Inputs.takeError()); 897 if (!R) { 898 // It's safe to pass in the TU, as dumpAST() does not 899 // deserialize the preamble. 900 auto Node = DynTypedNode::create( 901 *Inputs->AST.getASTContext().getTranslationUnitDecl()); 902 return CB(dumpAST(Node, Inputs->AST.getTokens(), 903 Inputs->AST.getASTContext())); 904 } 905 unsigned Start, End; 906 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->start)) 907 Start = *Offset; 908 else 909 return CB(Offset.takeError()); 910 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->end)) 911 End = *Offset; 912 else 913 return CB(Offset.takeError()); 914 bool Success = SelectionTree::createEach( 915 Inputs->AST.getASTContext(), Inputs->AST.getTokens(), Start, End, 916 [&](SelectionTree T) { 917 if (const SelectionTree::Node *N = T.commonAncestor()) { 918 CB(dumpAST(N->ASTNode, Inputs->AST.getTokens(), 919 Inputs->AST.getASTContext())); 920 return true; 921 } 922 return false; 923 }); 924 if (!Success) 925 CB(llvm::None); 926 }; 927 WorkScheduler->runWithAST("GetAST", File, std::move(Action)); 928 } 929 930 void ClangdServer::customAction(PathRef File, llvm::StringRef Name, 931 Callback<InputsAndAST> Action) { 932 WorkScheduler->runWithAST(Name, File, std::move(Action)); 933 } 934 935 void ClangdServer::diagnostics(PathRef File, Callback<std::vector<Diag>> CB) { 936 auto Action = 937 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 938 if (!InpAST) 939 return CB(InpAST.takeError()); 940 if (auto Diags = InpAST->AST.getDiagnostics()) 941 return CB(*Diags); 942 // FIXME: Use ServerCancelled error once it is settled in LSP-3.17. 943 return CB(llvm::make_error<LSPError>("server is busy parsing includes", 944 ErrorCode::InternalError)); 945 }; 946 947 WorkScheduler->runWithAST("Diagnostics", File, std::move(Action)); 948 } 949 950 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const { 951 return WorkScheduler->fileStats(); 952 } 953 954 LLVM_NODISCARD bool 955 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) { 956 // Order is important here: we don't want to block on A and then B, 957 // if B might schedule work on A. 958 959 // Nothing else can schedule work on TUScheduler, because it's not threadsafe 960 // and we're blocking the main thread. 961 if (!WorkScheduler->blockUntilIdle(timeoutSeconds(TimeoutSeconds))) 962 return false; 963 964 // Unfortunately we don't have strict topological order between the rest of 965 // the components. E.g. CDB broadcast triggers backrgound indexing. 966 // This queries the CDB which may discover new work if disk has changed. 967 // 968 // So try each one a few times in a loop. 969 // If there are no tricky interactions then all after the first are no-ops. 970 // Then on the last iteration, verify they're idle without waiting. 971 // 972 // There's a small chance they're juggling work and we didn't catch them :-( 973 for (llvm::Optional<double> Timeout : 974 {TimeoutSeconds, TimeoutSeconds, llvm::Optional<double>(0)}) { 975 if (!CDB.blockUntilIdle(timeoutSeconds(Timeout))) 976 return false; 977 if (BackgroundIdx && !BackgroundIdx->blockUntilIdleForTest(Timeout)) 978 return false; 979 if (FeatureModules && llvm::any_of(*FeatureModules, [&](FeatureModule &M) { 980 return !M.blockUntilIdle(timeoutSeconds(Timeout)); 981 })) 982 return false; 983 } 984 985 assert(WorkScheduler->blockUntilIdle(Deadline::zero()) && 986 "Something scheduled work while we're blocking the main thread!"); 987 return true; 988 } 989 990 void ClangdServer::profile(MemoryTree &MT) const { 991 if (DynamicIdx) 992 DynamicIdx->profile(MT.child("dynamic_index")); 993 if (BackgroundIdx) 994 BackgroundIdx->profile(MT.child("background_index")); 995 WorkScheduler->profile(MT.child("tuscheduler")); 996 } 997 } // namespace clangd 998 } // namespace clang 999