Skip to content

Commit 36b37c7

Browse files
authored
[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.
1 parent ab94fbb commit 36b37c7

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
@@ -215,6 +215,7 @@ class DependencyScanningFilesystemLocalCache {
215215
public:
216216
/// Returns entry associated with the filename or nullptr if none is found.
217217
const CachedFileSystemEntry *findEntryByFilename(StringRef Filename) const {
218+
assert(llvm::sys::path::is_absolute_gnu(Filename));
218219
auto It = Cache.find(Filename);
219220
return It == Cache.end() ? nullptr : It->getValue();
220221
}
@@ -224,6 +225,7 @@ class DependencyScanningFilesystemLocalCache {
224225
const CachedFileSystemEntry &
225226
insertEntryForFilename(StringRef Filename,
226227
const CachedFileSystemEntry &Entry) {
228+
assert(llvm::sys::path::is_absolute_gnu(Filename));
227229
const auto *InsertedEntry = Cache.insert({Filename, &Entry}).first->second;
228230
assert(InsertedEntry == &Entry && "entry already present");
229231
return *InsertedEntry;
@@ -282,13 +284,14 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
282284
public:
283285
DependencyScanningWorkerFilesystem(
284286
DependencyScanningFilesystemSharedCache &SharedCache,
285-
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
286-
: ProxyFileSystem(std::move(FS)), SharedCache(SharedCache) {}
287+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS);
287288

288289
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override;
289290
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
290291
openFileForRead(const Twine &Path) override;
291292

293+
std::error_code setCurrentWorkingDirectory(const Twine &Path) override;
294+
292295
/// Returns entry for the given filename.
293296
///
294297
/// Attempts to use the local and shared caches first, then falls back to
@@ -304,8 +307,11 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
304307
/// For a filename that's not yet associated with any entry in the caches,
305308
/// uses the underlying filesystem to either look up the entry based in the
306309
/// shared cache indexed by unique ID, or creates new entry from scratch.
310+
/// \p FilenameForLookup will always be an absolute path, and different than
311+
/// \p OriginalFilename if \p OriginalFilename is relative.
307312
llvm::ErrorOr<const CachedFileSystemEntry &>
308-
computeAndStoreResult(StringRef Filename);
313+
computeAndStoreResult(StringRef OriginalFilename,
314+
StringRef FilenameForLookup);
309315

310316
/// Scan for preprocessor directives for the given entry if necessary and
311317
/// returns a wrapper object with reference semantics.
@@ -388,6 +394,12 @@ class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem {
388394
/// The local cache is used by the worker thread to cache file system queries
389395
/// locally instead of querying the global cache every time.
390396
DependencyScanningFilesystemLocalCache LocalCache;
397+
398+
/// The working directory to use for making relative paths absolute before
399+
/// using them for cache lookups.
400+
llvm::ErrorOr<std::string> WorkingDirForCacheLookup;
401+
402+
void updateWorkingDirForCacheLookup();
391403
};
392404

393405
} // end namespace dependencies

clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ DependencyScanningFilesystemSharedCache::
9696
DependencyScanningFilesystemSharedCache::CacheShard &
9797
DependencyScanningFilesystemSharedCache::getShardForFilename(
9898
StringRef Filename) const {
99+
assert(llvm::sys::path::is_absolute_gnu(Filename));
99100
return CacheShards[llvm::hash_value(Filename) % NumShards];
100101
}
101102

@@ -109,6 +110,7 @@ DependencyScanningFilesystemSharedCache::getShardForUID(
109110
const CachedFileSystemEntry *
110111
DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
111112
StringRef Filename) const {
113+
assert(llvm::sys::path::is_absolute_gnu(Filename));
112114
std::lock_guard<std::mutex> LockGuard(CacheLock);
113115
auto It = EntriesByFilename.find(Filename);
114116
return It == EntriesByFilename.end() ? nullptr : It->getValue();
@@ -189,6 +191,14 @@ static bool shouldCacheStatFailures(StringRef Filename) {
189191
return shouldScanForDirectivesBasedOnExtension(Filename);
190192
}
191193

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

217227
llvm::ErrorOr<const CachedFileSystemEntry &>
218-
DependencyScanningWorkerFilesystem::computeAndStoreResult(StringRef Filename) {
219-
llvm::ErrorOr<llvm::vfs::Status> Stat = getUnderlyingFS().status(Filename);
228+
DependencyScanningWorkerFilesystem::computeAndStoreResult(
229+
StringRef OriginalFilename, StringRef FilenameForLookup) {
230+
llvm::ErrorOr<llvm::vfs::Status> Stat =
231+
getUnderlyingFS().status(OriginalFilename);
220232
if (!Stat) {
221-
if (!shouldCacheStatFailures(Filename))
233+
if (!shouldCacheStatFailures(OriginalFilename))
222234
return Stat.getError();
223235
const auto &Entry =
224-
getOrEmplaceSharedEntryForFilename(Filename, Stat.getError());
225-
return insertLocalEntryForFilename(Filename, Entry);
236+
getOrEmplaceSharedEntryForFilename(FilenameForLookup, Stat.getError());
237+
return insertLocalEntryForFilename(FilenameForLookup, Entry);
226238
}
227239

228240
if (const auto *Entry = findSharedEntryByUID(*Stat))
229-
return insertLocalEntryForFilename(Filename, *Entry);
241+
return insertLocalEntryForFilename(FilenameForLookup, *Entry);
230242

231243
auto TEntry =
232-
Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(Filename);
244+
Stat->isDirectory() ? TentativeEntry(*Stat) : readFile(OriginalFilename);
233245

234246
const CachedFileSystemEntry *SharedEntry = [&]() {
235247
if (TEntry) {
236248
const auto &UIDEntry = getOrEmplaceSharedEntryForUID(std::move(*TEntry));
237-
return &getOrInsertSharedEntryForFilename(Filename, UIDEntry);
249+
return &getOrInsertSharedEntryForFilename(FilenameForLookup, UIDEntry);
238250
}
239-
return &getOrEmplaceSharedEntryForFilename(Filename, TEntry.getError());
251+
return &getOrEmplaceSharedEntryForFilename(FilenameForLookup,
252+
TEntry.getError());
240253
}();
241254

242-
return insertLocalEntryForFilename(Filename, *SharedEntry);
255+
return insertLocalEntryForFilename(FilenameForLookup, *SharedEntry);
243256
}
244257

245258
llvm::ErrorOr<EntryRef>
246259
DependencyScanningWorkerFilesystem::getOrCreateFileSystemEntry(
247-
StringRef Filename, bool DisableDirectivesScanning) {
248-
if (const auto *Entry = findEntryByFilenameWithWriteThrough(Filename))
249-
return scanForDirectivesIfNecessary(*Entry, Filename,
260+
StringRef OriginalFilename, bool DisableDirectivesScanning) {
261+
StringRef FilenameForLookup;
262+
SmallString<256> PathBuf;
263+
if (llvm::sys::path::is_absolute_gnu(OriginalFilename)) {
264+
FilenameForLookup = OriginalFilename;
265+
} else if (!WorkingDirForCacheLookup) {
266+
return WorkingDirForCacheLookup.getError();
267+
} else {
268+
StringRef RelFilename = OriginalFilename;
269+
RelFilename.consume_front("./");
270+
PathBuf = *WorkingDirForCacheLookup;
271+
llvm::sys::path::append(PathBuf, RelFilename);
272+
FilenameForLookup = PathBuf.str();
273+
}
274+
assert(llvm::sys::path::is_absolute_gnu(FilenameForLookup));
275+
if (const auto *Entry =
276+
findEntryByFilenameWithWriteThrough(FilenameForLookup))
277+
return scanForDirectivesIfNecessary(*Entry, OriginalFilename,
250278
DisableDirectivesScanning)
251279
.unwrapError();
252-
auto MaybeEntry = computeAndStoreResult(Filename);
280+
auto MaybeEntry = computeAndStoreResult(OriginalFilename, FilenameForLookup);
253281
if (!MaybeEntry)
254282
return MaybeEntry.getError();
255-
return scanForDirectivesIfNecessary(*MaybeEntry, Filename,
283+
return scanForDirectivesIfNecessary(*MaybeEntry, OriginalFilename,
256284
DisableDirectivesScanning)
257285
.unwrapError();
258286
}
@@ -330,3 +358,24 @@ DependencyScanningWorkerFilesystem::openFileForRead(const Twine &Path) {
330358
return Result.getError();
331359
return DepScanFile::create(Result.get());
332360
}
361+
362+
std::error_code DependencyScanningWorkerFilesystem::setCurrentWorkingDirectory(
363+
const Twine &Path) {
364+
std::error_code EC = ProxyFileSystem::setCurrentWorkingDirectory(Path);
365+
updateWorkingDirForCacheLookup();
366+
return EC;
367+
}
368+
369+
void DependencyScanningWorkerFilesystem::updateWorkingDirForCacheLookup() {
370+
llvm::ErrorOr<std::string> CWD =
371+
getUnderlyingFS().getCurrentWorkingDirectory();
372+
if (!CWD) {
373+
WorkingDirForCacheLookup = CWD.getError();
374+
} else if (!llvm::sys::path::is_absolute_gnu(*CWD)) {
375+
WorkingDirForCacheLookup = llvm::errc::invalid_argument;
376+
} else {
377+
WorkingDirForCacheLookup = *CWD;
378+
}
379+
assert(!WorkingDirForCacheLookup ||
380+
llvm::sys::path::is_absolute_gnu(*WorkingDirForCacheLookup));
381+
}
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)