clang-tools 23.0.0git
ProjectModules.cpp
Go to the documentation of this file.
1//===------------------ ProjectModules.cpp --------- ------------*- 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 "ProjectModules.h"
10#include "Compiler.h"
11#include "support/Logger.h"
12#include "clang/DependencyScanning/DependencyScanningService.h"
13#include "clang/Tooling/DependencyScanningTool.h"
14#include "clang/Tooling/Tooling.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/ADT/StringMap.h"
17#include "llvm/ADT/StringSet.h"
18#include "llvm/Support/CommandLine.h"
19#include "llvm/Support/Path.h"
20#include "llvm/TargetParser/Host.h"
21
22namespace clang::clangd {
23namespace {
24
25llvm::SmallString<128> normalizePath(PathRef Path) {
26 llvm::SmallString<128> Result(Path);
27 llvm::sys::path::remove_dots(Result, /*remove_dot_dot=*/true);
28 llvm::sys::path::native(Result, llvm::sys::path::Style::posix);
29 return Result;
30}
31
32std::string normalizePath(PathRef Path, PathRef WorkingDir) {
33 if (Path.empty())
34 return {};
35
36 llvm::SmallString<128> Result;
37 if (llvm::sys::path::is_absolute(Path) || WorkingDir.empty())
38 Result = Path;
39 else {
40 Result = WorkingDir;
41 llvm::sys::path::append(Result, Path);
42 }
43
44 return normalizePath(Result).str().str();
45}
46
47/// The information related to modules parsed from compile commands.
48/// Including the source file, the module file it produces (if it is a
49/// producer), and the module and the corresponding module files it
50/// requires (if it is a consumer)
51struct ParsedCompileCommandInfo {
52 std::string SourceFile;
53 std::optional<std::string> OutputModuleFile;
54 // Map from required module name to the module file path.
55 llvm::StringMap<std::string> RequiredModuleFiles;
56};
57
58/// Get ParsedCompileCommandInfo by looking at the '--precompile',
59/// '-fmodule-file=' and '-fmodule-file=' commands in the compile command.
60std::optional<ParsedCompileCommandInfo>
61parseCompileCommandInfo(tooling::CompileCommand Cmd, const ThreadsafeFS &TFS) {
62 auto FS = TFS.view(std::nullopt);
63 auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()
64 ? llvm::cl::TokenizeWindowsCommandLine
65 : llvm::cl::TokenizeGNUCommandLine;
66 tooling::addExpandedResponseFiles(Cmd.CommandLine, Cmd.Directory, Tokenizer,
67 *FS);
68
69 ParsedCompileCommandInfo Result;
70 Result.SourceFile = normalizePath(Cmd.Filename, Cmd.Directory);
71
72 bool SawPrecompile = false;
73 for (size_t I = 1; I < Cmd.CommandLine.size(); ++I) {
74 llvm::StringRef Arg = Cmd.CommandLine[I];
75 if (Arg == "--precompile") {
76 SawPrecompile = true;
77 continue;
78 }
79
80 if (Arg.consume_front("-fmodule-output=")) {
81 Result.OutputModuleFile = normalizePath(Arg, Cmd.Directory);
82 continue;
83 }
84 if (Arg == "-fmodule-output" && I + 1 < Cmd.CommandLine.size()) {
85 Result.OutputModuleFile =
86 normalizePath(Cmd.CommandLine[++I], Cmd.Directory);
87 continue;
88 }
89 if (SawPrecompile && Arg == "-o" && I + 1 < Cmd.CommandLine.size()) {
90 Result.OutputModuleFile =
91 normalizePath(Cmd.CommandLine[++I], Cmd.Directory);
92 continue;
93 }
94 if (SawPrecompile && Arg.starts_with("-o") && Arg.size() > 2) {
95 Result.OutputModuleFile = normalizePath(Arg.drop_front(2), Cmd.Directory);
96 continue;
97 }
98
99 if (!Arg.consume_front("-fmodule-file="))
100 continue;
101
102 auto Sep = Arg.find('=');
103 if (Sep == llvm::StringRef::npos || Sep == 0 || Sep + 1 == Arg.size())
104 continue;
105
106 Result.RequiredModuleFiles[Arg.take_front(Sep)] =
107 normalizePath(Arg.drop_front(Sep + 1), Cmd.Directory);
108 }
109
110 return Result;
111}
112
113std::optional<tooling::CompileCommand>
114getCompileCommandForFile(const clang::tooling::CompilationDatabase &CDB,
115 PathRef FilePath,
116 const ProjectModules::CommandMangler &Mangler) {
117 auto Candidates = CDB.getCompileCommands(FilePath);
118 if (Candidates.empty())
119 return std::nullopt;
120
121 // Choose the first candidates as the compile commands as the file.
122 // Following the same logic with
123 // DirectoryBasedGlobalCompilationDatabase::getCompileCommand.
124 tooling::CompileCommand Cmd = std::move(Candidates.front());
125
126 if (Mangler)
127 Mangler(Cmd, FilePath);
128
129 return Cmd;
130}
131
132/// A scanner to query the dependency information for C++20 Modules.
133///
134/// The scanner can scan a single file with `scan(PathRef)` member function
135/// or scan the whole project with `globalScan(vector<PathRef>)` member
136/// function. See the comments of `globalScan` to see the details.
137///
138/// The ModuleDependencyScanner can get the directly required module names for a
139/// specific source file. Also the ModuleDependencyScanner can get the source
140/// file declaring the primary module interface for a specific module name.
141///
142/// IMPORTANT NOTE: we assume that every module unit is only declared once in a
143/// source file in the project. But the assumption is not strictly true even
144/// besides the invalid projects. The language specification requires that every
145/// module unit should be unique in a valid program. But a project can contain
146/// multiple programs. Then it is valid that we can have multiple source files
147/// declaring the same module in a project as long as these source files don't
148/// interfere with each other.
149class ModuleDependencyScanner {
150public:
151 ModuleDependencyScanner(
152 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
153 const ThreadsafeFS &TFS)
154 : CDB(CDB), Service([&TFS] {
155 dependencies::DependencyScanningServiceOptions Opts;
156 Opts.MakeVFS = [&] { return TFS.view(std::nullopt); };
157 Opts.Mode = dependencies::ScanningMode::CanonicalPreprocessing;
158 Opts.EmitWarnings = false;
159 Opts.ReportAbsolutePaths = false;
160 return Opts;
161 }()) {}
162
163 /// The scanned modules dependency information for a specific source file.
164 struct ModuleDependencyInfo {
165 /// The name of the module if the file is a module unit.
166 std::optional<std::string> ModuleName;
167 /// A list of names for the modules that the file directly depends.
168 std::vector<std::string> RequiredModules;
169 };
170
171 /// Scanning the single file specified by \param FilePath.
172 std::optional<ModuleDependencyInfo>
173 scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler);
174
175 /// Scanning every source file in the current project to get the
176 /// <module-name> to <module-unit-source> map.
177 /// TODO: We should find an efficient method to get the <module-name>
178 /// to <module-unit-source> map. We can make it either by providing
179 /// a global module dependency scanner to monitor every file. Or we
180 /// can simply require the build systems (or even the end users)
181 /// to provide the map.
182 void globalScan(const ProjectModules::CommandMangler &Mangler);
183
184 /// Get the source file from the module name. Note that the language
185 /// guarantees all the module names are unique in a valid program.
186 /// This function should only be called after globalScan.
187 ///
188 /// TODO: We should handle the case that there are multiple source files
189 /// declaring the same module.
190 PathRef getSourceForModuleName(llvm::StringRef ModuleName) const;
191
192 /// Return the direct required modules. Indirect required modules are not
193 /// included.
194 std::vector<std::string>
195 getRequiredModules(PathRef File,
196 const ProjectModules::CommandMangler &Mangler);
197
198private:
199 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
200
201 // Whether the scanner has scanned the project globally.
202 bool GlobalScanned = false;
203
204 clang::dependencies::DependencyScanningService Service;
205
206 // TODO: Add a scanning cache.
207
208 // Map module name to source file path.
209 llvm::StringMap<std::string> ModuleNameToSource;
210};
211
212std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
213ModuleDependencyScanner::scan(PathRef FilePath,
214 const ProjectModules::CommandMangler &Mangler) {
215 auto Cmd = getCompileCommandForFile(*CDB, FilePath, Mangler);
216 if (!Cmd)
217 return std::nullopt;
218
219 using namespace clang::tooling;
220
221 DependencyScanningTool ScanningTool(Service);
222
223 std::string S;
224 llvm::raw_string_ostream OS(S);
225 DiagnosticOptions DiagOpts;
226 DiagOpts.ShowCarets = false;
227 TextDiagnosticPrinter DiagConsumer(OS, DiagOpts);
228
229 std::optional<P1689Rule> ScanningResult =
230 ScanningTool.getP1689ModuleDependencyFile(*Cmd, Cmd->Directory,
231 DiagConsumer);
232
233 if (!ScanningResult) {
234 elog("Scanning modules dependencies for {0} failed: {1}", FilePath, S);
235 std::string Cmdline;
236 for (auto &Arg : Cmd->CommandLine)
237 Cmdline += Arg + " ";
238 elog("The command line the scanning tool use is: {0}", Cmdline);
239 return std::nullopt;
240 }
241
242 ModuleDependencyInfo Result;
243
244 if (ScanningResult->Provides) {
245 Result.ModuleName = ScanningResult->Provides->ModuleName;
246
247 auto [Iter, Inserted] = ModuleNameToSource.try_emplace(
248 ScanningResult->Provides->ModuleName, FilePath);
249
250 if (!Inserted && Iter->second != FilePath) {
251 elog("Detected multiple source files ({0}, {1}) declaring the same "
252 "module: '{2}'. "
253 "Now clangd may find the wrong source in such case.",
254 Iter->second, FilePath, ScanningResult->Provides->ModuleName);
255 }
256 }
257
258 for (auto &Required : ScanningResult->Requires)
259 Result.RequiredModules.push_back(Required.ModuleName);
260
261 return Result;
262}
263
264void ModuleDependencyScanner::globalScan(
265 const ProjectModules::CommandMangler &Mangler) {
266 if (GlobalScanned)
267 return;
268
269 for (auto &File : CDB->getAllFiles())
270 scan(File, Mangler);
271
272 GlobalScanned = true;
273}
274
275PathRef ModuleDependencyScanner::getSourceForModuleName(
276 llvm::StringRef ModuleName) const {
277 assert(
278 GlobalScanned &&
279 "We should only call getSourceForModuleName after calling globalScan()");
280
281 if (auto It = ModuleNameToSource.find(ModuleName);
282 It != ModuleNameToSource.end())
283 return It->second;
284
285 return {};
286}
287
288std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
290 auto ScanningResult = scan(File, Mangler);
291 if (!ScanningResult)
292 return {};
293
294 return ScanningResult->RequiredModules;
295}
296} // namespace
297
298/// TODO: The existing `ScanningAllProjectModules` is not efficient. See the
299/// comments in ModuleDependencyScanner for detail.
300///
301/// In the future, we wish the build system can provide a well design
302/// compilation database for modules then we can query that new compilation
303/// database directly. Or we need to have a global long-live scanner to detect
304/// the state of each file.
306public:
308 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
309 const ThreadsafeFS &TFS)
310 : Scanner(CDB, TFS) {}
311
312 ~ScanningAllProjectModules() override = default;
313
314 std::vector<std::string> getRequiredModules(PathRef File) override {
315 return Scanner.getRequiredModules(File, Mangler);
316 }
317
318 void setCommandMangler(CommandMangler Mangler) override {
319 this->Mangler = std::move(Mangler);
320 }
321
322 /// RequiredSourceFile is not used intentionally. See the comments of
323 /// ModuleDependencyScanner for detail.
324 std::string getSourceForModuleName(llvm::StringRef ModuleName,
325 PathRef RequiredSourceFile) override {
326 Scanner.globalScan(Mangler);
327 return Scanner.getSourceForModuleName(ModuleName).str();
328 }
329
330 std::string getModuleNameForSource(PathRef File) override {
331 auto ScanningResult = Scanner.scan(File, Mangler);
332 if (!ScanningResult || !ScanningResult->ModuleName)
333 return {};
334
335 return *ScanningResult->ModuleName;
336 }
337
338 // Determining Unique/Multiple needs a global scan; return Unknown for cost
339 // reasons. We will have other ProjectModules implementations can determine
340 // this more efficiently.
341 ModuleNameState getModuleNameState(llvm::StringRef /*ModuleName*/) override {
343 }
344
345private:
346 ModuleDependencyScanner Scanner;
347 CommandMangler Mangler;
348};
349
350/// Reads project module information directly from compile commands.
351///
352/// The key observation is that compile commands may already encode the mapping
353/// between a TU, the module names it imports, and the BMI paths it uses:
354/// - producers may spell the BMI path with `--precompile -o <bmi>` or
355/// `-fmodule-output=<bmi>`
356/// - consumers may spell the mapping from module name to BMI path with
357/// `-fmodule-file=<module>=<bmi>`
358///
359/// When that information is present, we can answer
360/// `getSourceForModuleName(ModuleName, RequiredSourceFile)` by first looking up
361/// the BMI path the consumer TU uses for `ModuleName`, and then mapping that
362/// BMI path back to the module unit source that produced it. This avoids the
363/// older scanning-only approach of guessing the module unit from the module
364/// name alone.
365///
366/// One subtle point is that producer commands alone do not reliably tell us the
367/// module name associated with a BMI path. In practice this backend learns that
368/// association from consumer `-fmodule-file=` entries, and then uses the BMI
369/// path to recover the producer source file. That is why indexing is built from
370/// both producer and consumer commands.
371///
372/// Note that compilation database can be stale, so results from this backend
373/// should be treated as preferred hints rather than unquestionable truth.
374/// The compound layer below validates or falls back when needed.
376public:
378 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
379 const ThreadsafeFS &TFS)
380 : CDB(std::move(CDB)), TFS(TFS) {}
381
382 std::vector<std::string> getRequiredModules(PathRef File) override {
383 auto Parsed = parseFileCommand(File);
384 if (!Parsed)
385 return {};
386
387 std::vector<std::string> Result;
388 Result.reserve(Parsed->RequiredModuleFiles.size());
389 for (const auto &Required : Parsed->RequiredModuleFiles)
390 Result.push_back(Required.getKey().str());
391 return Result;
392 }
393
394 std::string getModuleNameForSource(PathRef File) override {
395 indexProducerCommands();
396 auto It = SourceToModuleName.find(
397 maybeCaseFoldPath(normalizePath(File, /*WorkingDir=*/{})));
398 if (It == SourceToModuleName.end() || It->second.Ambiguous)
399 return {};
400 return It->second.Name;
401 }
402
403 ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override {
404 indexProducerCommands();
405 auto It = ModuleNameToDistinctSources.find(ModuleName);
406 if (It == ModuleNameToDistinctSources.end())
408 return It->second.size() > 1 ? ModuleNameState::Multiple
410 }
411
412 std::string getSourceForModuleName(llvm::StringRef ModuleName,
413 PathRef RequiredSourceFile) override {
414 auto Parsed = parseFileCommand(RequiredSourceFile);
415 if (!Parsed)
416 return {};
417
418 auto It = Parsed->RequiredModuleFiles.find(ModuleName);
419 if (It == Parsed->RequiredModuleFiles.end())
420 return {};
421
422 indexProducerCommands();
423 auto SourceIt = PCMToSource.find(maybeCaseFoldPath(It->second));
424 if (SourceIt == PCMToSource.end())
425 return {};
426
427 return SourceIt->second;
428 }
429
430 void setCommandMangler(CommandMangler Mangler) override {
431 this->Mangler = std::move(Mangler);
432 ProducerCommandsIndexed = false;
433 PCMToSource.clear();
434 ModuleNameToDistinctSources.clear();
435 SourceToModuleName.clear();
436 }
437
438private:
439 /// Parses the compile command for \p File into the module information
440 /// encoded in the command line.
441 std::optional<ParsedCompileCommandInfo> parseFileCommand(PathRef File) const {
442 auto Cmd = getCompileCommandForFile(*CDB, File, Mangler);
443 if (!Cmd)
444 return std::nullopt;
445 return parseCompileCommandInfo(std::move(*Cmd), TFS);
446 }
447
448 /// Builds indexes from producer and consumer compile commands.
449 ///
450 /// Compile commands are parsed once up front. The first pass records which
451 /// source file produces each BMI path. The second pass walks consumer
452 /// commands, uses `-fmodule-file=` information to associate module names with
453 /// those BMI paths, and then records which producer source files are
454 /// referenced for each module name.
455 void indexProducerCommands() {
456 if (ProducerCommandsIndexed)
457 return;
458
459 std::vector<ParsedCompileCommandInfo> ParsedCommands;
460 auto AllFiles = CDB->getAllFiles();
461 ParsedCommands.reserve(AllFiles.size());
462 for (const auto &File : AllFiles) {
463 auto Parsed = parseFileCommand(File);
464 if (!Parsed)
465 continue;
466
467 if (Parsed->OutputModuleFile)
468 PCMToSource[maybeCaseFoldPath(*Parsed->OutputModuleFile)] =
469 Parsed->SourceFile;
470
471 ParsedCommands.push_back(std::move(*Parsed));
472 }
473
474 for (const auto &Parsed : ParsedCommands) {
475 for (const auto &Required : Parsed.RequiredModuleFiles) {
476 auto SourceIt =
477 PCMToSource.find(maybeCaseFoldPath(Required.getValue()));
478 if (SourceIt == PCMToSource.end())
479 continue;
480 ModuleNameToDistinctSources[Required.getKey()].insert(
481 maybeCaseFoldPath(SourceIt->second));
482
483 auto &Recovered =
484 SourceToModuleName[maybeCaseFoldPath(SourceIt->second)];
485 if (Recovered.Name.empty())
486 Recovered.Name = Required.getKey().str();
487 else if (Recovered.Name != Required.getKey()) {
488 if (!Recovered.Ambiguous) {
489 elog("Detected conflicting module names ('{0}' and '{1}') for "
490 "the same module file {2} produced by source {3}",
491 Recovered.Name, Required.getKey(), Required.getValue(),
492 SourceIt->second);
493 }
494 Recovered.Ambiguous = true;
495 }
496 }
497 }
498
499 ProducerCommandsIndexed = true;
500 }
501
502 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
503 const ThreadsafeFS &TFS;
504 CommandMangler Mangler;
505 bool ProducerCommandsIndexed = false;
506
507 llvm::StringMap<std::string> PCMToSource;
508
509 using DistinctSourceSet = llvm::StringSet<>;
510 llvm::StringMap<DistinctSourceSet> ModuleNameToDistinctSources;
511
512 struct RecoveredModuleName {
513 std::string Name;
514 bool Ambiguous = false;
515 };
516 llvm::StringMap<RecoveredModuleName> SourceToModuleName;
517};
518
519/// Combines the compile-commands backend with the scanning backend.
520///
521/// For getSourceForModuleName, it prefers compile-command-derived results when
522/// available to avoid scanning the whole project, but validates them against
523/// scanning results to avoid returning stale information. For other queries,
524/// it returns scanning results directly as scanning information is update to
525/// date.
527public:
529 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
530 const ThreadsafeFS &TFS)
531 : CompileCommands(
532 std::make_unique<CompileCommandsProjectModules>(CDB, TFS)),
533 Scanning(
534 std::make_unique<ScanningAllProjectModules>(std::move(CDB), TFS)) {}
535
536 std::vector<std::string> getRequiredModules(PathRef File) override {
537 // Return scanning results directly as it is fast enough and up to date.
538 return Scanning->getRequiredModules(File);
539 }
540
541 std::string getModuleNameForSource(PathRef File) override {
542 // Return scanning results directly as it is fast enough and up to date.
543 return Scanning->getModuleNameForSource(File);
544 }
545
546 std::string getSourceForModuleName(llvm::StringRef ModuleName,
547 PathRef RequiredSourceFile) override {
548 auto FromCompileCommands =
549 CompileCommands->getSourceForModuleName(ModuleName, RequiredSourceFile);
550 // Check if the source still declares the module.
551 // This is to validate compile-command-derived results may be stale and
552 // scan a single file is fast enough. We just don't want to scan the project
553 // entirely.
554 if (!FromCompileCommands.empty() &&
555 Scanning->getModuleNameForSource(FromCompileCommands) == ModuleName)
556 return FromCompileCommands;
557
558 return Scanning->getSourceForModuleName(ModuleName, RequiredSourceFile);
559 }
560
561 ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override {
562 auto FromCompileCommands = CompileCommands->getModuleNameState(ModuleName);
563 if (FromCompileCommands != ModuleNameState::Unknown)
564 return FromCompileCommands;
565 return Scanning->getModuleNameState(ModuleName);
566 }
567
568 void setCommandMangler(CommandMangler Mangler) override {
569 this->Mangler = std::move(Mangler);
570 auto ForwardMangler = [this](tooling::CompileCommand &Command,
571 PathRef CommandPath) {
572 if (this->Mangler)
573 this->Mangler(Command, CommandPath);
574 };
575 CompileCommands->setCommandMangler(ForwardMangler);
576 Scanning->setCommandMangler(std::move(ForwardMangler));
577 }
578
579private:
580 std::unique_ptr<CompileCommandsProjectModules> CompileCommands;
581 std::unique_ptr<ScanningAllProjectModules> Scanning;
582 CommandMangler Mangler;
583};
584
585/// Creates the project-modules facade used by clangd.
586///
587/// The implementation is intentionally layered:
588///
589/// CompoundProjectModules
590/// / \
591/// v v
592/// CompileCommands ScanningAllProjectModules
593/// ProjectModules |
594/// | v
595/// | ModuleDependencyScanner
596/// |
597/// +-- preferred specifically for recovering the source file for a module
598/// | name in the context of a consumer TU, because compile commands
599/// | encode `module name -> BMI -> producer source`
600/// |
601/// +-- scanning remains fallback/validation for stale or missing data
602///
603/// - `CompileCommandsProjectModules` reads module relationships that the build
604/// system already made explicit in compile commands. In particular, it uses
605/// producer-side BMI output paths together with consumer-side
606/// `-fmodule-file=<module>=<bmi>` entries to recover the module unit source a
607/// TU actually depends on. This is the preferred source because it can
608/// distinguish different module producers for the same module name when
609/// different translation units reference different BMIs.
610/// - `ScanningAllProjectModules` derives module information by scanning source
611/// files. It is more expensive, but it can still answer queries that are not
612/// present in compile commands and validate compile-command-derived results.
613/// - `CompoundProjectModules` arbitrates between the two backends on a
614/// per-query basis. Compile commands are especially valuable for
615/// `getSourceForModuleName()` because they preserve the consumer TU's actual
616/// `module name -> BMI` choice. Other queries may still fall back to, or be
617/// validated by, scanning because compile-command information may be
618/// incomplete or stale.
619///
620/// This split keeps the logic simple: compile commands provide precision when
621/// available, while scanning preserves compatibility with projects that have
622/// incomplete module information in their compilation database.
623std::unique_ptr<ProjectModules> getProjectModules(
624 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
625 const ThreadsafeFS &TFS) {
626 return std::make_unique<CompoundProjectModules>(std::move(CDB), TFS);
627}
628
629} // namespace clang::clangd
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
Reads project module information directly from compile commands.
CompileCommandsProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
std::string getModuleNameForSource(PathRef File) override
void setCommandMangler(CommandMangler Mangler) override
std::string getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSourceFile) override
ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override
std::vector< std::string > getRequiredModules(PathRef File) override
std::vector< std::string > getRequiredModules(PathRef File) override
ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override
std::string getModuleNameForSource(PathRef File) override
void setCommandMangler(CommandMangler Mangler) override
std::string getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSourceFile) override
CompoundProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
An interface to query the modules information in the project.
llvm::unique_function< void(tooling::CompileCommand &, PathRef) const > CommandMangler
TODO: The existing ScanningAllProjectModules is not efficient.
void setCommandMangler(CommandMangler Mangler) override
ScanningAllProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
std::string getModuleNameForSource(PathRef File) override
std::vector< std::string > getRequiredModules(PathRef File) override
ModuleNameState getModuleNameState(llvm::StringRef) override
std::string getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSourceFile) override
RequiredSourceFile is not used intentionally.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
std::string maybeCaseFoldPath(PathRef Path)
Definition Path.cpp:18
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
std::string Path
A typedef to represent a file path.
Definition Path.h:26
std::unique_ptr< ProjectModules > getProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
Creates the project-modules facade used by clangd.