hokein updated this revision to Diff 49027.
hokein marked an inline comment as done.
hokein added a comment.
Make apply-fix work on relative path in `command` field of compilation database.
http://reviews.llvm.org/D17335
Files:
clang-tidy/ClangTidy.cpp
clang-tidy/ClangTidy.h
clang-tidy/ClangTidyDiagnosticConsumer.cpp
clang-tidy/ClangTidyDiagnosticConsumer.h
clang-tidy/tool/ClangTidyMain.cpp
test/clang-tidy/Inputs/compilation-database/header.h
test/clang-tidy/Inputs/compilation-database/template.json
test/clang-tidy/clang-tidy-run-with-database.cpp
Index: test/clang-tidy/clang-tidy-run-with-database.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/clang-tidy-run-with-database.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: shell
+// RUN: mkdir -p %T/compilation-database-test
+// RUN: mkdir -p %T/compilation-database-test/include
+// RUN: mkdir -p %T/compilation-database-test/a
+// RUN: mkdir -p %T/compilation-database-test/b
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: sed 's|pointer|AA|g' %t.cpp > %T/compilation-database-test/a/a.cpp
+// RUN: sed 's|pointer|AA|g' %s > %T/compilation-database-test/a/a.cpp.msg
+// RUN: sed 's|pointer|AB|g' %t.cpp > %T/compilation-database-test/a/b.cpp
+// RUN: sed 's|pointer|AB|g' %s > %T/compilation-database-test/a/b.cpp.msg
+// RUN: sed 's|pointer|BB|g' %t.cpp > %T/compilation-database-test/b/b.cpp
+// RUN: sed 's|pointer|BB|g' %s > %T/compilation-database-test/b/b.cpp.msg
+// RUN: sed 's|pointer|BC|g' %t.cpp > %T/compilation-database-test/b/c.cpp
+// RUN: sed 's|pointer|BC|g' %s > %T/compilation-database-test/b/c.cpp.msg
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/compilation-database/header.h > %T/compilation-database-test/include/header.h
+// RUN: echo '#include "header.h"' > %T/compilation-database-test/b/d.cpp
+// RUN: sed 's|test_dir|%T/compilation-database-test|g' %S/Inputs/compilation-database/template.json > %T/compile_commands.json
+// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/b/not-exist -header-filter=.*
+// 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
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/a.cpp.msg -check-prefix=CHECK-FIX
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/b.cpp %T/compilation-database-test/a/b.cpp.msg -check-prefix=CHECK-FIX
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/b.cpp.msg -check-prefix=CHECK-FIX
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/c.cpp.msg -check-prefix=CHECK-FIX
+// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %S/Inputs/compilation-database/header.h -check-prefix=CHECK-FIX
+#define NULL 0
+
+int *pointer = NULL;
+// CHECK-FIX: int *pointer = nullptr;
+// CHECK: warning: use nullptr [modernize-use-nullpt]
Index: test/clang-tidy/Inputs/compilation-database/template.json
===================================================================
--- /dev/null
+++ test/clang-tidy/Inputs/compilation-database/template.json
@@ -0,0 +1,32 @@
+[
+{
+ "directory": "test_dir/a",
+ "command": "clang++ -o test.o test_dir/a/a.cpp",
+ "file": "test_dir/a/a.cpp"
+},
+{
+ "directory": "test_dir/a",
+ "command": "clang++ -o test.o test_dir/a/b.cpp",
+ "file": "test_dir/a/b.cpp"
+},
+{
+ "directory": "test_dir/",
+ "command": "clang++ -o test.o test_dir/b/b.cpp",
+ "file": "test_dir/b/b.cpp"
+},
+{
+ "directory": "test_dir/b",
+ "command": "clang++ -o test.o ../b/c.cpp",
+ "file": "test_dir/b/c.cpp"
+},
+{
+ "directory": "test_dir/b",
+ "command": "clang++ -I../include -o test.o ../b/d.cpp",
+ "file": "test_dir/b/d.cpp"
+},
+{
+ "directory": "test_dir/",
+ "command": "clang++ -o test.o test_dir/b/not-exist.cpp",
+ "file": "test_dir/b/not-exist.cpp"
+}
+]
Index: test/clang-tidy/Inputs/compilation-database/header.h
===================================================================
--- /dev/null
+++ test/clang-tidy/Inputs/compilation-database/header.h
@@ -0,0 +1,4 @@
+#define NULL 0
+
+int *p = NULL;
+// CHECK-FIX: int *p = nullptr;
Index: clang-tidy/tool/ClangTidyMain.cpp
===================================================================
--- clang-tidy/tool/ClangTidyMain.cpp
+++ clang-tidy/tool/ClangTidyMain.cpp
@@ -342,11 +342,13 @@
ProfileData Profile;
- std::vector<ClangTidyError> Errors;
- ClangTidyStats Stats =
- runClangTidy(std::move(OptionsProvider), OptionsParser.getCompilations(),
- PathList, &Errors,
- EnableCheckProfile ? &Profile : nullptr);
+ clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
+ /// If Profile provided, it enables check profile collection in
+ /// MatchFinder, and will contain the result of the profile.
+ if (EnableCheckProfile)
+ Context.setCheckProfileData(&Profile);
+ runClangTidy(Context, OptionsParser.getCompilations(), PathList);
+ const std::vector<ClangTidyError> &Errors = Context.getErrors();
bool FoundErrors =
std::find_if(Errors.begin(), Errors.end(), [](const ClangTidyError &E) {
return E.DiagLevel == ClangTidyError::Error;
@@ -369,7 +371,7 @@
exportReplacements(Errors, OS);
}
- printStats(Stats);
+ printStats(Context.getStats());
if (DisableFixes)
llvm::errs()
<< "Found compiler errors, but -fix-errors was not specified.\n"
Index: clang-tidy/ClangTidyDiagnosticConsumer.h
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.h
+++ clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -57,13 +57,24 @@
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. The string is owned by
+ // ClangTidyContext.
+ //
+ // 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: In unittest, it is empty.
+ StringRef BuildDirectory;
+
Level DiagLevel;
bool IsWarningAsError;
};
@@ -198,6 +209,9 @@
void setCheckProfileData(ProfileData *Profile);
ProfileData *getCheckProfileData() const { return Profile; }
+ /// \brief Returns all build directories.
+ std::vector<std::string> &getBuildDirectories() { return BuildDirectories; }
+
private:
// Calls setDiagnosticsEngine() and storeError().
friend class ClangTidyDiagnosticConsumer;
@@ -222,6 +236,8 @@
ClangTidyStats Stats;
+ std::vector<std::string> BuildDirectories;
+
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
ProfileData *Profile;
Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ 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,14 @@
bool IsWarningAsError =
DiagLevel == DiagnosticsEngine::Warning &&
Context.getWarningAsErrorFilter().contains(CheckName);
- Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError));
+ if (Context.getBuildDirectories().empty()) {
+ // In unittest, the BuildDirectories are empty.
+ Errors.push_back(
+ ClangTidyError(CheckName, Level, IsWarningAsError, StringRef()));
+ } else {
+ Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError,
+ Context.getBuildDirectories().back()));
+ }
}
// FIXME: Provide correct LangOptions for each file.
Index: clang-tidy/ClangTidy.h
===================================================================
--- clang-tidy/ClangTidy.h
+++ clang-tidy/ClangTidy.h
@@ -210,12 +210,10 @@
///
/// \param Profile if provided, it enables check profile collection in
/// MatchFinder, and will contain the result of the profile.
-ClangTidyStats
-runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
+void
+runClangTidy(clang::tidy::ClangTidyContext& Context,
const tooling::CompilationDatabase &Compilations,
- ArrayRef<std::string> InputFiles,
- std::vector<ClangTidyError> *Errors,
- ProfileData *Profile = nullptr);
+ ArrayRef<std::string> InputFiles);
// FIXME: This interface will need to be significantly extended to be useful.
// FIXME: Implement confidence levels for displaying/fixing errors.
Index: clang-tidy/ClangTidy.cpp
===================================================================
--- clang-tidy/ClangTidy.cpp
+++ 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,16 @@
Context.setCurrentFile(File);
Context.setASTContext(&Compiler.getASTContext());
+ auto WorkingDir = Compiler.getSourceManager()
+ .getFileManager()
+ .getVirtualFileSystem()
+ ->getCurrentWorkingDirectory();
+ if (WorkingDir) {
+ if (Context.getBuildDirectories().empty() ||
+ Context.getBuildDirectories().back() != WorkingDir.get())
+ Context.getBuildDirectories().push_back(WorkingDir.get());
+ }
+
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
@@ -389,13 +406,10 @@
return Factory.getCheckOptions();
}
-ClangTidyStats
-runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
- const tooling::CompilationDatabase &Compilations,
- ArrayRef<std::string> InputFiles,
- std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
+void runClangTidy(clang::tidy::ClangTidyContext &Context,
+ const tooling::CompilationDatabase &Compilations,
+ ArrayRef<std::string> InputFiles) {
ClangTool Tool(Compilations, InputFiles);
- clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
ArgumentsAdjuster PerFileExtraArgumentsInserter = [&Context](
const CommandLineArguments &Args, StringRef Filename) {
ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
@@ -409,8 +423,6 @@
return AdjustedArgs;
};
Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
- if (Profile)
- Context.setCheckProfileData(Profile);
ClangTidyDiagnosticConsumer DiagConsumer(Context);
@@ -439,15 +451,29 @@
ActionFactory Factory(Context);
Tool.run(&Factory);
- *Errors = Context.getErrors();
- return Context.getStats();
}
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();
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits