Skip to content

Commit f75f406

Browse files
committed
[DependencyScanningFilesystem] Make sure the local/shared cache filename lookups use only absolute paths (llvm#66122)
Previously a relative path would be used as a key for cache lookup and if the same relative path was used from another compiler invocation with a different working directory then the first cache entry was erroneously returned. (cherry picked from commit 36b37c7)
1 parent 4b009f7 commit f75f406

File tree

3 files changed

+117
-18
lines changed

3 files changed

+117
-18
lines changed

clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class DependencyScanningFilesystemLocalCache {
228228
public:
229229
/// Returns entry associated with the filename or nullptr if none is found.
230230
const CachedFileSystemEntry *findEntryByFilename(StringRef Filename) const {
231+
assert(llvm::sys::path::is_absolute_gnu(Filename));
231232
auto It = Cache.find(Filename);
232233
return It == Cache.end() ? nullptr : It->getValue();
233234
}
@@ -237,6 +238,7 @@ class DependencyScanningFilesystemLocalCache {
237238
const CachedFileSystemEntry &
238239
insertEntryForFilename(StringRef Filename,
239240
const CachedFileSystemEntry &Entry) {
241+
assert(llvm::sys::path::is_absolute_gnu(Filename));
240242
const auto *InsertedEntry = Cache.insert({Filename, &Entry}).first->second;
241243
assert(InsertedEntry == &Entry && "entry already present");
242244
return *InsertedEntry;
@@ -298,13 +300,14 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
298300
public:
299301
DependencyScanningWorkerFilesystem(
300302
DependencyScanningFilesystemSharedCache &SharedCache,
301-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
302-
: ProxyFileSystem(std::move(FS)), SharedCache(SharedCache) {}
303+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
303304

304305
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
305306
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
306307
openFileForRead(const Twine &Path) override;
307308

309+
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
310+
308311
/// Returns entry for the given filename.
309312
///
310313
/// Attempts to use the local and shared caches first, then falls back to
@@ -320,8 +323,11 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
320323
/// For a filename that's not yet associated with any entry in the caches,
321324
/// uses the underlying filesystem to either look up the entry based in the
322325
/// shared cache indexed by unique ID, or creates new entry from scratch.
326+
/// \p FilenameForLookup will always be an absolute path, and different than
327+
/// \p OriginalFilename if \p OriginalFilename is relative.
323328
llvm::ErrorOr<const CachedFileSystemEntry &>
324-
computeAndStoreResult(StringRef Filename);
329+
computeAndStoreResult(StringRef OriginalFilename,
330+
StringRef FilenameForLookup);
325331

326332
/// Scan for preprocessor directives for the given entry if necessary and
327333
/// returns a wrapper object with reference semantics.
@@ -400,6 +406,12 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
400406
/// The local cache is used by the worker thread to cache file system queries
401407
/// locally instead of querying the global cache every time.
402408
DependencyScanningFilesystemLocalCache LocalCache;
409+
410+
/// The working directory to use for making relative paths absolute before
411+
/// using them for cache lookups.
412+
llvm::ErrorOr<std::string> WorkingDirForCacheLookup;
413+
414+
void updateWorkingDirForCacheLookup();
403415
};
404416

405417
} // end namespace dependencies

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ DependencyScanningFilesystemSharedCache::
100100
DependencyScanningFilesystemSharedCache::CacheShard &
101101
DependencyScanningFilesystemSharedCache::getShardForFilename(
102102
StringRef Filename) const {
103+
assert(llvm::sys::path::is_absolute_gnu(Filename));
103104
return CacheShards[llvm::hash_value(Filename) % NumShards];
104105
}
105106

@@ -113,6 +114,7 @@ DependencyScanningFilesystemSharedCache::getShardForUID(
113114
const CachedFileSystemEntry *
114115
DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
115116
StringRef Filename) const {
117+
assert(llvm::sys::path::is_absolute_gnu(Filename));
116118
std::lock_guard<std::mutex> LockGuard(CacheLock);
117119
auto It = EntriesByFilename.find(Filename);
118120
return It == EntriesByFilename.end() ? nullptr : It->getValue();
@@ -190,6 +192,14 @@ static bool shouldCacheStatFailures(StringRef Filename) {
190192
return shouldScanForDirectivesBasedOnExtension(Filename);
191193
}
192194

195+
DependencyScanningWorkerFilesystem::DependencyScanningWorkerFilesystem(
196+
DependencyScanningFilesystemSharedCache &SharedCache,
197+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
198+
: ProxyFileSystem(std::move(FS)), SharedCache(SharedCache),
199+
WorkingDirForCacheLookup(llvm::errc::invalid_argument) {
200+
updateWorkingDirForCacheLookup();
201+
}
202+
193203
bool DependencyScanningWorkerFilesystem::shouldScanForDirectives(
194204
StringRef Filename) {
195205
return shouldScanForDirectivesBasedOnExtension(Filename);
@@ -216,44 +226,62 @@ DependencyScanningWorkerFilesystem::findEntryByFilenameWithWriteThrough(
216226
}
217227

218228
llvm::ErrorOr<const CachedFileSystemEntry &>
219-
DependencyScanningWorkerFilesystem::computeAndStoreResult(StringRef Filename) {
220-
llvm::ErrorOr<llvm::vfs::Status> Stat = getUnderlyingFS().status(Filename);
229+
DependencyScanningWorkerFilesystem::computeAndStoreResult(
230+
StringRef OriginalFilename, StringRef FilenameForLookup) {
231+
llvm::ErrorOr<llvm::vfs::Status> Stat =
232+
getUnderlyingFS().status(OriginalFilename);
221233
if (!Stat) {
222-
if (!shouldCacheStatFailures(Filename))
234+
if (!shouldCacheStatFailures(OriginalFilename))
223235
return Stat.getError();
224236
const auto &Entry =
225-
getOrEmplaceSharedEntryForFilename(Filename, Stat.getError());
226-
return insertLocalEntryForFilename(Filename, Entry);
237+
getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
238+
return insertLocalEntryForFilename(FilenameForLookup, Entry);
227239
}
228240

229241
if (const auto *Entry = findSharedEntryByUID(*Stat))
230-
return insertLocalEntryForFilename(Filename, *Entry);
242+
return insertLocalEntryForFilename(FilenameForLookup, *Entry);
231243

232244
auto TEntry =
233-
Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(Filename);
245+
Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
234246

235247
const CachedFileSystemEntry *SharedEntry = [&]() {
236248
if (TEntry) {
237249
const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
238-
return &getOrInsertSharedEntryForFilename(Filename, UIDEntry);
250+
return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
239251
}
240-
return &getOrEmplaceSharedEntryForFilename(Filename, TEntry.getError());
252+
return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
253+
TEntry.getError());
241254
}();
242255

243-
return insertLocalEntryForFilename(Filename, *SharedEntry);
256+
return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
244257
}
245258

246259
llvm::ErrorOr<EntryRef>
247260
DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
248-
StringRef Filename, bool DisableDirectivesScanning) {
249-
if (const auto *Entry = findEntryByFilenameWithWriteThrough(Filename))
250-
return scanForDirectivesIfNecessary(*Entry, Filename,
261+
StringRef OriginalFilename, bool DisableDirectivesScanning) {
262+
StringRef FilenameForLookup;
263+
SmallString<256> PathBuf;
264+
if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
265+
FilenameForLookup = OriginalFilename;
266+
} else if (!WorkingDirForCacheLookup) {
267+
return WorkingDirForCacheLookup.getError();
268+
} else {
269+
StringRef RelFilename = OriginalFilename;
270+
RelFilename.consume_front("./");
271+
PathBuf = *WorkingDirForCacheLookup;
272+
llvm::sys::path::append(PathBuf, RelFilename);
273+
FilenameForLookup = PathBuf.str();
274+
}
275+
assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
276+
if (const auto *Entry =
277+
findEntryByFilenameWithWriteThrough(FilenameForLookup))
278+
return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
251279
DisableDirectivesScanning)
252280
.unwrapError();
253-
auto MaybeEntry = computeAndStoreResult(Filename);
281+
auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
254282
if (!MaybeEntry)
255283
return MaybeEntry.getError();
256-
return scanForDirectivesIfNecessary(*MaybeEntry, Filename,
284+
return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
257285
DisableDirectivesScanning)
258286
.unwrapError();
259287
}
@@ -331,3 +359,24 @@ DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) {
331359
return Result.getError();
332360
return DepScanFile::create(Result.get());
333361
}
362+
363+
std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory(
364+
const Twine &Path) {
365+
std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
366+
updateWorkingDirForCacheLookup();
367+
return EC;
368+
}
369+
370+
void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
371+
llvm::ErrorOr<std::string> CWD =
372+
getUnderlyingFS().getCurrentWorkingDirectory();
373+
if (!CWD) {
374+
WorkingDirForCacheLookup = CWD.getError();
375+
} else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
376+
WorkingDirForCacheLookup = llvm::errc::invalid_argument;
377+
} else {
378+
WorkingDirForCacheLookup = *CWD;
379+
}
380+
assert(!WorkingDirForCacheLookup ||
381+
llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
382+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
4+
5+
// RUN: clang-scan-deps -compilation-database %t/cdb.json -format make -j 1 > %t/result.txt
6+
// RUN: FileCheck %s -input-file=%t/result.txt
7+
8+
// CHECK: {{/|\\}}dir1{{/|\\}}t1.c
9+
// CHECK: {{/|\\}}dir1{{/|\\}}head.h
10+
// CHECK: {{/|\\}}dir2{{/|\\}}t2.c
11+
// CHECK: {{/|\\}}dir2{{/|\\}}head.h
12+
13+
//--- cdb.json.template
14+
[
15+
{
16+
"directory": "DIR/dir1",
17+
"command": "clang -fsyntax-only t1.c",
18+
"file": "t1.c"
19+
},
20+
{
21+
"directory": "DIR/dir2",
22+
"command": "clang -fsyntax-only t2.c",
23+
"file": "t2.c"
24+
}
25+
]
26+
27+
//--- dir1/t1.c
28+
#include "head.h"
29+
30+
//--- dir1/head.h
31+
#ifndef BBB
32+
#define BBB
33+
#endif
34+
35+
//--- dir2/t2.c
36+
#include "head.h"
37+
38+
//--- dir2/head.h

0 commit comments

Comments
 (0)