File indexing completed on 2026-05-10 08:37:11
0001
0002
0003
0004
0005
0006
0007
0008
0009 #ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGFILESYSTEM_H
0010 #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGFILESYSTEM_H
0011
0012 #include "clang/Basic/LLVM.h"
0013 #include "clang/Lex/DependencyDirectivesScanner.h"
0014 #include "llvm/ADT/DenseMap.h"
0015 #include "llvm/ADT/StringMap.h"
0016 #include "llvm/Support/Allocator.h"
0017 #include "llvm/Support/ErrorOr.h"
0018 #include "llvm/Support/VirtualFileSystem.h"
0019 #include <mutex>
0020 #include <optional>
0021
0022 namespace clang {
0023 namespace tooling {
0024 namespace dependencies {
0025
0026 using DependencyDirectivesTy =
0027 SmallVector<dependency_directives_scan::Directive, 20>;
0028
0029
0030
0031 struct CachedFileContents {
0032 CachedFileContents(std::unique_ptr<llvm::MemoryBuffer> Contents)
0033 : Original(std::move(Contents)), DepDirectives(nullptr) {}
0034
0035
0036 std::unique_ptr<llvm::MemoryBuffer> Original;
0037
0038
0039 std::mutex ValueLock;
0040 SmallVector<dependency_directives_scan::Token, 10> DepDirectiveTokens;
0041
0042
0043 std::atomic<const std::optional<DependencyDirectivesTy> *> DepDirectives;
0044
0045 ~CachedFileContents() { delete DepDirectives.load(); }
0046 };
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 class CachedFileSystemEntry {
0061 public:
0062
0063
0064 CachedFileSystemEntry(llvm::ErrorOr<llvm::vfs::Status> Stat)
0065 : MaybeStat(std::move(Stat)), Contents(nullptr) {
0066 clearStatName();
0067 }
0068
0069
0070 CachedFileSystemEntry(llvm::ErrorOr<llvm::vfs::Status> Stat,
0071 CachedFileContents *Contents)
0072 : MaybeStat(std::move(Stat)), Contents(std::move(Contents)) {
0073 clearStatName();
0074 }
0075
0076
0077 bool isError() const { return !MaybeStat; }
0078
0079
0080 bool isDirectory() const { return !isError() && MaybeStat->isDirectory(); }
0081
0082
0083 StringRef getOriginalContents() const {
0084 assert(!isError() && "error");
0085 assert(!MaybeStat->isDirectory() && "not a file");
0086 assert(Contents && "contents not initialized");
0087 return Contents->Original->getBuffer();
0088 }
0089
0090
0091
0092 std::optional<ArrayRef<dependency_directives_scan::Directive>>
0093 getDirectiveTokens() const {
0094 assert(!isError() && "error");
0095 assert(!isDirectory() && "not a file");
0096 assert(Contents && "contents not initialized");
0097 if (auto *Directives = Contents->DepDirectives.load()) {
0098 if (Directives->has_value())
0099 return ArrayRef<dependency_directives_scan::Directive>(**Directives);
0100 }
0101 return std::nullopt;
0102 }
0103
0104
0105 std::error_code getError() const { return MaybeStat.getError(); }
0106
0107
0108 llvm::vfs::Status getStatus() const {
0109 assert(!isError() && "error");
0110 assert(MaybeStat->getName().empty() && "stat name must be empty");
0111 return *MaybeStat;
0112 }
0113
0114
0115 llvm::sys::fs::UniqueID getUniqueID() const {
0116 assert(!isError() && "error");
0117 return MaybeStat->getUniqueID();
0118 }
0119
0120
0121 CachedFileContents *getCachedContents() const {
0122 assert(!isError() && "error");
0123 assert(!isDirectory() && "not a file");
0124 return Contents;
0125 }
0126
0127 private:
0128 void clearStatName() {
0129 if (MaybeStat)
0130 MaybeStat = llvm::vfs::Status::copyWithNewName(*MaybeStat, "");
0131 }
0132
0133
0134
0135 llvm::ErrorOr<llvm::vfs::Status> MaybeStat;
0136
0137
0138
0139
0140
0141
0142 CachedFileContents *Contents;
0143 };
0144
0145 using CachedRealPath = llvm::ErrorOr<std::string>;
0146
0147
0148
0149
0150
0151
0152
0153 class DependencyScanningFilesystemSharedCache {
0154 public:
0155 struct CacheShard {
0156
0157 mutable std::mutex CacheLock;
0158
0159
0160 llvm::StringMap<
0161 std::pair<const CachedFileSystemEntry *, const CachedRealPath *>,
0162 llvm::BumpPtrAllocator>
0163 CacheByFilename;
0164
0165
0166 llvm::DenseMap<llvm::sys::fs::UniqueID, const CachedFileSystemEntry *>
0167 EntriesByUID;
0168
0169
0170 llvm::SpecificBumpPtrAllocator<CachedFileSystemEntry> EntryStorage;
0171
0172
0173 llvm::SpecificBumpPtrAllocator<CachedFileContents> ContentsStorage;
0174
0175
0176 llvm::SpecificBumpPtrAllocator<CachedRealPath> RealPathStorage;
0177
0178
0179 const CachedFileSystemEntry *findEntryByFilename(StringRef Filename) const;
0180
0181
0182 const CachedFileSystemEntry *
0183 findEntryByUID(llvm::sys::fs::UniqueID UID) const;
0184
0185
0186
0187
0188 const CachedFileSystemEntry &
0189 getOrEmplaceEntryForFilename(StringRef Filename,
0190 llvm::ErrorOr<llvm::vfs::Status> Stat);
0191
0192
0193
0194
0195 const CachedFileSystemEntry &
0196 getOrEmplaceEntryForUID(llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat,
0197 std::unique_ptr<llvm::MemoryBuffer> Contents);
0198
0199
0200
0201 const CachedFileSystemEntry &
0202 getOrInsertEntryForFilename(StringRef Filename,
0203 const CachedFileSystemEntry &Entry);
0204
0205
0206
0207 const CachedRealPath *findRealPathByFilename(StringRef Filename) const;
0208
0209
0210
0211
0212 const CachedRealPath &
0213 getOrEmplaceRealPathForFilename(StringRef Filename,
0214 llvm::ErrorOr<StringRef> RealPath);
0215 };
0216
0217 DependencyScanningFilesystemSharedCache();
0218
0219
0220 CacheShard &getShardForFilename(StringRef Filename) const;
0221 CacheShard &getShardForUID(llvm::sys::fs::UniqueID UID) const;
0222
0223 private:
0224 std::unique_ptr<CacheShard[]> CacheShards;
0225 unsigned NumShards;
0226 };
0227
0228
0229
0230 class DependencyScanningFilesystemLocalCache {
0231 llvm::StringMap<
0232 std::pair<const CachedFileSystemEntry *, const CachedRealPath *>,
0233 llvm::BumpPtrAllocator>
0234 Cache;
0235
0236 public:
0237
0238 const CachedFileSystemEntry *findEntryByFilename(StringRef Filename) const {
0239 assert(llvm::sys::path::is_absolute_gnu(Filename));
0240 auto It = Cache.find(Filename);
0241 return It == Cache.end() ? nullptr : It->getValue().first;
0242 }
0243
0244
0245
0246 const CachedFileSystemEntry &
0247 insertEntryForFilename(StringRef Filename,
0248 const CachedFileSystemEntry &Entry) {
0249 assert(llvm::sys::path::is_absolute_gnu(Filename));
0250 auto [It, Inserted] = Cache.insert({Filename, {&Entry, nullptr}});
0251 auto &[CachedEntry, CachedRealPath] = It->getValue();
0252 if (!Inserted) {
0253
0254
0255 assert((!CachedEntry && CachedRealPath) && "entry already present");
0256 CachedEntry = &Entry;
0257 }
0258 return *CachedEntry;
0259 }
0260
0261
0262
0263 const CachedRealPath *findRealPathByFilename(StringRef Filename) const {
0264 assert(llvm::sys::path::is_absolute_gnu(Filename));
0265 auto It = Cache.find(Filename);
0266 return It == Cache.end() ? nullptr : It->getValue().second;
0267 }
0268
0269
0270
0271 const CachedRealPath &
0272 insertRealPathForFilename(StringRef Filename,
0273 const CachedRealPath &RealPath) {
0274 assert(llvm::sys::path::is_absolute_gnu(Filename));
0275 auto [It, Inserted] = Cache.insert({Filename, {nullptr, &RealPath}});
0276 auto &[CachedEntry, CachedRealPath] = It->getValue();
0277 if (!Inserted) {
0278
0279
0280 assert((!CachedRealPath && CachedEntry) && "real path already present");
0281 CachedRealPath = &RealPath;
0282 }
0283 return *CachedRealPath;
0284 }
0285 };
0286
0287
0288
0289
0290 class EntryRef {
0291
0292 std::string Filename;
0293
0294
0295 const CachedFileSystemEntry &Entry;
0296
0297 friend class DependencyScanningWorkerFilesystem;
0298
0299 public:
0300 EntryRef(StringRef Name, const CachedFileSystemEntry &Entry)
0301 : Filename(Name), Entry(Entry) {}
0302
0303 llvm::vfs::Status getStatus() const {
0304 llvm::vfs::Status Stat = Entry.getStatus();
0305 if (!Stat.isDirectory())
0306 Stat = llvm::vfs::Status::copyWithNewSize(Stat, getContents().size());
0307 return llvm::vfs::Status::copyWithNewName(Stat, Filename);
0308 }
0309
0310 bool isError() const { return Entry.isError(); }
0311 bool isDirectory() const { return Entry.isDirectory(); }
0312
0313
0314 llvm::ErrorOr<EntryRef> unwrapError() const {
0315 if (isError())
0316 return Entry.getError();
0317 return *this;
0318 }
0319
0320 StringRef getContents() const { return Entry.getOriginalContents(); }
0321
0322 std::optional<ArrayRef<dependency_directives_scan::Directive>>
0323 getDirectiveTokens() const {
0324 return Entry.getDirectiveTokens();
0325 }
0326 };
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337 class DependencyScanningWorkerFilesystem
0338 : public llvm::RTTIExtends<DependencyScanningWorkerFilesystem,
0339 llvm::vfs::ProxyFileSystem> {
0340 public:
0341 static const char ID;
0342
0343 DependencyScanningWorkerFilesystem(
0344 DependencyScanningFilesystemSharedCache &SharedCache,
0345 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
0346
0347 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
0348 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
0349 openFileForRead(const Twine &Path) override;
0350
0351 std::error_code getRealPath(const Twine &Path,
0352 SmallVectorImpl<char> &Output) override;
0353
0354 std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
0355
0356
0357 void resetBypassedPathPrefix() { BypassedPathPrefix.reset(); }
0358
0359
0360 void setBypassedPathPrefix(StringRef Prefix) { BypassedPathPrefix = Prefix; }
0361
0362
0363
0364
0365
0366 llvm::ErrorOr<EntryRef> getOrCreateFileSystemEntry(StringRef Filename);
0367
0368
0369
0370
0371
0372 bool ensureDirectiveTokensArePopulated(EntryRef Entry);
0373
0374
0375
0376 bool exists(const Twine &Path) override;
0377
0378 private:
0379
0380
0381
0382
0383
0384 llvm::ErrorOr<const CachedFileSystemEntry &>
0385 computeAndStoreResult(StringRef OriginalFilename,
0386 StringRef FilenameForLookup);
0387
0388
0389
0390 struct TentativeEntry {
0391 llvm::vfs::Status Status;
0392 std::unique_ptr<llvm::MemoryBuffer> Contents;
0393
0394 TentativeEntry(llvm::vfs::Status Status,
0395 std::unique_ptr<llvm::MemoryBuffer> Contents = nullptr)
0396 : Status(std::move(Status)), Contents(std::move(Contents)) {}
0397 };
0398
0399
0400
0401 llvm::ErrorOr<TentativeEntry> readFile(StringRef Filename);
0402
0403
0404
0405
0406 const CachedFileSystemEntry &
0407 getOrEmplaceSharedEntryForUID(TentativeEntry TEntry);
0408
0409
0410
0411
0412
0413
0414 const CachedFileSystemEntry *
0415 findEntryByFilenameWithWriteThrough(StringRef Filename);
0416
0417
0418
0419 const CachedFileSystemEntry *
0420 findSharedEntryByUID(llvm::vfs::Status Stat) const {
0421 return SharedCache.getShardForUID(Stat.getUniqueID())
0422 .findEntryByUID(Stat.getUniqueID());
0423 }
0424
0425
0426
0427 const CachedFileSystemEntry &
0428 insertLocalEntryForFilename(StringRef Filename,
0429 const CachedFileSystemEntry &Entry) {
0430 return LocalCache.insertEntryForFilename(Filename, Entry);
0431 }
0432
0433
0434
0435
0436 const CachedFileSystemEntry &
0437 getOrEmplaceSharedEntryForFilename(StringRef Filename, std::error_code EC) {
0438 return SharedCache.getShardForFilename(Filename)
0439 .getOrEmplaceEntryForFilename(Filename, EC);
0440 }
0441
0442
0443
0444
0445 const CachedFileSystemEntry &
0446 getOrInsertSharedEntryForFilename(StringRef Filename,
0447 const CachedFileSystemEntry &Entry) {
0448 return SharedCache.getShardForFilename(Filename)
0449 .getOrInsertEntryForFilename(Filename, Entry);
0450 }
0451
0452 void printImpl(raw_ostream &OS, PrintType Type,
0453 unsigned IndentLevel) const override {
0454 printIndent(OS, IndentLevel);
0455 OS << "DependencyScanningFilesystem\n";
0456 getUnderlyingFS().print(OS, Type, IndentLevel + 1);
0457 }
0458
0459
0460
0461 bool shouldBypass(StringRef Path) const;
0462
0463
0464 DependencyScanningFilesystemSharedCache &SharedCache;
0465
0466
0467 DependencyScanningFilesystemLocalCache LocalCache;
0468
0469
0470 std::optional<std::string> BypassedPathPrefix;
0471
0472
0473
0474 llvm::ErrorOr<std::string> WorkingDirForCacheLookup;
0475
0476 void updateWorkingDirForCacheLookup();
0477
0478 llvm::ErrorOr<StringRef>
0479 tryGetFilenameForLookup(StringRef OriginalFilename,
0480 llvm::SmallVectorImpl<char> &PathBuf) const;
0481 };
0482
0483 }
0484 }
0485 }
0486
0487 #endif