hokein created this revision.
Herald added subscribers: cfe-commits, mgrang, xazax.hun.

Only works on StandaloneToolExecutor, crashes happen when running
AllTUsExecutors with multithreads (I believe it is because
ClangTidyContext is not threadsafe).

  // Restrict thread to 1.
  ./bin/clang-tidy -executor=all-TUs -filter=".*/extra/*" 
-checks="-*,readability-braces-around-statements" -execute-concurrency=1 
-export-fixes="/tmp/fixes.yaml" .


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D54257

Files:
  clang-tidy/ClangTidy.cpp
  clang-tidy/ClangTidy.h
  clang-tidy/ClangTidyDiagnosticConsumer.cpp
  clang-tidy/ClangTidyDiagnosticConsumer.h
  clang-tidy/tool/ClangTidyMain.cpp

Index: clang-tidy/tool/ClangTidyMain.cpp
===================================================================
--- clang-tidy/tool/ClangTidyMain.cpp
+++ clang-tidy/tool/ClangTidyMain.cpp
@@ -18,6 +18,7 @@
 #include "../ClangTidy.h"
 #include "clang/Config/config.h"
 #include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Execution.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/TargetSelect.h"
 
@@ -328,8 +329,14 @@
 }
 
 static int clangTidyMain(int argc, const char **argv) {
-  CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory,
-                                    cl::ZeroOrMore);
+  // CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory,
+  //                                   cl::ZeroOrMore);
+  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
+      argc, argv, ClangTidyCategory);
+  if (!Executor) {
+    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
+    return 1;
+  }
   llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS(
       VfsOverlay.empty() ? vfs::getRealFileSystem()
                          : getVfsOverlayFromFile(VfsOverlay));
@@ -355,10 +362,10 @@
   SmallString<256> ProfilePrefix = MakeAbsolute(StoreCheckProfile);
 
   StringRef FileName("dummy");
-  auto PathList = OptionsParser.getSourcePathList();
-  if (!PathList.empty()) {
-    FileName = PathList.front();
-  }
+  // auto PathList = OptionsParser.getSourcePathList();
+  // if (!PathList.empty()) {
+  //   FileName = PathList.front();
+  // }
 
   SmallString<256> FilePath = MakeAbsolute(FileName);
 
@@ -410,21 +417,20 @@
     return 1;
   }
 
-  if (PathList.empty()) {
-    llvm::errs() << "Error: no input files specified.\n";
-    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
-    return 1;
-  }
+  // if (PathList.empty()) {
+  //   llvm::errs() << "Error: no input files specified.\n";
+  //   llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+  //   return 1;
+  // }
 
   llvm::InitializeAllTargetInfos();
   llvm::InitializeAllTargetMCs();
   llvm::InitializeAllAsmParsers();
 
   ClangTidyContext Context(std::move(OwningOptionsProvider),
                            AllowEnablingAnalyzerAlphaCheckers);
-  std::vector<ClangTidyError> Errors =
-      runClangTidy(Context, OptionsParser.getCompilations(), PathList, BaseFS,
-                   EnableCheckProfile, ProfilePrefix);
+  std::vector<ClangTidyError> Errors = runClangTidy(
+      Context, std::move(*Executor), BaseFS, EnableCheckProfile, ProfilePrefix);
   bool FoundErrors = llvm::find_if(Errors, [](const ClangTidyError &E) {
                        return E.DiagLevel == ClangTidyError::Error;
                      }) != Errors.end();
Index: clang-tidy/ClangTidyDiagnosticConsumer.h
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.h
+++ clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -47,6 +47,20 @@
 
   bool IsWarningAsError;
 };
+inline bool operator<(const ClangTidyError &LHS, const ClangTidyError &RHS) {
+  const tooling::DiagnosticMessage &M1 = LHS.Message;
+  const tooling::DiagnosticMessage &M2 = RHS.Message;
+
+  return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
+         std::tie(M2.FilePath, M2.FileOffset, M2.Message);
+};
+inline bool operator==(const ClangTidyError &LHS, const ClangTidyError &RHS) {
+  const tooling::DiagnosticMessage &M1 = LHS.Message;
+  const tooling::DiagnosticMessage &M2 = RHS.Message;
+
+  return std::tie(M1.FilePath, M1.FileOffset, M1.Message) ==
+         std::tie(M2.FilePath, M2.FileOffset, M2.Message);
+}
 
 /// \brief Read-only set of strings represented as a list of positive and
 /// negative globs. Positive globs add all matched strings to the set, negative
Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -645,30 +645,11 @@
   }
 }
 
-namespace {
-struct LessClangTidyError {
-  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
-    const tooling::DiagnosticMessage &M1 = LHS.Message;
-    const tooling::DiagnosticMessage &M2 = RHS.Message;
-
-    return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
-           std::tie(M2.FilePath, M2.FileOffset, M2.Message);
-  }
-};
-struct EqualClangTidyError {
-  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
-    LessClangTidyError Less;
-    return !Less(LHS, RHS) && !Less(RHS, LHS);
-  }
-};
-} // end anonymous namespace
-
 std::vector<ClangTidyError> ClangTidyDiagnosticConsumer::take() {
   finalizeLastError();
 
-  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
-  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
-               Errors.end());
+  std::sort(Errors.begin(), Errors.end());
+  Errors.erase(std::unique(Errors.begin(), Errors.end()), Errors.end());
   if (RemoveIncompatibleErrors)
     removeIncompatibleErrors();
   return std::move(Errors);
Index: clang-tidy/ClangTidy.h
===================================================================
--- clang-tidy/ClangTidy.h
+++ clang-tidy/ClangTidy.h
@@ -15,6 +15,7 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Execution.h"
 #include "clang/Tooling/Refactoring.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
@@ -232,8 +233,9 @@
 /// as a JSON file in the specified directory.
 std::vector<ClangTidyError>
 runClangTidy(clang::tidy::ClangTidyContext &Context,
-             const tooling::CompilationDatabase &Compilations,
-             ArrayRef<std::string> InputFiles,
+             std::unique_ptr<tooling::ToolExecutor> Executor,
+             // const tooling::CompilationDatabase &Compilations,
+             //  ArrayRef<std::string> InputFiles,
              llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
              bool EnableCheckProfile = false,
              llvm::StringRef StoreCheckProfile = StringRef());
Index: clang-tidy/ClangTidy.cpp
===================================================================
--- clang-tidy/ClangTidy.cpp
+++ clang-tidy/ClangTidy.cpp
@@ -502,15 +502,13 @@
   return Factory.getCheckOptions();
 }
 
+// FIXME: support FS overlay.
+// FIXME: make ClangTidyContext thread safe.
 std::vector<ClangTidyError>
 runClangTidy(clang::tidy::ClangTidyContext &Context,
-             const CompilationDatabase &Compilations,
-             ArrayRef<std::string> InputFiles,
+             std::unique_ptr<tooling::ToolExecutor> Executor,
              llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
              bool EnableCheckProfile, llvm::StringRef StoreCheckProfile) {
-  ClangTool Tool(Compilations, InputFiles,
-                 std::make_shared<PCHContainerOperations>(), BaseFS);
-
   // Add extra arguments passed by the clang-tidy command-line.
   ArgumentsAdjuster PerFileExtraArgumentsInserter =
       [&Context](const CommandLineArguments &Args, StringRef Filename) {
@@ -545,19 +543,28 @@
         return AdjustedArgs;
       };
 
-  Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
-  Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
   Context.setEnableProfiling(EnableCheckProfile);
   Context.setProfileStoragePrefix(StoreCheckProfile);
 
-  ClangTidyDiagnosticConsumer DiagConsumer(Context);
-
-  Tool.setDiagnosticConsumer(&DiagConsumer);
-
   class ActionFactory : public FrontendActionFactory {
   public:
-    ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
-    FrontendAction *create() override { return new Action(&ConsumerFactory); }
+    ActionFactory(ClangTidyContext &Context,
+                  std::vector<tidy::ClangTidyError> &Results)
+        : ConsumerFactory(Context), Context(Context), Results(Results) {}
+
+    FrontendAction *create() override {
+      return new Action(&ConsumerFactory, Context,
+                        [&](std::vector<tidy::ClangTidyError> Errors) {
+                          Results.insert(Results.end(), Errors.begin(),
+                                         Errors.end());
+                        });
+    }
+
+    ~ActionFactory() {
+      // Deduplicate, and emit the final results.
+      std::sort(Results.begin(), Results.end());
+      Results.erase(std::unique(Results.begin(), Results.end()), Results.end());
+    }
 
     bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
                        FileManager *Files,
@@ -574,22 +581,53 @@
   private:
     class Action : public ASTFrontendAction {
     public:
-      Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
+      Action(ClangTidyASTConsumerFactory *Factory, ClangTidyContext &Context,
+             std::function<void(std::vector<tidy::ClangTidyError>)>
+                 CTErrorsCallback)
+          : Factory(Factory), CTDiagConsumer(Context),
+            CTErrorsCallback(CTErrorsCallback) {}
       std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
                                                      StringRef File) override {
         return Factory->CreateASTConsumer(Compiler, File);
       }
 
+      bool BeginInvocation(CompilerInstance &CI) override {
+        // Inject clang-tidy diagnostic consumer to store all diagnostics.
+        // FIXME: at this point, we miss diagnostics (e.g. unknown argument)
+        // that emitted during a CompilerInstance is created from compile
+        // command arguments.
+        CI.getDiagnostics().setClient(&CTDiagConsumer, false);
+        return true;
+      }
+
+      // Emits all collected diagnostics.
+      // We can't emit at EndSourceFileAction, because EndSourceFileAction will
+      // not be called when the compliation of the file should be aborted.
+      // clang-tidy wants all diagnostics, including errors.
+      ~Action() { CTErrorsCallback(CTDiagConsumer.take()); }
+
     private:
       ClangTidyASTConsumerFactory *Factory;
+      ClangTidyDiagnosticConsumer CTDiagConsumer;
+      std::function<void(std::vector<tidy::ClangTidyError>)> CTErrorsCallback;
     };
 
     ClangTidyASTConsumerFactory ConsumerFactory;
+    ClangTidyContext &Context;
+    std::vector<tidy::ClangTidyError> &Results;
   };
 
-  ActionFactory Factory(Context);
-  Tool.run(&Factory);
-  return DiagConsumer.take();
+  ArgumentsAdjuster CTArgumentAjuster =
+      combineAdjusters(std::move(PerFileExtraArgumentsInserter),
+                       std::move(PluginArgumentsRemover));
+  std::vector<tidy::ClangTidyError> Results;
+  auto Factory = llvm::make_unique<ActionFactory>(Context, Results);
+  auto Err = Executor->execute(std::move(Factory), CTArgumentAjuster);
+  if (Err) {
+    // Consume this error, as all errors will be reported by clang-tidy.
+    llvm::consumeError(std::move(Err));
+  }
+  return Results;
 }
 
 void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D54257: [clang-tidy] **... Haojian Wu via Phabricator via cfe-commits

Reply via email to