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 "InlayHints.h" 18 #include "ParsedAST.h" 19 #include "Preamble.h" 20 #include "Protocol.h" 21 #include "SemanticHighlighting.h" 22 #include "SemanticSelection.h" 23 #include "SourceCode.h" 24 #include "TUScheduler.h" 25 #include "XRefs.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" 48 #include <algorithm> 49 #include <chrono> 50 #include <future> 51 #include <memory> 52 #include <mutex> 53 #include <string> 54 #include <type_traits> 55 56 namespace clang { 57 namespace clangd { 58 namespace { 59 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), 66 Tasks(Tasks) {} 67 68 void onPreambleAST(PathRef Path, llvm::StringRef Version, 69 const CompilerInvocation &CI, ASTContext &Ctx, 70 Preprocessor &PP, 71 const CanonicalIncludes &CanonIncludes) override { 72 // If this preamble uses a standard library we haven't seen yet, index it. 73 if (FIndex) 74 if (auto Loc = Stdlib.add(*CI.getLangOpts(), PP.getHeaderSearchInfo())) 75 indexStdlib(CI, std::move(*Loc)); 76 77 if (FIndex) 78 FIndex->updatePreamble(Path, Version, Ctx, PP, CanonIncludes); 79 } 80 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 { 84 IndexFileIn IF; 85 IF.Symbols = indexStandardLibrary(std::move(CI), Loc, TFS); 86 if (Stdlib.isBest(LO)) 87 FIndex->updatePreamble(std::move(IF)); 88 }; 89 if (Tasks) 90 // This doesn't have a semaphore to enforce -j, but it's rare. 91 Tasks->runAsync("IndexStdlib", std::move(Task)); 92 else 93 Task(); 94 } 95 96 void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override { 97 if (FIndex) 98 FIndex->updateMain(Path, AST); 99 100 assert(AST.getDiagnostics() && 101 "We issue callback only with fresh preambles"); 102 std::vector<Diag> Diagnostics = *AST.getDiagnostics(); 103 if (ServerCallbacks) 104 Publish([&]() { 105 ServerCallbacks->onDiagnosticsReady(Path, AST.version(), 106 std::move(Diagnostics)); 107 }); 108 } 109 110 void onFailedAST(PathRef Path, llvm::StringRef Version, 111 std::vector<Diag> Diags, PublishFn Publish) override { 112 if (ServerCallbacks) 113 Publish( 114 [&]() { ServerCallbacks->onDiagnosticsReady(Path, Version, Diags); }); 115 } 116 117 void onFileUpdated(PathRef File, const TUStatus &Status) override { 118 if (ServerCallbacks) 119 ServerCallbacks->onFileUpdated(File, Status); 120 } 121 122 void onPreamblePublished(PathRef File) override { 123 if (ServerCallbacks) 124 ServerCallbacks->onSemanticsMaybeChanged(File); 125 } 126 127 private: 128 FileIndex *FIndex; 129 ClangdServer::Callbacks *ServerCallbacks; 130 const ThreadsafeFS &TFS; 131 StdLibSet Stdlib; 132 AsyncTaskRunner *Tasks; 133 }; 134 135 class DraftStoreFS : public ThreadsafeFS { 136 public: 137 DraftStoreFS(const ThreadsafeFS &Base, const DraftStore &Drafts) 138 : Base(Base), DirtyFiles(Drafts) {} 139 140 private: 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()); 145 return OFS; 146 } 147 148 const ThreadsafeFS &Base; 149 const DraftStore &DirtyFiles; 150 }; 151 152 } // namespace 153 154 ClangdServer::Options ClangdServer::optsForTest() { 155 ClangdServer::Options Opts; 156 Opts.UpdateDebounce = DebouncePolicy::fixed(/*zero*/ {}); 157 Opts.StorePreamblesInMemory = true; 158 Opts.AsyncThreadsCount = 4; // Consistent! 159 return Opts; 160 } 161 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; 170 return Opts; 171 } 172 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 189 // is parsed. 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(); 199 } else { 200 this->Index = Idx; 201 } 202 }; 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) { 209 if (Callbacks) 210 Callbacks->onBackgroundIndexProgress(S); 211 }; 212 BGOpts.ContextProvider = Opts.ContextProvider; 213 BackgroundIdx = std::make_unique<BackgroundIndex>( 214 TFS, CDB, 215 BackgroundIndexStorage::createDiskBackedStorageFactory( 216 [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }), 217 std::move(BGOpts)); 218 AddIndex(BackgroundIdx.get()); 219 } 220 if (DynamicIdx) 221 AddIndex(DynamicIdx.get()); 222 223 if (Opts.FeatureModules) { 224 FeatureModule::Facilities F{ 225 *this->WorkScheduler, 226 this->Index, 227 this->TFS, 228 }; 229 for (auto &Mod : *Opts.FeatureModules) 230 Mod.initialize(F); 231 } 232 } 233 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) 242 Mod.stop(); 243 for (auto &Mod : *FeatureModules) 244 Mod.blockUntilIdle(Deadline::infinity()); 245 } 246 } 247 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); 252 ParseOptions Opts; 253 Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions; 254 255 // Compile command is set asynchronously during update, as it can be slow. 256 ParseInputs Inputs; 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); 269 } 270 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); 279 } 280 281 std::shared_ptr<const std::string> ClangdServer::getDraft(PathRef File) const { 282 auto Draft = DraftMgr.getDraft(File); 283 if (!Draft) 284 return nullptr; 285 return std::move(Draft->Contents); 286 } 287 288 std::function<Context(PathRef)> 289 ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, 290 Callbacks *Publish) { 291 if (!Provider) 292 return [](llvm::StringRef) { return Context::current().clone(); }; 293 294 struct Impl { 295 const config::Provider *Provider; 296 ClangdServer::Callbacks *Publish; 297 std::mutex PublishMu; 298 299 Impl(const config::Provider *Provider, ClangdServer::Callbacks *Publish) 300 : Provider(Provider), Publish(Publish) {} 301 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? 306 Params.FreshTime = 307 std::chrono::steady_clock::now() - std::chrono::seconds(5); 308 llvm::SmallString<256> PosixPath; 309 if (!File.empty()) { 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(); 313 } 314 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() 321 ? nullptr 322 : &ReportableDiagnostics[D.getFilename()]); 323 }); 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)); 332 } 333 return Context::current().derive(Config::Key, std::move(C)); 334 } 335 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()); 342 break; 343 case llvm::SourceMgr::DK_Warning: 344 log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), 345 D.getLineNo(), D.getColumnNo(), D.getMessage()); 346 break; 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. 352 break; 353 } 354 if (ClientDiagnostics) 355 ClientDiagnostics->push_back(toDiag(D, Diag::ClangdConfig)); 356 } 357 }; 358 359 // Copyable wrapper. 360 return [I(std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) { 361 return (*I)(Path); 362 }; 363 } 364 365 void ClangdServer::removeDocument(PathRef File) { 366 DraftMgr.removeDraft(File); 367 WorkScheduler->remove(File); 368 } 369 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; 377 378 auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB), 379 this](llvm::Expected<InputsAndPreamble> IP) mutable { 380 if (!IP) 381 return CB(IP.takeError()); 382 if (auto Reason = isCancelled()) 383 return CB(llvm::make_error<CancelledError>(Reason)); 384 385 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; 386 if (!IP->Preamble) { 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(); 392 { 393 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); 394 SpecFuzzyFind->CachedReq = CachedCompletionFuzzyFindRequestByFile[File]; 395 } 396 } 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; 403 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); 411 { 412 clang::clangd::trace::Span Tracer("Completion results callback"); 413 CB(std::move(Result)); 414 } 415 if (SpecFuzzyFind && SpecFuzzyFind->NewReq) { 416 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); 417 CachedCompletionFuzzyFindRequestByFile[File] = 418 SpecFuzzyFind->NewReq.value(); 419 } 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. 424 }; 425 426 // We use a potentially-stale preamble because latency is critical here. 427 WorkScheduler->runWithPreamble( 428 "CodeComplete", File, 429 (Opts.RunParser == CodeCompleteOptions::AlwaysParse) 430 ? TUScheduler::Stale 431 : TUScheduler::StaleOrAbsent, 432 std::move(Task)); 433 } 434 435 void ClangdServer::signatureHelp(PathRef File, Position Pos, 436 MarkupKind DocumentationFormat, 437 Callback<SignatureHelp> CB) { 438 439 auto Action = [Pos, File = File.str(), CB = std::move(CB), 440 DocumentationFormat, 441 this](llvm::Expected<InputsAndPreamble> IP) mutable { 442 if (!IP) 443 return CB(IP.takeError()); 444 445 const auto *PreambleData = IP->Preamble; 446 if (!PreambleData) 447 return CB(error("Failed to parse includes")); 448 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)); 457 }; 458 459 // Unlike code completion, we wait for a preamble here. 460 WorkScheduler->runWithPreamble("SignatureHelp", File, TUScheduler::Stale, 461 std::move(Action)); 462 } 463 464 void ClangdServer::formatFile(PathRef File, llvm::Optional<Range> Rng, 465 Callback<tooling::Replacements> CB) { 466 auto Code = getDraft(File); 467 if (!Code) 468 return CB(llvm::make_error<LSPError>("trying to format non-added document", 469 ErrorCode::InvalidParams)); 470 tooling::Range RequestedRange; 471 if (Rng) { 472 llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start); 473 if (!Begin) 474 return CB(Begin.takeError()); 475 llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end); 476 if (!End) 477 return CB(End.takeError()); 478 RequestedRange = tooling::Range(*Begin, *End - *Begin); 479 } else { 480 RequestedRange = tooling::Range(0, Code->size()); 481 } 482 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); 491 if (!Changed) 492 return CB(Changed.takeError()); 493 494 CB(IncludeReplaces.merge(format::reformat( 495 Style, *Changed, 496 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), 497 File))); 498 }; 499 WorkScheduler->runQuick("Format", File, std::move(Action)); 500 } 501 502 void ClangdServer::formatOnType(PathRef File, Position Pos, 503 StringRef TriggerText, 504 Callback<std::vector<TextEdit>> CB) { 505 auto Code = getDraft(File); 506 if (!Code) 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); 510 if (!CursorPos) 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()); 518 if (!Style) 519 return CB(Style.takeError()); 520 521 std::vector<TextEdit> Result; 522 for (const tooling::Replacement &R : 523 formatIncremental(Code, CursorPos, TriggerText, *Style)) 524 Result.push_back(replacementToEdit(Code, R)); 525 return CB(Result); 526 }; 527 WorkScheduler->runQuick("FormatOnType", File, std::move(Action)); 528 } 529 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 { 537 if (!InpAST) 538 return CB(InpAST.takeError()); 539 // prepareRename is latency-sensitive: we don't query the index, as we 540 // only need main-file references 541 auto Results = 542 clangd::rename({Pos, NewName.value_or("__clangd_rename_placeholder"), 543 InpAST->AST, File, /*FS=*/nullptr, 544 /*Index=*/nullptr, RenameOpts}); 545 if (!Results) { 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()); 550 } 551 return CB(*Results); 552 }; 553 WorkScheduler->runWithAST("PrepareRename", File, std::move(Action)); 554 } 555 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, 560 CB = std::move(CB), 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); 565 if (!InpAST) 566 return CB(InpAST.takeError()); 567 auto R = clangd::rename({Pos, NewName, InpAST->AST, File, 568 DirtyFS->view(llvm::None), Index, Opts}); 569 if (!R) 570 return CB(R.takeError()); 571 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) 577 Err = 578 llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); 579 580 if (Err) 581 return CB(std::move(Err)); 582 } 583 RenameFiles.record(R->GlobalChanges.size()); 584 return CB(*R); 585 }; 586 WorkScheduler->runWithAST("Rename", File, std::move(Action)); 587 } 588 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); 595 if (!Begin) 596 return Begin.takeError(); 597 auto End = positionToOffset(AST.Inputs.Contents, Sel.end); 598 if (!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)); 606 return false; 607 }); 608 assert(!Result.empty() && "Expected at least one SelectionTree"); 609 return std::move(Result); 610 } 611 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 { 621 if (!InpAST) 622 return CB(InpAST.takeError()); 623 auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr); 624 if (!Selections) 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()); 631 }; 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()); 637 } 638 } 639 640 CB(std::move(Res)); 641 }; 642 643 WorkScheduler->runWithAST("EnumerateTweaks", File, std::move(Action), 644 Transient); 645 } 646 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(), 657 CB = std::move(CB), 658 this](Expected<InputsAndAST> InpAST) mutable { 659 if (!InpAST) 660 return CB(InpAST.takeError()); 661 auto FS = DirtyFS->view(llvm::None); 662 auto Selections = tweakSelection(Sel, *InpAST, FS.get()); 663 if (!Selections) 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); 670 if (T) { 671 Effect = (*T)->apply(*Selection); 672 break; 673 } 674 Effect = T.takeError(); 675 } 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) { 680 Edit &E = It.second; 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)); 685 } 686 } else { 687 TweakFailed.record(1, TweakID); 688 } 689 return CB(std::move(*Effect)); 690 }; 691 WorkScheduler->runWithAST("ApplyTweak", File, std::move(Action)); 692 } 693 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 { 698 if (!InpAST) 699 return CB(InpAST.takeError()); 700 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index)); 701 }; 702 703 WorkScheduler->runWithAST("Definitions", File, std::move(Action)); 704 } 705 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 { 719 if (!InpAST) 720 return CB(InpAST.takeError()); 721 CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index)); 722 }; 723 WorkScheduler->runWithAST("SwitchHeaderSource", Path, std::move(Action)); 724 } 725 726 void ClangdServer::findDocumentHighlights( 727 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) { 728 auto Action = 729 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 730 if (!InpAST) 731 return CB(InpAST.takeError()); 732 CB(clangd::findDocumentHighlights(InpAST->AST, Pos)); 733 }; 734 735 WorkScheduler->runWithAST("Highlights", File, std::move(Action), Transient); 736 } 737 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 { 742 if (!InpAST) 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)); 747 }; 748 749 WorkScheduler->runWithAST("Hover", File, std::move(Action), Transient); 750 } 751 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 { 757 if (!InpAST) 758 return CB(InpAST.takeError()); 759 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index, 760 File)); 761 }; 762 763 WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action)); 764 } 765 766 void ClangdServer::resolveTypeHierarchy( 767 TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, 768 Callback<llvm::Optional<TypeHierarchyItem>> CB) { 769 WorkScheduler->run( 770 "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { 771 clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); 772 CB(Item); 773 }); 774 } 775 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 { 780 if (!InpAST) 781 return CB(InpAST.takeError()); 782 CB(clangd::prepareCallHierarchy(InpAST->AST, Pos, File)); 783 }; 784 WorkScheduler->runWithAST("CallHierarchy", File, std::move(Action)); 785 } 786 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)); 793 }); 794 } 795 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 { 800 if (!InpAST) 801 return CB(InpAST.takeError()); 802 CB(clangd::inlayHints(InpAST->AST, std::move(RestrictRange))); 803 }; 804 WorkScheduler->runWithAST("InlayHints", File, std::move(Action), Transient); 805 } 806 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. 810 } 811 812 void ClangdServer::workspaceSymbols( 813 llvm::StringRef Query, int Limit, 814 Callback<std::vector<SymbolInformation>> CB) { 815 WorkScheduler->run( 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(""))); 820 }); 821 } 822 823 void ClangdServer::documentSymbols(llvm::StringRef File, 824 Callback<std::vector<DocumentSymbol>> CB) { 825 auto Action = 826 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 827 if (!InpAST) 828 return CB(InpAST.takeError()); 829 CB(clangd::getDocumentSymbols(InpAST->AST)); 830 }; 831 WorkScheduler->runWithAST("DocumentSymbols", File, std::move(Action), 832 Transient); 833 } 834 835 void ClangdServer::foldingRanges(llvm::StringRef File, 836 Callback<std::vector<FoldingRange>> CB) { 837 auto Action = 838 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 839 if (!InpAST) 840 return CB(InpAST.takeError()); 841 CB(clangd::getFoldingRanges(InpAST->AST)); 842 }; 843 WorkScheduler->runWithAST("FoldingRanges", File, std::move(Action), 844 Transient); 845 } 846 847 void ClangdServer::findType(llvm::StringRef File, Position Pos, 848 Callback<std::vector<LocatedSymbol>> CB) { 849 auto Action = 850 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 851 if (!InpAST) 852 return CB(InpAST.takeError()); 853 CB(clangd::findType(InpAST->AST, Pos)); 854 }; 855 WorkScheduler->runWithAST("FindType", File, std::move(Action)); 856 } 857 858 void ClangdServer::findImplementations( 859 PathRef File, Position Pos, Callback<std::vector<LocatedSymbol>> CB) { 860 auto Action = [Pos, CB = std::move(CB), 861 this](llvm::Expected<InputsAndAST> InpAST) mutable { 862 if (!InpAST) 863 return CB(InpAST.takeError()); 864 CB(clangd::findImplementations(InpAST->AST, Pos, Index)); 865 }; 866 867 WorkScheduler->runWithAST("Implementations", File, std::move(Action)); 868 } 869 870 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, 871 Callback<ReferencesResult> CB) { 872 auto Action = [Pos, Limit, CB = std::move(CB), 873 this](llvm::Expected<InputsAndAST> InpAST) mutable { 874 if (!InpAST) 875 return CB(InpAST.takeError()); 876 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index)); 877 }; 878 879 WorkScheduler->runWithAST("References", File, std::move(Action)); 880 } 881 882 void ClangdServer::symbolInfo(PathRef File, Position Pos, 883 Callback<std::vector<SymbolDetails>> CB) { 884 auto Action = 885 [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 886 if (!InpAST) 887 return CB(InpAST.takeError()); 888 CB(clangd::getSymbolInfo(InpAST->AST, Pos)); 889 }; 890 891 WorkScheduler->runWithAST("SymbolInfo", File, std::move(Action)); 892 } 893 894 void ClangdServer::semanticRanges(PathRef File, 895 const std::vector<Position> &Positions, 896 Callback<std::vector<SelectionRange>> CB) { 897 auto Action = [Positions, CB = std::move(CB)]( 898 llvm::Expected<InputsAndAST> InpAST) mutable { 899 if (!InpAST) 900 return CB(InpAST.takeError()); 901 std::vector<SelectionRange> Result; 902 for (const auto &Pos : Positions) { 903 if (auto Range = clangd::getSemanticRanges(InpAST->AST, Pos)) 904 Result.push_back(std::move(*Range)); 905 else 906 return CB(Range.takeError()); 907 } 908 CB(std::move(Result)); 909 }; 910 WorkScheduler->runWithAST("SemanticRanges", File, std::move(Action)); 911 } 912 913 void ClangdServer::documentLinks(PathRef File, 914 Callback<std::vector<DocumentLink>> CB) { 915 auto Action = 916 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 917 if (!InpAST) 918 return CB(InpAST.takeError()); 919 CB(clangd::getDocumentLinks(InpAST->AST)); 920 }; 921 WorkScheduler->runWithAST("DocumentLinks", File, std::move(Action), 922 Transient); 923 } 924 925 void ClangdServer::semanticHighlights( 926 PathRef File, Callback<std::vector<HighlightingToken>> CB) { 927 auto Action = 928 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 929 if (!InpAST) 930 return CB(InpAST.takeError()); 931 CB(clangd::getSemanticHighlightings(InpAST->AST)); 932 }; 933 WorkScheduler->runWithAST("SemanticHighlights", File, std::move(Action), 934 Transient); 935 } 936 937 void ClangdServer::getAST(PathRef File, llvm::Optional<Range> R, 938 Callback<llvm::Optional<ASTNode>> CB) { 939 auto Action = 940 [R, CB(std::move(CB))](llvm::Expected<InputsAndAST> Inputs) mutable { 941 if (!Inputs) 942 return CB(Inputs.takeError()); 943 if (!R) { 944 // It's safe to pass in the TU, as dumpAST() does not 945 // deserialize the preamble. 946 auto Node = DynTypedNode::create( 947 *Inputs->AST.getASTContext().getTranslationUnitDecl()); 948 return CB(dumpAST(Node, Inputs->AST.getTokens(), 949 Inputs->AST.getASTContext())); 950 } 951 unsigned Start, End; 952 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->start)) 953 Start = *Offset; 954 else 955 return CB(Offset.takeError()); 956 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->end)) 957 End = *Offset; 958 else 959 return CB(Offset.takeError()); 960 bool Success = SelectionTree::createEach( 961 Inputs->AST.getASTContext(), Inputs->AST.getTokens(), Start, End, 962 [&](SelectionTree T) { 963 if (const SelectionTree::Node *N = T.commonAncestor()) { 964 CB(dumpAST(N->ASTNode, Inputs->AST.getTokens(), 965 Inputs->AST.getASTContext())); 966 return true; 967 } 968 return false; 969 }); 970 if (!Success) 971 CB(llvm::None); 972 }; 973 WorkScheduler->runWithAST("GetAST", File, std::move(Action)); 974 } 975 976 void ClangdServer::customAction(PathRef File, llvm::StringRef Name, 977 Callback<InputsAndAST> Action) { 978 WorkScheduler->runWithAST(Name, File, std::move(Action)); 979 } 980 981 void ClangdServer::diagnostics(PathRef File, Callback<std::vector<Diag>> CB) { 982 auto Action = 983 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { 984 if (!InpAST) 985 return CB(InpAST.takeError()); 986 if (auto Diags = InpAST->AST.getDiagnostics()) 987 return CB(*Diags); 988 // FIXME: Use ServerCancelled error once it is settled in LSP-3.17. 989 return CB(llvm::make_error<LSPError>("server is busy parsing includes", 990 ErrorCode::InternalError)); 991 }; 992 993 WorkScheduler->runWithAST("Diagnostics", File, std::move(Action)); 994 } 995 996 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const { 997 return WorkScheduler->fileStats(); 998 } 999 1000 LLVM_NODISCARD bool 1001 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) { 1002 // Order is important here: we don't want to block on A and then B, 1003 // if B might schedule work on A. 1004 1005 // Nothing else can schedule work on TUScheduler, because it's not threadsafe 1006 // and we're blocking the main thread. 1007 if (!WorkScheduler->blockUntilIdle(timeoutSeconds(TimeoutSeconds))) 1008 return false; 1009 // TUScheduler is the only thing that starts background indexing work. 1010 if (IndexTasks && !IndexTasks->wait(timeoutSeconds(TimeoutSeconds))) 1011 return false; 1012 1013 // Unfortunately we don't have strict topological order between the rest of 1014 // the components. E.g. CDB broadcast triggers backrgound indexing. 1015 // This queries the CDB which may discover new work if disk has changed. 1016 // 1017 // So try each one a few times in a loop. 1018 // If there are no tricky interactions then all after the first are no-ops. 1019 // Then on the last iteration, verify they're idle without waiting. 1020 // 1021 // There's a small chance they're juggling work and we didn't catch them :-( 1022 for (llvm::Optional<double> Timeout : 1023 {TimeoutSeconds, TimeoutSeconds, llvm::Optional<double>(0)}) { 1024 if (!CDB.blockUntilIdle(timeoutSeconds(Timeout))) 1025 return false; 1026 if (BackgroundIdx && !BackgroundIdx->blockUntilIdleForTest(Timeout)) 1027 return false; 1028 if (FeatureModules && llvm::any_of(*FeatureModules, [&](FeatureModule &M) { 1029 return !M.blockUntilIdle(timeoutSeconds(Timeout)); 1030 })) 1031 return false; 1032 } 1033 1034 assert(WorkScheduler->blockUntilIdle(Deadline::zero()) && 1035 "Something scheduled work while we're blocking the main thread!"); 1036 return true; 1037 } 1038 1039 void ClangdServer::profile(MemoryTree &MT) const { 1040 if (DynamicIdx) 1041 DynamicIdx->profile(MT.child("dynamic_index")); 1042 if (BackgroundIdx) 1043 BackgroundIdx->profile(MT.child("background_index")); 1044 WorkScheduler->profile(MT.child("tuscheduler")); 1045 } 1046 } // namespace clangd 1047 } // namespace clang 1048