An alternative approach using a combined Action/ASTConsumer factory.

  This way it may be somewhat cleaner from the user's perspective.

Hi klimek,

http://llvm-reviews.chandlerc.com/D2481

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2481?vs=6327&id=6328#toc

Files:
  clang-tidy/ClangTidy.cpp
  clang-tidy/ClangTidy.h
  clang-tidy/ClangTidyDiagnosticConsumer.h
Index: clang-tidy/ClangTidy.cpp
===================================================================
--- clang-tidy/ClangTidy.cpp
+++ clang-tidy/ClangTidy.cpp
@@ -26,58 +26,30 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Frontend/ASTConsumers.h"
 #include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Rewrite/Frontend/FixItRewriter.h"
 #include "clang/Rewrite/Frontend/FrontendActions.h"
-#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
 #include "clang/Tooling/Tooling.h"
 #include "clang/Tooling/Refactoring.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Signals.h"
 #include <algorithm>
 #include <vector>
+// FIXME: Move AnalysisConsumer to include/clang/StaticAnalyzer/Frontend.
+#include "../../../lib/StaticAnalyzer/Frontend/AnalysisConsumer.h"
 
 using namespace clang::ast_matchers;
 using namespace clang::driver;
 using namespace clang::tooling;
 using namespace llvm;
 
 namespace clang {
 namespace tidy {
-namespace {
-
-/// \brief A combined ASTConsumer that forwards calls to two different
-/// consumers.
-///
-/// FIXME: This currently forwards just enough methods for the static analyzer
-/// and the \c MatchFinder's consumer to work; expand this to all methods of
-/// ASTConsumer and put it into a common location.
-class CombiningASTConsumer : public ASTConsumer {
-public:
-  CombiningASTConsumer(ASTConsumer *Consumer1, ASTConsumer *Consumer2)
-      : Consumer1(Consumer1), Consumer2(Consumer2) {}
 
-  virtual void Initialize(ASTContext &Context) LLVM_OVERRIDE {
-    Consumer1->Initialize(Context);
-    Consumer2->Initialize(Context);
-  }
-  virtual bool HandleTopLevelDecl(DeclGroupRef D) LLVM_OVERRIDE {
-    return Consumer1->HandleTopLevelDecl(D) && Consumer2->HandleTopLevelDecl(D);
-  }
-  virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) LLVM_OVERRIDE {
-    Consumer1->HandleTopLevelDeclInObjCContainer(D);
-    Consumer2->HandleTopLevelDeclInObjCContainer(D);
-  }
-  virtual void HandleTranslationUnit(ASTContext &Context) LLVM_OVERRIDE {
-    Consumer1->HandleTranslationUnit(Context);
-    Consumer2->HandleTranslationUnit(Context);
-  }
+namespace {
 
-private:
-  llvm::OwningPtr<ASTConsumer> Consumer1;
-  llvm::OwningPtr<ASTConsumer> Consumer2;
-};
+static const char *AnalyzerCheckerNamePrefix = "clang-analyzer-";
 
 static StringRef StaticAnalyzerCheckers[] = {
 #define GET_CHECKERS
@@ -88,142 +60,131 @@
 #undef GET_CHECKERS
 };
 
-static const char *AnalyzerCheckerNamePrefix = "clang-analyzer-";
-
 /// \brief Action that runs clang-tidy and static analyzer checks.
-///
-/// FIXME: Note that this inherits from \c AnalysisAction as this is the only
-/// way we can currently get to AnalysisAction::CreateASTConsumer. Ideally
-/// we'd want to build a more generic way to use \c FrontendAction based
-/// checkers in clang-tidy, but that needs some preparation work first.
-class ClangTidyAction : public ento::AnalysisAction {
+class ClangTidyAction : public ASTFrontendAction {
 public:
-  ClangTidyAction(ChecksFilter &Filter,
-                  SmallVectorImpl<ClangTidyCheck *> &Checks,
-                  ClangTidyContext &Context, MatchFinder &Finder)
-      : Filter(Filter), Checks(Checks), Context(Context), Finder(Finder) {}
-
-  typedef std::vector<std::pair<std::string, bool> > CheckersList;
-  void fillCheckersControlList(CheckersList &List) {
-    ArrayRef<StringRef> Checkers(StaticAnalyzerCheckers);
+  ClangTidyAction(ClangTidyActionFactory *Factory) : Factory(Factory) {}
 
-    bool AnalyzerChecksEnabled = false;
-    for (unsigned i = 0; i < Checkers.size(); ++i) {
-      std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
-      AnalyzerChecksEnabled |=
-          Filter.IsCheckEnabled(Checker) && !Checkers[i].startswith("debug");
-    }
-
-    if (!AnalyzerChecksEnabled)
-      return;
-
-    // Run our regex against all possible static analyzer checkers.
-    // Note that debug checkers print values / run programs to visualize the CFG
-    // and are thus not applicable to clang-tidy in general.
-    // Always add all core checkers if any other static analyzer checks are
-    // enabled. This is currently necessary, as other path sensitive checks rely
-    // on the core checkers.
-    for (unsigned i = 0; i < Checkers.size(); ++i) {
-      std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
-
-      if (Checkers[i].startswith("core") ||
-          (!Checkers[i].startswith("debug") && Filter.IsCheckEnabled(Checker)))
-        List.push_back(std::make_pair(Checkers[i], true));
-    }
-  }
-
-private:
   clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
                                         StringRef File) LLVM_OVERRIDE {
-    AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
-    fillCheckersControlList(Options->CheckersControlList);
-    Options->AnalysisStoreOpt = RegionStoreModel;
-    Options->AnalysisDiagOpt = PD_TEXT;
-    Options->AnalyzeNestedBlocks = true;
-    Options->eagerlyAssumeBinOpBifurcation = true;
-    return new CombiningASTConsumer(
-        Finder.newASTConsumer(),
-        ento::AnalysisAction::CreateASTConsumer(Compiler, File));
+    return Factory->CreateASTConsumer(Compiler, File);
   }
 
-  virtual bool BeginSourceFileAction(CompilerInstance &Compiler,
-                                     llvm::StringRef Filename) LLVM_OVERRIDE {
-    if (!ento::AnalysisAction::BeginSourceFileAction(Compiler, Filename))
-      return false;
-    Context.setSourceManager(&Compiler.getSourceManager());
-    for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
-                                                     E = Checks.end();
-         I != E; ++I)
-      (*I)->registerPPCallbacks(Compiler);
-    return true;
-  }
-
-  ChecksFilter &Filter;
-  SmallVectorImpl<ClangTidyCheck *> &Checks;
-  ClangTidyContext &Context;
-  MatchFinder &Finder;
+private:
+  ClangTidyActionFactory *Factory;
 };
 
-class ClangTidyActionFactory : public FrontendActionFactory {
-public:
-  ClangTidyActionFactory(StringRef EnableChecksRegex,
-                         StringRef DisableChecksRegex,
-                         ClangTidyContext &Context)
-      : Filter(EnableChecksRegex, DisableChecksRegex), Context(Context) {
-    for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
-                                           E = ClangTidyModuleRegistry::end();
-         I != E; ++I) {
-      OwningPtr<ClangTidyModule> Module(I->instantiate());
-      Module->addCheckFactories(CheckFactories);
-    }
+} // namespace
 
-    SmallVector<ClangTidyCheck *, 16> Checks;
-    CheckFactories.createChecks(Filter, Checks);
+ClangTidyActionFactory::ClangTidyActionFactory(StringRef EnableChecksRegex,
+                                               StringRef DisableChecksRegex,
+                                               ClangTidyContext &Context)
+    : Filter(EnableChecksRegex, DisableChecksRegex), Context(Context),
+      CheckFactories(new ClangTidyCheckFactories) {
+  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+                                         E = ClangTidyModuleRegistry::end();
+       I != E; ++I) {
+    OwningPtr<ClangTidyModule> Module(I->instantiate());
+    Module->addCheckFactories(*CheckFactories);
+  }
 
-    for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
-                                                     E = Checks.end();
-         I != E; ++I) {
-      (*I)->setContext(&Context);
-      (*I)->registerMatchers(&Finder);
-    }
+  CheckFactories->createChecks(Filter, Checks);
+
+  for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
+                                                   E = Checks.end();
+       I != E; ++I) {
+    (*I)->setContext(&Context);
+    (*I)->registerMatchers(&Finder);
   }
+}
 
-  virtual FrontendAction *create() {
-    return new ClangTidyAction(Filter, Checks, Context, Finder);
+ClangTidyActionFactory::~ClangTidyActionFactory() {
+  for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
+                                                   E = Checks.end();
+       I != E; ++I)
+    delete *I;
+}
+
+clang::ASTConsumer *
+ClangTidyActionFactory::CreateASTConsumer(clang::CompilerInstance &Compiler,
+                                          StringRef File) {
+  // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
+  // modify Compiler.
+  Context.setSourceManager(&Compiler.getSourceManager());
+  for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
+                                                   E = Checks.end();
+       I != E; ++I)
+    (*I)->registerPPCallbacks(Compiler);
+
+  AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
+  Options->CheckersControlList = getCheckersControlList();
+  Options->AnalysisStoreOpt = RegionStoreModel;
+  Options->AnalysisDiagOpt = PD_TEXT;
+  Options->AnalyzeNestedBlocks = true;
+  Options->eagerlyAssumeBinOpBifurcation = true;
+  ASTConsumer *Consumers[] = {
+    Finder.newASTConsumer(),
+    ento::CreateAnalysisConsumer(Compiler.getPreprocessor(),
+                                 Compiler.getFrontendOpts().OutputFile, Options,
+                                 Compiler.getFrontendOpts().Plugins)
+  };
+  return new MultiplexConsumer(Consumers);
+}
+
+FrontendAction *ClangTidyActionFactory::create() {
+  return new ClangTidyAction(this);
+}
+
+std::vector<std::string> ClangTidyActionFactory::getCheckNames() {
+  std::vector<std::string> CheckNames;
+  for (ClangTidyCheckFactories::FactoryMap::const_iterator
+           I = CheckFactories->begin(),
+           E = CheckFactories->end();
+       I != E; ++I) {
+    if (Filter.IsCheckEnabled(I->first))
+      CheckNames.push_back(I->first);
   }
 
-  std::vector<std::string> getCheckNames() {
-    std::vector<std::string> CheckNames;
-    for (ClangTidyCheckFactories::FactoryMap::const_iterator
-             I = CheckFactories.begin(),
-             E = CheckFactories.end();
-         I != E; ++I) {
-      if (Filter.IsCheckEnabled(I->first))
-        CheckNames.push_back(I->first);
-    }
+  CheckersList AnalyzerChecks = getCheckersControlList();
+  for (CheckersList::const_iterator I = AnalyzerChecks.begin(),
+                                    E = AnalyzerChecks.end();
+       I != E; ++I)
+    CheckNames.push_back(AnalyzerCheckerNamePrefix + I->first);
+
+  std::sort(CheckNames.begin(), CheckNames.end());
+  return CheckNames;
+}
 
-    ClangTidyAction Action(Filter, Checks, Context, Finder);
-    ClangTidyAction::CheckersList AnalyzerChecks;
-    Action.fillCheckersControlList(AnalyzerChecks);
-    for (ClangTidyAction::CheckersList::const_iterator
-             I = AnalyzerChecks.begin(),
-             E = AnalyzerChecks.end();
-         I != E; ++I)
-      CheckNames.push_back(AnalyzerCheckerNamePrefix + I->first);
+ClangTidyActionFactory::CheckersList
+ClangTidyActionFactory::getCheckersControlList() {
+  CheckersList List;
+  ArrayRef<StringRef> Checkers(StaticAnalyzerCheckers);
 
-    std::sort(CheckNames.begin(), CheckNames.end());
-    return CheckNames;
+  bool AnalyzerChecksEnabled = false;
+  for (unsigned i = 0; i < Checkers.size(); ++i) {
+    std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
+    AnalyzerChecksEnabled |=
+        Filter.IsCheckEnabled(Checker) && !Checkers[i].startswith("debug");
   }
 
-private:
-  ChecksFilter Filter;
-  SmallVector<ClangTidyCheck *, 8> Checks;
-  ClangTidyContext &Context;
-  MatchFinder Finder;
-  ClangTidyCheckFactories CheckFactories;
-};
+  if (AnalyzerChecksEnabled) {
+    // Run our regex against all possible static analyzer checkers.  Note that
+    // debug checkers print values / run programs to visualize the CFG and are
+    // thus not applicable to clang-tidy in general.
+    //
+    // Always add all core checkers if any other static analyzer checks are
+    // enabled. This is currently necessary, as other path sensitive checks
+    // rely on the core checkers.
+    for (unsigned i = 0; i < Checkers.size(); ++i) {
+      std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
 
-} // namespace
+      if (Checkers[i].startswith("core") ||
+          (!Checkers[i].startswith("debug") && Filter.IsCheckEnabled(Checker)))
+        List.push_back(std::make_pair(Checkers[i], true));
+    }
+  }
+  return List;
+}
 
 ChecksFilter::ChecksFilter(StringRef EnableChecksRegex,
                            StringRef DisableChecksRegex)
@@ -270,14 +231,6 @@
   check(Result);
 }
 
-FrontendActionFactory *
-createClangTidyActionFactory(StringRef EnableChecksRegex,
-                             StringRef DisableChecksRegex,
-                             ClangTidyContext &Context) {
-  return new ClangTidyActionFactory(EnableChecksRegex, DisableChecksRegex,
-                                    Context);
-}
-
 std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
                                        StringRef DisableChecksRegex) {
   SmallVector<ClangTidyError, 8> Errors;
@@ -298,8 +251,8 @@
   ClangTidyDiagnosticConsumer DiagConsumer(Context);
 
   Tool.setDiagnosticConsumer(&DiagConsumer);
-  Tool.run(createClangTidyActionFactory(EnableChecksRegex, DisableChecksRegex,
-                                        Context));
+  Tool.run(new ClangTidyActionFactory(EnableChecksRegex, DisableChecksRegex,
+                                      Context));
 }
 
 static void reportDiagnostic(const ClangTidyMessage &Message,
Index: clang-tidy/ClangTidy.h
===================================================================
--- clang-tidy/ClangTidy.h
+++ clang-tidy/ClangTidy.h
@@ -13,6 +13,7 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FrontendActions.h"
 #include "clang/Tooling/Refactoring.h"
 #include "ClangTidyDiagnosticConsumer.h"
 
@@ -96,17 +97,40 @@
   llvm::Regex DisableChecks;
 };
 
+class ClangTidyCheckFactories;
+
+class ClangTidyActionFactory : public tooling::FrontendActionFactory {
+public:
+  ClangTidyActionFactory(StringRef EnableChecksRegex,
+                         StringRef DisableChecksRegex,
+                         ClangTidyContext &Context);
+  ~ClangTidyActionFactory();
+
+  /// \brief Returns an action that runs the specified clang-tidy checks.
+  virtual FrontendAction *create() LLVM_OVERRIDE;
+
+  clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler,
+                                        StringRef File);
+
+  /// \brief Get the list of enabled checks.
+  std::vector<std::string> getCheckNames();
+
+private:
+  typedef std::vector<std::pair<std::string, bool> > CheckersList;
+  CheckersList getCheckersControlList();
+
+  ChecksFilter Filter;
+  SmallVector<ClangTidyCheck *, 8> Checks;
+  ClangTidyContext &Context;
+  ast_matchers::MatchFinder Finder;
+  OwningPtr<ClangTidyCheckFactories> CheckFactories;
+};
+
 /// \brief Fills the list of check names that are enabled when the provided
 /// filters are applied.
 std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
                                        StringRef DisableChecksRegex);
 
-/// \brief Returns an action factory that runs the specified clang-tidy checks.
-tooling::FrontendActionFactory *
-createClangTidyActionFactory(StringRef EnableChecksRegex,
-                             StringRef DisableChecksRegex,
-                             ClangTidyContext &Context);
-
 /// \brief Run a set of clang-tidy checks on a set of files.
 void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
                   const tooling::CompilationDatabase &Compilations,
Index: clang-tidy/ClangTidyDiagnosticConsumer.h
===================================================================
--- clang-tidy/ClangTidyDiagnosticConsumer.h
+++ clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -65,7 +65,8 @@
 /// \endcode
 class ClangTidyContext {
 public:
-  ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors) : Errors(Errors) {}
+  ClangTidyContext(SmallVectorImpl<ClangTidyError> *Errors)
+      : Errors(Errors), DiagEngine(0) {}
 
   /// \brief Report any errors detected using this method.
   ///
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to