NoQ created this revision.
NoQ added reviewers: vsavchenko, xazax.hun, martong, Szelethus, 
baloghadamsoftware, Charusso, steakhal, gribozavr2, alexfh, aaron.ballman.
Herald added subscribers: dang, ASDenysPetrov, phosek, dkrupp, donat.nagy, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, mgorny.
Herald added a reviewer: jansvoboda11.
NoQ requested review of this revision.
Herald added a reviewer: jdoerfert.
Herald added a subscriber: sstefan1.

This patch allows building a clang binary that has clang-tidy checks embedded 
into the static analyzer. I discussed these plans briefly in 
https://lists.llvm.org/pipermail/cfe-dev/2020-October/067002.html.

WIP because i haven't handled any build-time settings yet. This new facility 
//will be// under a CMake flag and most likely off by default (unless everybody 
suddenly loves this option, but even then, I still have to turn it into an 
option). I'll make sure to implement (and hopefully test) a reasonable behavior 
for all various combinations of CMake flags (eg., clang-tidy enabled/disabled, 
static analyzer enabled/disabled, static-analyzer-into-clang-tidy integration 
enabled/disabled, etc.).

This patch introduces a frontend flag `-analyzer-tidy-checker=...` that accepts 
clang-tidy checker strings (eg., 
`-analyzer-tidy-checker=bugprone-*,-cert-*,cert-env33-c`). As long as at least 
one such flag is supplied, `ClangTidyContext` is instantiated with the given 
checker list and a clang-tidy AST consumer gets multiplexed with 
`AnalysisConsumer` so that to run clang-tidy checkers. Diagnostics from these 
checks are pumped into a `PathDiagnosticConverterDiagnosticConsumer` (D94476 
<https://reviews.llvm.org/D94476>) and displayed similarly to static analyzer 
diagnostics, eg. as HTML or Plist reports:

F15183921: Screen Shot 2021-01-25 at 2.59.30 PM.png 
<https://reviews.llvm.org/F15183921>

This patch suggests enabling a few clang-tidy checks by default in Clang Driver 
(under the --analyze flag, obviously, and the new reverse integration should be 
enabled). Requirements for enabling a clang-tidy check by default would be 
similar to requirements of enabling a static analyzer check by default (finds 
real bugs by design, understandable diagnostics, etc.). Additionally, 
on-by-default clang-tidy checks should have static analyzer bug categories 
assigned to them (eg., "Logic error", "Memory error", etc.) in the diagnostic 
converter (a relatively convenient way to do so is provided).

If bug categories aren't assigned, the default bug category for clang-tidy 
check `x-y-z` is `Clang-Tidy [x]` and the default bug type is `Clang-Tidy 
[x-y-z]`. This looks pretty ok in scan-build's index.html:

F15183900: Screen Shot 2021-01-25 at 2.56.47 PM.png 
<https://reviews.llvm.org/F15183900>

That said, we should probably provide an option to disable bug category 
conversion and use it whenever an arbitrary clang-tidy config is expected. As 
usual, we're optimizing for a reasonable default rather than for 
configurability.

I've considered a different approach to enabling/disabling clang-tidy checks 
(but didn't implement it in this patch) that's more static analyzer-like: 
`-analyzer-checker tidy.bugprone.assert-side-effect` etc. Here we treat `x` in 
`x-y-z` as a static analyzer package, hence the dot. This approach is less 
flexible because it doesn't allow arbitrary wildcards, so we still have to have 
the current approach but we should probably implement both. One thing to think 
about here would be to figure out whether each individual off-by-default 
clang-tidy check is treated as "alpha" ("we're not supporting it, don't use 
it") or opt-in ("we're supporting it but it's still of by default"). Probably 
we could additionally separate them into `alpha.tidy.*` and `optin.tidy.*`.

Clang-tidy check-specific options are not implemented yet. Again, we could 
provide a separate frontend flag, or we could interface them through 
`AnalyzerOptions` like this: `-analyzer-config 
tidy.bugprone.assert-side-effect:AssertMacros=assert,Assert,ASSERT`. And, 
again, we should probably do both.


https://reviews.llvm.org/D95403

Files:
  clang/include/clang/Driver/Options.td
  clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
  clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt

Index: clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Frontend/CMakeLists.txt
@@ -1,3 +1,7 @@
+configure_file(
+  ${CMAKE_SOURCE_DIR}/../clang-tools-extra/clang-tidy/clang-tidy-config.h.cmake
+  ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
+
 include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../Checkers )
 
 set(LLVM_LINK_COMPONENTS
@@ -23,6 +27,31 @@
   clangLex
   clangStaticAnalyzerCheckers
   clangStaticAnalyzerCore
+  clangTidy
+  clangTidyAbseilModule
+  clangTidyAndroidModule
+  clangTidyAlteraModule
+  clangTidyBoostModule
+  clangTidyBugproneModule
+  clangTidyCERTModule
+  clangTidyConcurrencyModule
+  clangTidyCppCoreGuidelinesModule
+  clangTidyDarwinModule
+  clangTidyFuchsiaModule
+  clangTidyGoogleModule
+  clangTidyHICPPModule
+  clangTidyLinuxKernelModule
+  clangTidyLLVMModule
+  clangTidyLLVMLibcModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyMPIModule
+  clangTidyObjCModule
+  clangTidyOpenMPModule
+  clangTidyPerformanceModule
+  clangTidyPortabilityModule
+  clangTidyReadabilityModule
+  clangTidyZirconModule
 
   DEPENDS
   omp_gen
Index: clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -16,15 +16,20 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Analysis/Analyses/LiveVariables.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/CallGraph.h"
 #include "clang/Analysis/CodeInjector.h"
 #include "clang/Analysis/PathDiagnostic.h"
 #include "clang/Analysis/PathDiagnosticConsumers.h"
+#include "clang/Analysis/PathDiagnosticConverterDiagnosticConsumer.h"
+#include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/CrossTU/CrossTranslationUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
@@ -38,21 +43,35 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
+#include "llvm/Support/Registry.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
 #include <memory>
 #include <queue>
 #include <utility>
 
+#include "../../clang-tools-extra/clang-tidy/ClangTidyCheck.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyForceLinker.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyModule.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyModuleRegistry.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyOptions.h"
+#include "../../clang-tools-extra/clang-tidy/ClangTidyProfiling.h"
+
+LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
+
 using namespace clang;
 using namespace ento;
+using namespace tidy;
+using namespace llvm;
 
 #define DEBUG_TYPE "AnalysisConsumer"
 
 STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
 STATISTIC(NumFunctionsAnalyzed,
-                      "The # of functions and blocks analyzed (as top level "
-                      "with inlining turned on).");
+          "The # of functions and blocks analyzed (as top level "
+          "with inlining turned on).");
 STATISTIC(NumBlocksInAnalyzedFunctions,
                       "The # of basic blocks in the analyzed functions.");
 STATISTIC(NumVisitedBlocksInAnalyzedFunctions,
@@ -66,7 +85,7 @@
 
 namespace {
 
-class AnalysisConsumer : public AnalysisASTConsumer,
+class AnalysisConsumer : public ASTConsumer,
                          public RecursiveASTVisitor<AnalysisConsumer> {
   enum {
     AM_None = 0,
@@ -83,7 +102,7 @@
   std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns;
 
 public:
-  ASTContext *Ctx;
+  ASTContext &Ctx;
   Preprocessor &PP;
   const std::string OutDir;
   AnalyzerOptionsRef Opts;
@@ -117,10 +136,96 @@
   /// translation unit.
   FunctionSummariesTy FunctionSummaries;
 
+private: // Clang-tidy integration.
+  ClangTidyGlobalOptions ClangTidyGlobalOpts{};
+  std::unique_ptr<ClangTidyContext> ClangTidyCtx;
+  std::unique_ptr<ClangTidyProfiling> ClangTidyProfile;
+
+  // Provide bug types and categories for clang-tidy, also convert
+  // check names to checker names.
+  struct OwningBugTypeInfo {
+    const std::string CheckName;
+    const std::string BugType;
+    const std::string BugCategory;
+
+    PathDiagnosticConverterDiagnosticConsumer::BugTypeInfo toBugTypeInfo() {
+      // Take a non-owning reference to the same data. Very fast.
+      return PathDiagnosticConverterDiagnosticConsumer::BugTypeInfo{
+          CheckName, BugType, BugCategory};
+    }
+
+    OwningBugTypeInfo(std::string &&CheckName, std::string &&BugType,
+                      std::string &&BugCategory)
+        : CheckName(std::move(CheckName)), BugType(std::move(BugType)),
+          BugCategory(std::move(BugCategory)) {}
+
+    // Move-only. We don't want the cache to copy strings and then delete
+    // original strings that we have references to.
+    OwningBugTypeInfo(const OwningBugTypeInfo &) = delete;
+    OwningBugTypeInfo(OwningBugTypeInfo &&) = default;
+    OwningBugTypeInfo &operator=(const OwningBugTypeInfo &) = delete;
+  };
+
+  std::map<unsigned, OwningBugTypeInfo> BugTypeCache;
+
+  // Fold clang-tidy check categories into common static analyzer categories.
+  std::map<std::string, std::string> OverriddenCategories{
+      {"bugprone-assert-side-effect", categories::LogicError},
+      {"bugprone-infinite-loop", categories::LogicError},
+      {"bugprone-move-forwarding-reference", categories::CXXMoveSemantics},
+  };
+
+  PathDiagnosticConverterDiagnosticConsumer::BugTypeInfo
+  defaultBugTypeInfoProvider(const Diagnostic &Info) {
+    unsigned ID = Info.getID();
+    auto I = BugTypeCache.find(ID);
+    if (I != BugTypeCache.end())
+      return I->second.toBugTypeInfo();
+
+    std::string CheckName = ClangTidyCtx->getCheckName(Info.getID());
+    assert(!CheckName.empty() &&
+           "No check name available for clang-tidy diagnostic");
+    // Eg., Clang-Tidy [bugprone-assert-side-effect].
+    std::string Bugtype = "Clang-Tidy [" + CheckName + "]";
+
+    std::string Category;
+    auto J = OverriddenCategories.find(CheckName);
+    if (J != OverriddenCategories.end()) {
+      Category = J->second;
+    } else {
+      auto I = CheckName.find('-');
+      assert(I != std::string::npos && "Clang-tidy check name isn't dashed");
+      std::string CheckCategory =
+          (I != std::string::npos) ? CheckName.substr(0, I) : CheckName;
+      // Eg., Clang-Tidy [bugprone]
+      Category = "Clang-Tidy [" + CheckCategory + "]";
+    }
+
+    auto IF = BugTypeCache.insert(std::make_pair(
+        ID, OwningBugTypeInfo(std::move(CheckName), std::move(Bugtype),
+                              std::move(Category))));
+    return IF.first->second.toBugTypeInfo();
+  }
+
+  PathDiagnosticConverterDiagnosticConsumer ClangTidyDiagConsumer{
+      PathConsumers,
+      [this](const Diagnostic &Info) {
+        return defaultBugTypeInfoProvider(Info);
+      },
+      /*ShouldDisplayNotesAsEvents=*/true,
+      /*ShouldTrimCheckerName=*/true};
+  DiagnosticsEngine ClangTidyDiagEngine{
+      new DiagnosticIDs, new DiagnosticOptions, &ClangTidyDiagConsumer,
+      /*ShouldOwnClient=*/false};
+  ClangTidyCheckFactories ClangTidyChkFactories{};
+  std::unique_ptr<ast_matchers::MatchFinder> ClangTidyMatchFinder;
+  std::vector<std::unique_ptr<ClangTidyCheck>> ClangTidyChecks{};
+
+public:
   AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
                    AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
                    CodeInjector *injector)
-      : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
+      : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(CI.getASTContext()),
         PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
         Plugins(plugins), Injector(injector), CTU(CI) {
     DigestAnalyzerOptions();
@@ -207,11 +312,10 @@
   }
 
   void Initialize(ASTContext &Context) override {
-    Ctx = &Context;
-    checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins,
+    checkerMgr = std::make_unique<CheckerManager>(Ctx, *Opts, PP, Plugins,
                                                   CheckerRegistrationFns);
 
-    Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers,
+    Mgr = std::make_unique<AnalysisManager>(Ctx, PP, PathConsumers,
                                             CreateStoreMgr, CreateConstraintMgr,
                                             checkerMgr.get(), *Opts, Injector);
   }
@@ -268,7 +372,7 @@
       return true;
 
     if (VD->hasExternalStorage() || VD->isStaticDataMember()) {
-      if (!cross_tu::containsConst(VD, *Ctx))
+      if (!cross_tu::containsConst(VD, Ctx))
         return true;
     } else {
       // Cannot be initialized in another TU.
@@ -327,12 +431,62 @@
     return true;
   }
 
-  void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
-    PathConsumers.push_back(Consumer);
-  }
+  friend class AnalysisMultiplexConsumer;
 
-  void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override {
-    CheckerRegistrationFns.push_back(std::move(Fn));
+  std::unique_ptr<ASTConsumer> createClangTidyConsumer() {
+    SmallString<128> Buf;
+    raw_svector_ostream ClangTidyConfig(Buf);
+    ClangTidyConfig << "Checks: '-*";
+    if (Opts->TidyChecks.size() == 0) {
+      // TODO: Detect if no checks are enabled even when some flags are present.
+      return nullptr;
+    }
+    for (const std::string &TidyCheck: Opts->TidyChecks) {
+      // Should we try to prevent config injections?
+      ClangTidyConfig << "," << TidyCheck;
+    }
+    ClangTidyConfig << "'\n";
+    // raw_svector_ostream doesn't need flush.
+    SmallVectorMemoryBuffer ClangTidyConfigBuffer{std::move(Buf)};
+    ClangTidyCtx = std::make_unique<ClangTidyContext>(
+        std::make_unique<DefaultOptionsProvider>(
+            ClangTidyGlobalOpts, *parseConfiguration(ClangTidyConfigBuffer)));
+
+    ClangTidyCtx->setDiagnosticsEngine(&ClangTidyDiagEngine);
+
+    SourceManager &SM = Ctx.getSourceManager();
+    ClangTidyCtx->setSourceManager(&SM);
+    ClangTidyCtx->setCurrentFile(
+        SM.getFileEntryForID(SM.getMainFileID())->getName());
+    ClangTidyCtx->setASTContext(&Ctx);
+
+    for (ClangTidyModuleRegistry::entry E :
+         ClangTidyModuleRegistry::entries()) {
+      std::unique_ptr<ClangTidyModule> Module = E.instantiate();
+      Module->addCheckFactories(ClangTidyChkFactories);
+    }
+    ClangTidyChecks = ClangTidyChkFactories.createChecks(&*ClangTidyCtx);
+
+    ast_matchers::MatchFinder::MatchFinderOptions MatchFinderOpts{};
+    if (Opts->PrintStats) {
+      ClangTidyCtx->setEnableProfiling(true);
+      ClangTidyCtx->setProfileStoragePrefix("");
+      ClangTidyProfile = std::make_unique<ClangTidyProfiling>(
+          ClangTidyCtx->getProfileStorageParams());
+      MatchFinderOpts.CheckProfiling.emplace(ClangTidyProfile->Records);
+    }
+
+    ClangTidyMatchFinder = std::make_unique<ast_matchers::MatchFinder>(
+        MatchFinderOpts);
+
+    for (auto &Check : ClangTidyChecks) {
+      if (!Check->isLanguageVersionSupported(Ctx.getLangOpts()))
+        continue;
+      Check->registerMatchers(&*ClangTidyMatchFinder);
+      Check->registerPPCallbacks(SM, &PP, &PP);
+    }
+
+    return ClangTidyMatchFinder->newASTConsumer();
   }
 
 private:
@@ -346,8 +500,39 @@
   /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.
   void reportAnalyzerProgress(StringRef S);
 }; // namespace
-} // end anonymous namespace
 
+class AnalysisMultiplexConsumer : public AnalysisASTConsumer {
+  AnalysisConsumer *Impl;
+
+  std::vector<std::unique_ptr<ASTConsumer>>
+  hijackImplAndMultiplex(std::unique_ptr<AnalysisConsumer> Consumer1,
+                         std::unique_ptr<ASTConsumer> Consumer2) {
+    Impl = Consumer1.get();
+    std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+    if (Consumer2)
+      Consumers.push_back(std::move(Consumer2));
+    Consumers.push_back(std::move(Consumer1));
+    return Consumers;
+  }
+
+public:
+  AnalysisMultiplexConsumer(std::unique_ptr<AnalysisConsumer> Consumer1,
+                            std::unique_ptr<ASTConsumer> Consumer2)
+      : AnalysisASTConsumer(hijackImplAndMultiplex(std::move(Consumer1),
+                                                   std::move(Consumer2))) {
+    assert(Impl);
+  }
+
+  void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
+    Impl->PathConsumers.push_back(Consumer);
+  }
+
+  void
+  AddCheckerRegistrationFn(std::function<void(CheckerRegistry &)> Fn) override {
+    Impl->CheckerRegistrationFns.push_back(std::move(Fn));
+  }
+};
+} // end anonymous namespace
 
 //===----------------------------------------------------------------------===//
 // AnalysisConsumer implementation.
@@ -553,6 +738,9 @@
       (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
         NumBlocksInAnalyzedFunctions;
 
+  // No more notes are coming in for the last warning.
+  ClangTidyDiagConsumer.finish();
+
   // Explicitly destroy the PathDiagnosticConsumer.  This will flush its output.
   // FIXME: This should be replaced with something that doesn't rely on
   // side-effects in PathDiagnosticConsumer's destructor. This is required when
@@ -568,7 +756,7 @@
     OS << FD->getQualifiedNameAsString();
 
     // In C++, there are overloads.
-    if (Ctx->getLangOpts().CPlusPlus) {
+    if (Ctx.getLangOpts().CPlusPlus) {
       OS << '(';
       for (const auto &P : FD->parameters()) {
         if (P != *FD->param_begin())
@@ -579,7 +767,7 @@
     }
 
   } else if (isa<BlockDecl>(D)) {
-    PresumedLoc Loc = Ctx->getSourceManager().getPresumedLoc(D->getLocation());
+    PresumedLoc Loc = Ctx.getSourceManager().getPresumedLoc(D->getLocation());
 
     if (Loc.isValid()) {
       OS << "block (line: " << Loc.getLine() << ", col: " << Loc.getColumn()
@@ -624,7 +812,7 @@
   // - Main source file: run both path-sensitive and non-path-sensitive checks.
   // - Header files: run non-path-sensitive checks only.
   // - System headers: don't run any checks.
-  SourceManager &SM = Ctx->getSourceManager();
+  SourceManager &SM = Ctx.getSourceManager();
   const Stmt *Body = D->getBody();
   SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation();
   SL = SM.getExpansionLoc(SL);
@@ -730,8 +918,14 @@
   AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
   bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
 
-  return std::make_unique<AnalysisConsumer>(
+  auto StaticAnalyzerConsumer = std::make_unique<AnalysisConsumer>(
       CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
       CI.getFrontendOpts().Plugins,
       hasModelPath ? new ModelInjector(CI) : nullptr);
+
+  std::unique_ptr<ASTConsumer> ClangTidyConsumer =
+      StaticAnalyzerConsumer->createClangTidyConsumer();
+
+  return std::make_unique<AnalysisMultiplexConsumer>(
+      std::move(StaticAnalyzerConsumer), std::move(ClangTidyConsumer));
 }
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -634,6 +634,12 @@
                                             IsEnabled);
   }
 
+  Opts.TidyChecks.clear();
+  for (const Arg *A : Args.filtered(OPT_analyzer_tidy_checker)) {
+    StringRef CheckList = A->getValue();
+    Opts.TidyChecks.emplace_back(CheckList);
+  }
+
   // Go through the analyzer configuration options.
   for (const auto *A : Args.filtered(OPT_analyzer_config)) {
 
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -2960,6 +2960,12 @@
     // Default nullability checks.
     CmdArgs.push_back("-analyzer-checker=nullability.NullPassedToNonnull");
     CmdArgs.push_back("-analyzer-checker=nullability.NullReturnedFromNonnull");
+
+    // Default clang-tidy checks.
+    CmdArgs.push_back("-analyzer-tidy-checker=bugprone-assert-side-effect");
+    CmdArgs.push_back("-analyzer-tidy-checker=bugprone-infinite-loop");
+    CmdArgs.push_back(
+        "-analyzer-tidy-checker=bugprone-move-forwarding-reference");
   }
 
   // Set the output format. The default is plist, for (lame) historical reasons.
Index: clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
+++ clang/include/clang/StaticAnalyzer/Frontend/AnalysisConsumer.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/ASTConsumer.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Frontend/MultiplexConsumer.h"
 #include <functional>
 #include <memory>
 
@@ -31,8 +32,11 @@
 class CheckerManager;
 class CheckerRegistry;
 
-class AnalysisASTConsumer : public ASTConsumer {
+class AnalysisASTConsumer : public MultiplexConsumer {
 public:
+  AnalysisASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> V)
+      : MultiplexConsumer(std::move(V)) {}
+
   virtual void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) = 0;
 
   /// This method allows registering statically linked custom checkers that are
Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -202,6 +202,9 @@
   /// Vector of checker/package names which will not emit warnings.
   std::vector<std::string> SilencedCheckersAndPackages;
 
+  /// Vector of clang-tidy check flags.
+  std::vector<std::string> TidyChecks;
+
   /// A key-value table of use-specified configuration values.
   // TODO: This shouldn't be public.
   ConfigTable Config;
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -4287,6 +4287,11 @@
   HelpText<"Disable all static analyzer checks">,
   MarshallingInfoFlag<"AnalyzerOpts->DisableAllCheckers">;
 
+def analyzer_tidy_checker : Separate<["-"], "analyzer-tidy-checker">,
+  HelpText<"Add clang-tidy check string (enable, -disable, comma-separated)">;
+def analyzer_tidy_checker_EQ : Joined<["-"], "analyzer-tidy-checker=">,
+  Alias<analyzer_tidy_checker>;
+
 def analyzer_checker_help : Flag<["-"], "analyzer-checker-help">,
   HelpText<"Display the list of analyzer checkers that are available">,
   MarshallingInfoFlag<"AnalyzerOpts->ShowCheckerHelp">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to