[clang-tidy] Fix a crash issue when clang-tidy runs with compilation database.
Summary:
The clang-tidy will trigger an assertion if it's not in the building directory.
TEST:
cd <llvm-repo>/
./build/bin/clang-tidy --checks=-*,modernize-use-nullptr -p build tools/clang/tools/extra/clang-tidy/ClangTidy.cpp
The crash issue is gone after applying this patch.
Fixes PR24834, PR26241
Reviewers: bkramer, alexfh
Subscribers: rizsotto.mailinglist, cfe-commits
Differential Revision: http://reviews.llvm.org/D17335
llvm-svn: 261991
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index b2dd7e7..9fbe0dd 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -107,6 +107,10 @@
DiagPrinter->BeginSourceFile(LangOpts);
}
+ SourceManager& getSourceManager() {
+ return SourceMgr;
+ }
+
void reportDiagnostic(const ClangTidyError &Error) {
const ClangTidyMessage &Message = Error.Message;
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
@@ -124,7 +128,10 @@
auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
<< Message.Message << Name;
for (const tooling::Replacement &Fix : Error.Fix) {
- SourceLocation FixLoc = getLocation(Fix.getFilePath(), Fix.getOffset());
+ SmallString<128> FixAbsoluteFilePath = Fix.getFilePath();
+ Files.makeAbsolutePath(FixAbsoluteFilePath);
+ SourceLocation FixLoc =
+ getLocation(FixAbsoluteFilePath, Fix.getOffset());
SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
Diag << FixItHint::CreateReplacement(SourceRange(FixLoc, FixEndLoc),
Fix.getReplacementText());
@@ -232,6 +239,13 @@
Context.setCurrentFile(File);
Context.setASTContext(&Compiler.getASTContext());
+ auto WorkingDir = Compiler.getSourceManager()
+ .getFileManager()
+ .getVirtualFileSystem()
+ ->getCurrentWorkingDirectory();
+ if (WorkingDir)
+ Context.setCurrentBuildDirectory(WorkingDir.get());
+
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
@@ -446,8 +460,24 @@
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Fix);
- for (const ClangTidyError &Error : Errors)
+ vfs::FileSystem &FileSystem =
+ *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
+ auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
+ if (!InitialWorkingDir)
+ llvm::report_fatal_error("Cannot get current working path.");
+
+ for (const ClangTidyError &Error : Errors) {
+ if (!Error.BuildDirectory.empty()) {
+ // By default, the working directory of file system is the current
+ // clang-tidy running directory.
+ //
+ // Change the directory to the one used during the analysis.
+ FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
+ }
Reporter.reportDiagnostic(Error);
+ // Return to the initial directory to correctly resolve next Error.
+ FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
+ }
Reporter.Finish();
WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
}
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index d1cb4eb..48ae922 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -116,8 +116,9 @@
ClangTidyError::ClangTidyError(StringRef CheckName,
ClangTidyError::Level DiagLevel,
- bool IsWarningAsError)
- : CheckName(CheckName), DiagLevel(DiagLevel),
+ bool IsWarningAsError,
+ StringRef BuildDirectory)
+ : CheckName(CheckName), BuildDirectory(BuildDirectory), DiagLevel(DiagLevel),
IsWarningAsError(IsWarningAsError) {}
// Returns true if GlobList starts with the negative indicator ('-'), removes it
@@ -335,7 +336,8 @@
bool IsWarningAsError =
DiagLevel == DiagnosticsEngine::Warning &&
Context.getWarningAsErrorFilter().contains(CheckName);
- Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError));
+ Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError,
+ Context.getCurrentBuildDirectory()));
}
ClangTidyDiagnosticRenderer Converter(
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
index cbd376c..0adfe2f 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -57,13 +57,23 @@
Error = DiagnosticsEngine::Error
};
- ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError);
+ ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError,
+ StringRef BuildDirectory);
std::string CheckName;
ClangTidyMessage Message;
tooling::Replacements Fix;
SmallVector<ClangTidyMessage, 1> Notes;
+ // A build directory of the diagnostic source file.
+ //
+ // It's an absolute path which is `directory` field of the source file in
+ // compilation database. If users don't specify the compilation database
+ // directory, it is the current directory where clang-tidy runs.
+ //
+ // Note: it is empty in unittest.
+ std::string BuildDirectory;
+
Level DiagLevel;
bool IsWarningAsError;
};
@@ -198,6 +208,16 @@
void setCheckProfileData(ProfileData *Profile);
ProfileData *getCheckProfileData() const { return Profile; }
+ /// \brief Should be called when starting to process new translation unit.
+ void setCurrentBuildDirectory(StringRef BuildDirectory) {
+ CurrentBuildDirectory = BuildDirectory;
+ }
+
+ /// \brief Returns build directory of the current translation unit.
+ const std::string &getCurrentBuildDirectory() {
+ return CurrentBuildDirectory;
+ }
+
private:
// Calls setDiagnosticsEngine() and storeError().
friend class ClangTidyDiagnosticConsumer;
@@ -222,6 +242,8 @@
ClangTidyStats Stats;
+ std::string CurrentBuildDirectory;
+
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
ProfileData *Profile;