comparison clang-tools-extra/clangd/ClangdServer.cpp @ 221:79ff65ed7e25

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