hokein updated this revision to Diff 48924.
hokein added a comment.

Update:

- Update test to apply multiple files and fixes.
- Make it work with multiple files.


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/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,26 @@
+// REQUIRES: shell
+// RUN: mkdir -p %T/compilation-database-test
+// 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: 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 -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
+
+#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,27 @@
+[
+{
+  "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/",
+  "command": "clang++ -o test.o test_dir/b/c.cpp",
+  "file": "test_dir/b/c.cpp"
+},
+{
+  "directory": "test_dir/",
+  "command": "clang++ -o test.o test_dir/b/not-exist.cpp",
+  "file": "test_dir/b/not-exist.cpp"
+}
+]
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);
@@ -232,6 +236,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 +403,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 +420,6 @@
     return AdjustedArgs;
   };
   Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
-  if (Profile)
-    Context.setCheckProfileData(Profile);
 
   ClangTidyDiagnosticConsumer DiagConsumer(Context);
 
@@ -439,15 +448,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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to