Skip to content

Commit 07157db

Browse files
authored
[clang][tidy] Ensure rewriter has the correct CWD (#67839)
This patch replaces use of the deprecated `FileEntry::getName()` with `FileEntryRef::getName()`. This means the code now uses the path that was used to register file entry in `SourceManager` instead of the absolute path that happened to be used in the last call to `FileManager::getFile()` some place else. This caused some test failures due to the fact that some paths can be relative and thus rely on the VFS CWD. The CWD can change for each TU, so when we run `clang-tidy` on a compilation database and try to perform all the replacements at the end, relative paths won't resolve the same. This patch takes care to reinstate the correct CWD and make the path reported by `FileEntryRef` absolute before passing it to `llvm::writeToOutput()`.
1 parent 9f87509 commit 07157db

File tree

4 files changed

+44
-18
lines changed

4 files changed

+44
-18
lines changed

clang-tools-extra/clang-tidy/ClangTidy.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ class ErrorReporter {
147147
Files.makeAbsolutePath(FixAbsoluteFilePath);
148148
tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
149149
Repl.getLength(), Repl.getReplacementText());
150-
Replacements &Replacements = FileReplacements[R.getFilePath()];
150+
auto &Entry = FileReplacements[R.getFilePath()];
151+
Replacements &Replacements = Entry.Replacements;
151152
llvm::Error Err = Replacements.add(R);
152153
if (Err) {
153154
// FIXME: Implement better conflict handling.
@@ -174,6 +175,7 @@ class ErrorReporter {
174175
}
175176
FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
176177
FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
178+
Entry.BuildDir = Error.BuildDirectory;
177179
}
178180
}
179181
}
@@ -189,9 +191,14 @@ class ErrorReporter {
189191

190192
void finish() {
191193
if (TotalFixes > 0) {
192-
Rewriter Rewrite(SourceMgr, LangOpts);
194+
auto &VFS = Files.getVirtualFileSystem();
195+
auto OriginalCWD = VFS.getCurrentWorkingDirectory();
196+
bool AnyNotWritten = false;
197+
193198
for (const auto &FileAndReplacements : FileReplacements) {
199+
Rewriter Rewrite(SourceMgr, LangOpts);
194200
StringRef File = FileAndReplacements.first();
201+
VFS.setCurrentWorkingDirectory(FileAndReplacements.second.BuildDir);
195202
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
196203
SourceMgr.getFileManager().getBufferForFile(File);
197204
if (!Buffer) {
@@ -208,8 +215,8 @@ class ErrorReporter {
208215
continue;
209216
}
210217
llvm::Expected<tooling::Replacements> Replacements =
211-
format::cleanupAroundReplacements(Code, FileAndReplacements.second,
212-
*Style);
218+
format::cleanupAroundReplacements(
219+
Code, FileAndReplacements.second.Replacements, *Style);
213220
if (!Replacements) {
214221
llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
215222
continue;
@@ -226,13 +233,18 @@ class ErrorReporter {
226233
if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
227234
llvm::errs() << "Can't apply replacements for file " << File << "\n";
228235
}
236+
AnyNotWritten &= Rewrite.overwriteChangedFiles();
229237
}
230-
if (Rewrite.overwriteChangedFiles()) {
238+
239+
if (AnyNotWritten) {
231240
llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
232241
} else {
233242
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
234243
<< TotalFixes << " suggested fixes.\n";
235244
}
245+
246+
if (OriginalCWD)
247+
VFS.setCurrentWorkingDirectory(*OriginalCWD);
236248
}
237249
}
238250

@@ -289,13 +301,18 @@ class ErrorReporter {
289301
return CharSourceRange::getCharRange(BeginLoc, EndLoc);
290302
}
291303

304+
struct ReplacementsWithBuildDir {
305+
StringRef BuildDir;
306+
Replacements Replacements;
307+
};
308+
292309
FileManager Files;
293310
LangOptions LangOpts; // FIXME: use langopts from each original file
294311
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
295312
DiagnosticConsumer *DiagPrinter;
296313
DiagnosticsEngine Diags;
297314
SourceManager SourceMgr;
298-
llvm::StringMap<Replacements> FileReplacements;
315+
llvm::StringMap<ReplacementsWithBuildDir> FileReplacements;
299316
ClangTidyContext &Context;
300317
FixBehaviour ApplyFixes;
301318
unsigned TotalFixes = 0U;

clang-tools-extra/test/clang-tidy/infrastructure/Inputs/compilation-database/template.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@
2020
"file": "test_dir/b/c.cpp"
2121
},
2222
{
23-
"directory": "test_dir/b",
24-
"command": "clang++ -I../include -o test.o ../b/d.cpp",
23+
"directory": "test_dir/",
24+
"command": "clang++ -o test.o ./b/d.cpp",
2525
"file": "test_dir/b/d.cpp"
2626
},
27+
{
28+
"directory": "test_dir/b",
29+
"command": "clang++ -I../include -o test.o ../include.cpp",
30+
"file": "test_dir/include.cpp"
31+
},
2732
{
2833
"directory": "test_dir/",
2934
"command": "clang++ -o test.o test_dir/b/not-exist.cpp",

clang-tools-extra/test/clang-tidy/infrastructure/clang-tidy-run-with-database.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
// RUN: echo 'int *AB = 0;' > %T/compilation-database-test/a/b.cpp
66
// RUN: echo 'int *BB = 0;' > %T/compilation-database-test/b/b.cpp
77
// RUN: echo 'int *BC = 0;' > %T/compilation-database-test/b/c.cpp
8+
// RUN: echo 'int *BD = 0;' > %T/compilation-database-test/b/d.cpp
89
// RUN: echo 'int *HP = 0;' > %T/compilation-database-test/include/header.h
9-
// RUN: echo '#include "header.h"' > %T/compilation-database-test/b/d.cpp
10+
// RUN: echo '#include "header.h"' > %T/compilation-database-test/include.cpp
1011
// RUN: sed 's|test_dir|%/T/compilation-database-test|g' %S/Inputs/compilation-database/template.json > %T/compile_commands.json
1112

1213
// Regression test: shouldn't crash.
@@ -15,15 +16,17 @@
1516
// CHECK-NOT-EXIST: unable to handle compilation
1617
// CHECK-NOT-EXIST: Found compiler error
1718

18-
// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp -header-filter=.* -fix
19+
// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp %T/compilation-database-test/include.cpp -header-filter=.* -fix
1920
// RUN: FileCheck -input-file=%T/compilation-database-test/a/a.cpp %s -check-prefix=CHECK-FIX1
2021
// RUN: FileCheck -input-file=%T/compilation-database-test/a/b.cpp %s -check-prefix=CHECK-FIX2
2122
// RUN: FileCheck -input-file=%T/compilation-database-test/b/b.cpp %s -check-prefix=CHECK-FIX3
2223
// RUN: FileCheck -input-file=%T/compilation-database-test/b/c.cpp %s -check-prefix=CHECK-FIX4
23-
// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX5
24+
// RUN: FileCheck -input-file=%T/compilation-database-test/b/d.cpp %s -check-prefix=CHECK-FIX5
25+
// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX6
2426

2527
// CHECK-FIX1: int *AA = nullptr;
2628
// CHECK-FIX2: int *AB = nullptr;
2729
// CHECK-FIX3: int *BB = nullptr;
2830
// CHECK-FIX4: int *BC = nullptr;
29-
// CHECK-FIX5: int *HP = nullptr;
31+
// CHECK-FIX5: int *BD = nullptr;
32+
// CHECK-FIX6: int *HP = nullptr;

clang/lib/Rewrite/Rewriter.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,12 +412,13 @@ bool Rewriter::overwriteChangedFiles() {
412412
unsigned OverwriteFailure = Diag.getCustomDiagID(
413413
DiagnosticsEngine::Error, "unable to overwrite file %0: %1");
414414
for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
415-
const FileEntry *Entry = getSourceMgr().getFileEntryForID(I->first);
416-
if (auto Error =
417-
llvm::writeToOutput(Entry->getName(), [&](llvm::raw_ostream &OS) {
418-
I->second.write(OS);
419-
return llvm::Error::success();
420-
})) {
415+
OptionalFileEntryRef Entry = getSourceMgr().getFileEntryRefForID(I->first);
416+
llvm::SmallString<128> Path(Entry->getName());
417+
getSourceMgr().getFileManager().makeAbsolutePath(Path);
418+
if (auto Error = llvm::writeToOutput(Path, [&](llvm::raw_ostream &OS) {
419+
I->second.write(OS);
420+
return llvm::Error::success();
421+
})) {
421422
Diag.Report(OverwriteFailure)
422423
<< Entry->getName() << llvm::toString(std::move(Error));
423424
AllWritten = false;

0 commit comments

Comments
 (0)