brintos

brintos / llvm-project-archived public Read only

0
0
Text · 28.9 KiB · 4af328c Raw
741 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9///  \file This file implements a clang-tidy tool.10///11///  This tool uses the Clang Tooling infrastructure, see12///    https://clang.llvm.org/docs/HowToSetupToolingForLLVM.html13///  for details on setting it up with LLVM source tree.14///15//===----------------------------------------------------------------------===//16 17#include "ClangTidy.h"18#include "ClangTidyCheck.h"19#include "ClangTidyDiagnosticConsumer.h"20#include "ClangTidyModuleRegistry.h"21#include "ClangTidyProfiling.h"22#include "ExpandModularHeadersPPCallbacks.h"23#include "clang-tidy-config.h"24#include "clang/AST/ASTConsumer.h"25#include "clang/ASTMatchers/ASTMatchFinder.h"26#include "clang/Basic/DiagnosticFrontend.h"27#include "clang/Format/Format.h"28#include "clang/Frontend/ASTConsumers.h"29#include "clang/Frontend/CompilerInstance.h"30#include "clang/Frontend/MultiplexConsumer.h"31#include "clang/Frontend/TextDiagnosticPrinter.h"32#include "clang/Lex/Preprocessor.h"33#include "clang/Lex/PreprocessorOptions.h"34#include "clang/Rewrite/Frontend/FixItRewriter.h"35#include "clang/Tooling/Core/Diagnostic.h"36#include "clang/Tooling/DiagnosticsYaml.h"37#include "clang/Tooling/Refactoring.h"38#include "clang/Tooling/Tooling.h"39#include "llvm/Support/Process.h"40#include <utility>41 42#if CLANG_TIDY_ENABLE_STATIC_ANALYZER43#include "clang/Analysis/PathDiagnostic.h"44#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"45#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER46 47using namespace clang::ast_matchers;48using namespace clang::driver;49using namespace clang::tooling;50using namespace llvm;51 52LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)53 54namespace clang::tidy {55 56#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS57namespace custom {58void (*RegisterCustomChecks)(const ClangTidyOptions &O,59                             ClangTidyCheckFactories &Factories) = nullptr;60} // namespace custom61#endif62 63namespace {64#if CLANG_TIDY_ENABLE_STATIC_ANALYZER65#define ANALYZER_CHECK_NAME_PREFIX "clang-analyzer-"66static constexpr llvm::StringLiteral AnalyzerCheckNamePrefix =67    ANALYZER_CHECK_NAME_PREFIX;68 69class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {70public:71  AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}72 73  void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,74                            FilesMade *FilesMade) override {75    for (const ento::PathDiagnostic *PD : Diags) {76      SmallString<64> CheckName(AnalyzerCheckNamePrefix);77      CheckName += PD->getCheckerName();78      Context.diag(CheckName, PD->getLocation().asLocation(),79                   PD->getShortDescription())80          << PD->path.back()->getRanges();81 82      for (const auto &DiagPiece :83           PD->path.flatten(/*ShouldFlattenMacros=*/true)) {84        Context.diag(CheckName, DiagPiece->getLocation().asLocation(),85                     DiagPiece->getString(), DiagnosticIDs::Note)86            << DiagPiece->getRanges();87      }88    }89  }90 91  StringRef getName() const override { return "ClangTidyDiags"; }92  bool supportsLogicalOpControlFlow() const override { return true; }93  bool supportsCrossFileDiagnostics() const override { return true; }94 95private:96  ClangTidyContext &Context;97};98#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER99 100class ErrorReporter {101public:102  ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,103                llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)104      : Files(FileSystemOptions(), std::move(BaseFS)),105        DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),106        Diags(DiagnosticIDs::create(), DiagOpts, DiagPrinter),107        SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes) {108    DiagOpts.ShowColors = Context.getOptions().UseColor.value_or(109        llvm::sys::Process::StandardOutHasColors());110    DiagPrinter->BeginSourceFile(LangOpts);111    if (DiagOpts.ShowColors && !llvm::sys::Process::StandardOutIsDisplayed()) {112      llvm::sys::Process::UseANSIEscapeCodes(true);113    }114  }115 116  SourceManager &getSourceManager() { return SourceMgr; }117 118  void reportDiagnostic(const ClangTidyError &Error) {119    const tooling::DiagnosticMessage &Message = Error.Message;120    const SourceLocation Loc =121        getLocation(Message.FilePath, Message.FileOffset);122    // Contains a pair for each attempted fix: location and whether the fix was123    // applied successfully.124    SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;125    {126      auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);127      std::string Name = Error.DiagnosticName;128      if (!Error.EnabledDiagnosticAliases.empty())129        Name += "," + llvm::join(Error.EnabledDiagnosticAliases, ",");130      if (Error.IsWarningAsError) {131        Name += ",-warnings-as-errors";132        Level = DiagnosticsEngine::Error;133        WarningsAsErrors++;134      }135      auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))136                  << Message.Message << Name;137      for (const FileByteRange &FBR : Error.Message.Ranges)138        Diag << getRange(FBR);139      // FIXME: explore options to support interactive fix selection.140      const llvm::StringMap<Replacements> *ChosenFix = nullptr;141      if (ApplyFixes != FB_NoFix &&142          (ChosenFix = getFixIt(Error, ApplyFixes == FB_FixNotes))) {143        for (const auto &FileAndReplacements : *ChosenFix) {144          for (const auto &Repl : FileAndReplacements.second) {145            ++TotalFixes;146            bool CanBeApplied = false;147            if (!Repl.isApplicable())148              continue;149            SourceLocation FixLoc;150            SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();151            Files.makeAbsolutePath(FixAbsoluteFilePath);152            tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),153                                   Repl.getLength(), Repl.getReplacementText());154            auto &Entry = FileReplacements[R.getFilePath()];155            Replacements &Replacements = Entry.Replaces;156            llvm::Error Err = Replacements.add(R);157            if (Err) {158              // FIXME: Implement better conflict handling.159              llvm::errs() << "Trying to resolve conflict: "160                           << llvm::toString(std::move(Err)) << "\n";161              const unsigned NewOffset =162                  Replacements.getShiftedCodePosition(R.getOffset());163              const unsigned NewLength = Replacements.getShiftedCodePosition(164                                             R.getOffset() + R.getLength()) -165                                         NewOffset;166              if (NewLength == R.getLength()) {167                R = Replacement(R.getFilePath(), NewOffset, NewLength,168                                R.getReplacementText());169                Replacements = Replacements.merge(tooling::Replacements(R));170                CanBeApplied = true;171                ++AppliedFixes;172              } else {173                llvm::errs()174                    << "Can't resolve conflict, skipping the replacement.\n";175              }176            } else {177              CanBeApplied = true;178              ++AppliedFixes;179            }180            FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());181            FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));182            Entry.BuildDir = Error.BuildDirectory;183          }184        }185      }186      reportFix(Diag, Error.Message.Fix);187    }188    for (auto Fix : FixLocations) {189      Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied190                                         : diag::note_fixit_failed);191    }192    for (const auto &Note : Error.Notes)193      reportNote(Note);194  }195 196  void finish() {197    if (TotalFixes > 0) {198      auto &VFS = Files.getVirtualFileSystem();199      auto OriginalCWD = VFS.getCurrentWorkingDirectory();200      bool AnyNotWritten = false;201 202      for (const auto &FileAndReplacements : FileReplacements) {203        Rewriter Rewrite(SourceMgr, LangOpts);204        const StringRef File = FileAndReplacements.first();205        VFS.setCurrentWorkingDirectory(FileAndReplacements.second.BuildDir);206        llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =207            SourceMgr.getFileManager().getBufferForFile(File);208        if (!Buffer) {209          llvm::errs() << "Can't get buffer for file " << File << ": "210                       << Buffer.getError().message() << "\n";211          // FIXME: Maybe don't apply fixes for other files as well.212          continue;213        }214        const StringRef Code = Buffer.get()->getBuffer();215        auto Style = format::getStyle(216            *Context.getOptionsForFile(File).FormatStyle, File, "none");217        if (!Style) {218          llvm::errs() << llvm::toString(Style.takeError()) << "\n";219          continue;220        }221        llvm::Expected<tooling::Replacements> Replacements =222            format::cleanupAroundReplacements(223                Code, FileAndReplacements.second.Replaces, *Style);224        if (!Replacements) {225          llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";226          continue;227        }228        if (llvm::Expected<tooling::Replacements> FormattedReplacements =229                format::formatReplacements(Code, *Replacements, *Style)) {230          Replacements = std::move(FormattedReplacements);231          if (!Replacements)232            llvm_unreachable("!Replacements");233        } else {234          llvm::errs() << llvm::toString(FormattedReplacements.takeError())235                       << ". Skipping formatting.\n";236        }237        if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {238          llvm::errs() << "Can't apply replacements for file " << File << "\n";239        }240        AnyNotWritten |= Rewrite.overwriteChangedFiles();241      }242 243      if (AnyNotWritten) {244        llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";245      } else {246        llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "247                     << TotalFixes << " suggested fixes.\n";248      }249 250      if (OriginalCWD)251        VFS.setCurrentWorkingDirectory(*OriginalCWD);252    }253  }254 255  unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }256 257private:258  SourceLocation getLocation(StringRef FilePath, unsigned Offset) {259    if (FilePath.empty())260      return {};261 262    auto File = SourceMgr.getFileManager().getOptionalFileRef(FilePath);263    if (!File)264      return {};265 266    const FileID ID = SourceMgr.getOrCreateFileID(*File, SrcMgr::C_User);267    return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);268  }269 270  void reportFix(const DiagnosticBuilder &Diag,271                 const llvm::StringMap<Replacements> &Fix) {272    for (const auto &FileAndReplacements : Fix) {273      for (const auto &Repl : FileAndReplacements.second) {274        if (!Repl.isApplicable())275          continue;276        FileByteRange FBR;277        FBR.FilePath = Repl.getFilePath().str();278        FBR.FileOffset = Repl.getOffset();279        FBR.Length = Repl.getLength();280 281        Diag << FixItHint::CreateReplacement(getRange(FBR),282                                             Repl.getReplacementText());283      }284    }285  }286 287  void reportNote(const tooling::DiagnosticMessage &Message) {288    const SourceLocation Loc =289        getLocation(Message.FilePath, Message.FileOffset);290    auto Diag =291        Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))292        << Message.Message;293    for (const FileByteRange &FBR : Message.Ranges)294      Diag << getRange(FBR);295    reportFix(Diag, Message.Fix);296  }297 298  CharSourceRange getRange(const FileByteRange &Range) {299    SmallString<128> AbsoluteFilePath{Range.FilePath};300    Files.makeAbsolutePath(AbsoluteFilePath);301    const SourceLocation BeginLoc =302        getLocation(AbsoluteFilePath, Range.FileOffset);303    const SourceLocation EndLoc = BeginLoc.getLocWithOffset(Range.Length);304    // Retrieve the source range for applicable highlights and fixes. Macro305    // definition on the command line have locations in a virtual buffer and306    // don't have valid file paths and are therefore not applicable.307    return CharSourceRange::getCharRange(BeginLoc, EndLoc);308  }309 310  struct ReplacementsWithBuildDir {311    StringRef BuildDir;312    Replacements Replaces;313  };314 315  FileManager Files;316  LangOptions LangOpts; // FIXME: use langopts from each original file317  DiagnosticOptions DiagOpts;318  DiagnosticConsumer *DiagPrinter;319  DiagnosticsEngine Diags;320  SourceManager SourceMgr;321  llvm::StringMap<ReplacementsWithBuildDir> FileReplacements;322  ClangTidyContext &Context;323  FixBehaviour ApplyFixes;324  unsigned TotalFixes = 0U;325  unsigned AppliedFixes = 0U;326  unsigned WarningsAsErrors = 0U;327};328 329class ClangTidyASTConsumer : public MultiplexConsumer {330public:331  ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,332                       std::unique_ptr<ClangTidyProfiling> Profiling,333                       std::unique_ptr<ast_matchers::MatchFinder> Finder,334                       std::vector<std::unique_ptr<ClangTidyCheck>> Checks)335      : MultiplexConsumer(std::move(Consumers)),336        Profiling(std::move(Profiling)), Finder(std::move(Finder)),337        Checks(std::move(Checks)) {}338 339private:340  // Destructor order matters! Profiling must be destructed last.341  // Or at least after Finder.342  std::unique_ptr<ClangTidyProfiling> Profiling;343  std::unique_ptr<ast_matchers::MatchFinder> Finder;344  std::vector<std::unique_ptr<ClangTidyCheck>> Checks;345  void anchor() override {};346};347 348} // namespace349 350ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(351    ClangTidyContext &Context,352    IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)353    : Context(Context), OverlayFS(std::move(OverlayFS)),354      CheckFactories(new ClangTidyCheckFactories) {355#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS356  if (Context.canExperimentalCustomChecks() && custom::RegisterCustomChecks)357    custom::RegisterCustomChecks(Context.getOptions(), *CheckFactories);358#endif359  for (const ClangTidyModuleRegistry::entry E :360       ClangTidyModuleRegistry::entries()) {361    std::unique_ptr<ClangTidyModule> Module = E.instantiate();362    Module->addCheckFactories(*CheckFactories);363  }364}365 366#if CLANG_TIDY_ENABLE_STATIC_ANALYZER367static void368setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,369                             clang::AnalyzerOptions &AnalyzerOptions) {370  for (const auto &Opt : Opts.CheckOptions) {371    StringRef OptName(Opt.getKey());372    if (!OptName.consume_front(AnalyzerCheckNamePrefix))373      continue;374    // Analyzer options are always local options so we can ignore priority.375    AnalyzerOptions.Config[OptName] = Opt.getValue().Value;376  }377}378 379using CheckersList = std::vector<std::pair<std::string, bool>>;380 381static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context,382                                                   bool IncludeExperimental) {383  CheckersList List;384 385  const auto &RegisteredCheckers =386      AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);387  const bool AnalyzerChecksEnabled =388      llvm::any_of(RegisteredCheckers, [&](StringRef CheckName) -> bool {389        return Context.isCheckEnabled(390            (AnalyzerCheckNamePrefix + CheckName).str());391      });392 393  if (!AnalyzerChecksEnabled)394    return List;395 396  // List all static analyzer checkers that our filter enables.397  //398  // Always add all core checkers if any other static analyzer check is enabled.399  // This is currently necessary, as other path sensitive checks rely on the400  // core checkers.401  for (const StringRef CheckName : RegisteredCheckers) {402    const std::string ClangTidyCheckName(403        (AnalyzerCheckNamePrefix + CheckName).str());404 405    if (CheckName.starts_with("core") ||406        Context.isCheckEnabled(ClangTidyCheckName)) {407      List.emplace_back(std::string(CheckName), true);408    }409  }410  return List;411}412#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER413 414std::unique_ptr<clang::ASTConsumer>415ClangTidyASTConsumerFactory::createASTConsumer(416    clang::CompilerInstance &Compiler, StringRef File) {417  // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't418  // modify Compiler.419  SourceManager *SM = &Compiler.getSourceManager();420  Context.setSourceManager(SM);421  Context.setCurrentFile(File);422  Context.setASTContext(&Compiler.getASTContext());423 424  auto WorkingDir = Compiler.getSourceManager()425                        .getFileManager()426                        .getVirtualFileSystem()427                        .getCurrentWorkingDirectory();428  if (WorkingDir)429    Context.setCurrentBuildDirectory(WorkingDir.get());430#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS431  if (Context.canExperimentalCustomChecks() && custom::RegisterCustomChecks)432    custom::RegisterCustomChecks(Context.getOptions(), *CheckFactories);433#endif434  std::vector<std::unique_ptr<ClangTidyCheck>> Checks =435      CheckFactories->createChecksForLanguage(&Context);436 437  ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;438 439  std::unique_ptr<ClangTidyProfiling> Profiling;440  if (Context.getEnableProfiling()) {441    Profiling =442        std::make_unique<ClangTidyProfiling>(Context.getProfileStorageParams());443    FinderOptions.CheckProfiling.emplace(Profiling->Records);444  }445 446  // Avoid processing system headers, unless the user explicitly requests it447  if (!Context.getOptions().SystemHeaders.value_or(false))448    FinderOptions.IgnoreSystemHeaders = true;449 450  std::unique_ptr<ast_matchers::MatchFinder> Finder(451      new ast_matchers::MatchFinder(std::move(FinderOptions)));452 453  Preprocessor *PP = &Compiler.getPreprocessor();454  Preprocessor *ModuleExpanderPP = PP;455 456  if (Context.canEnableModuleHeadersParsing() &&457      Context.getLangOpts().Modules && OverlayFS != nullptr) {458    auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>(459        &Compiler, *OverlayFS);460    ModuleExpanderPP = ModuleExpander->getPreprocessor();461    PP->addPPCallbacks(std::move(ModuleExpander));462  }463 464  for (auto &Check : Checks) {465    Check->registerMatchers(&*Finder);466    Check->registerPPCallbacks(*SM, PP, ModuleExpanderPP);467  }468 469  std::vector<std::unique_ptr<ASTConsumer>> Consumers;470  if (!Checks.empty())471    Consumers.push_back(Finder->newASTConsumer());472 473#if CLANG_TIDY_ENABLE_STATIC_ANALYZER474  AnalyzerOptions &AnalyzerOptions = Compiler.getAnalyzerOpts();475  AnalyzerOptions.CheckersAndPackages = getAnalyzerCheckersAndPackages(476      Context, Context.canEnableAnalyzerAlphaCheckers());477  if (!AnalyzerOptions.CheckersAndPackages.empty()) {478    setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);479    AnalyzerOptions.AnalysisDiagOpt = PD_NONE;480    std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =481        ento::CreateAnalysisConsumer(Compiler);482    AnalysisConsumer->AddDiagnosticConsumer(483        std::make_unique<AnalyzerDiagnosticConsumer>(Context));484    Consumers.push_back(std::move(AnalysisConsumer));485  }486#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER487  return std::make_unique<ClangTidyASTConsumer>(488      std::move(Consumers), std::move(Profiling), std::move(Finder),489      std::move(Checks));490}491 492std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {493  std::vector<std::string> CheckNames;494  for (const auto &CheckFactory : *CheckFactories) {495    if (Context.isCheckEnabled(CheckFactory.getKey()))496      CheckNames.emplace_back(CheckFactory.getKey());497  }498 499#if CLANG_TIDY_ENABLE_STATIC_ANALYZER500  for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages(501           Context, Context.canEnableAnalyzerAlphaCheckers()))502    CheckNames.emplace_back(503        (AnalyzerCheckNamePrefix + AnalyzerCheck.first).str());504#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER505 506  llvm::sort(CheckNames);507  return CheckNames;508}509 510ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {511  ClangTidyOptions::OptionMap Options;512  const std::vector<std::unique_ptr<ClangTidyCheck>> Checks =513      CheckFactories->createChecks(&Context);514  for (const auto &Check : Checks)515    Check->storeOptions(Options);516  return Options;517}518 519std::vector<std::string> getCheckNames(const ClangTidyOptions &Options,520                                       bool AllowEnablingAnalyzerAlphaCheckers,521                                       bool ExperimentalCustomChecks) {522  clang::tidy::ClangTidyContext Context(523      std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),524                                               Options),525      AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);526  ClangTidyASTConsumerFactory Factory(Context);527  return Factory.getCheckNames();528}529 530void filterCheckOptions(ClangTidyOptions &Options,531                        const std::vector<std::string> &EnabledChecks) {532  ClangTidyOptions::OptionMap FilteredOptions;533  for (const auto &[OptionName, Value] : Options.CheckOptions) {534    const size_t CheckNameEndPos = OptionName.find('.');535    if (CheckNameEndPos == StringRef::npos)536      continue;537    const StringRef CheckName = OptionName.substr(0, CheckNameEndPos);538    if (llvm::binary_search(EnabledChecks, CheckName))539      FilteredOptions[OptionName] = Value;540  }541  Options.CheckOptions = std::move(FilteredOptions);542}543 544ClangTidyOptions::OptionMap545getCheckOptions(const ClangTidyOptions &Options,546                bool AllowEnablingAnalyzerAlphaCheckers,547                bool ExperimentalCustomChecks) {548  clang::tidy::ClangTidyContext Context(549      std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),550                                               Options),551      AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);552  ClangTidyDiagnosticConsumer DiagConsumer(Context);553  auto DiagOpts = std::make_unique<DiagnosticOptions>();554  DiagnosticsEngine DE(llvm::makeIntrusiveRefCnt<DiagnosticIDs>(), *DiagOpts,555                       &DiagConsumer, /*ShouldOwnClient=*/false);556  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);557  ClangTidyASTConsumerFactory Factory(Context);558  return Factory.getCheckOptions();559}560 561std::vector<ClangTidyError>562runClangTidy(clang::tidy::ClangTidyContext &Context,563             const CompilationDatabase &Compilations,564             ArrayRef<std::string> InputFiles,565             llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,566             bool ApplyAnyFix, bool EnableCheckProfile,567             llvm::StringRef StoreCheckProfile, bool Quiet) {568  ClangTool Tool(Compilations, InputFiles,569                 std::make_shared<PCHContainerOperations>(), BaseFS);570 571  // Add extra arguments passed by the clang-tidy command-line.572  const ArgumentsAdjuster PerFileExtraArgumentsInserter =573      [&Context](const CommandLineArguments &Args, StringRef Filename) {574        ClangTidyOptions Opts = Context.getOptionsForFile(Filename);575        CommandLineArguments AdjustedArgs = Args;576        if (Opts.ExtraArgsBefore) {577          auto I = AdjustedArgs.begin();578          if (I != AdjustedArgs.end() && !StringRef(*I).starts_with("-"))579            ++I; // Skip compiler binary name, if it is there.580          AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),581                              Opts.ExtraArgsBefore->end());582        }583        if (Opts.ExtraArgs)584          AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),585                              Opts.ExtraArgs->end());586        return AdjustedArgs;587      };588 589  Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);590  Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());591  Context.setEnableProfiling(EnableCheckProfile);592  Context.setProfileStoragePrefix(StoreCheckProfile);593 594  ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix);595  auto DiagOpts = std::make_unique<DiagnosticOptions>();596  DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,597                       /*ShouldOwnClient=*/false);598  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);599  Tool.setDiagnosticConsumer(&DiagConsumer);600 601  class ActionFactory : public FrontendActionFactory {602  public:603    ActionFactory(ClangTidyContext &Context,604                  IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,605                  bool Quiet)606        : ConsumerFactory(Context, std::move(BaseFS)), Quiet(Quiet) {}607    std::unique_ptr<FrontendAction> create() override {608      return std::make_unique<Action>(&ConsumerFactory);609    }610 611    bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,612                       FileManager *Files,613                       std::shared_ptr<PCHContainerOperations> PCHContainerOps,614                       DiagnosticConsumer *DiagConsumer) override {615      // Explicitly ask to define __clang_analyzer__ macro.616      Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;617      if (Quiet)618        Invocation->getDiagnosticOpts().ShowCarets = false;619      return FrontendActionFactory::runInvocation(620          Invocation, Files, PCHContainerOps, DiagConsumer);621    }622 623  private:624    class Action : public ASTFrontendAction {625    public:626      Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}627      std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,628                                                     StringRef File) override {629        return Factory->createASTConsumer(Compiler, File);630      }631 632    private:633      ClangTidyASTConsumerFactory *Factory;634    };635 636    ClangTidyASTConsumerFactory ConsumerFactory;637    bool Quiet;638  };639 640  ActionFactory Factory(Context, std::move(BaseFS), Quiet);641  Tool.run(&Factory);642  return DiagConsumer.take();643}644 645void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,646                  ClangTidyContext &Context, FixBehaviour Fix,647                  unsigned &WarningsAsErrorsCount,648                  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {649  ErrorReporter Reporter(Context, Fix, std::move(BaseFS));650  llvm::vfs::FileSystem &FileSystem =651      Reporter.getSourceManager().getFileManager().getVirtualFileSystem();652  auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();653  if (!InitialWorkingDir)654    llvm::report_fatal_error("Cannot get current working path.");655 656  for (const ClangTidyError &Error : Errors) {657    if (!Error.BuildDirectory.empty()) {658      // By default, the working directory of file system is the current659      // clang-tidy running directory.660      //661      // Change the directory to the one used during the analysis.662      FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);663    }664    Reporter.reportDiagnostic(Error);665    // Return to the initial directory to correctly resolve next Error.666    FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());667  }668  Reporter.finish();669  WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();670}671 672void exportReplacements(const llvm::StringRef MainFilePath,673                        const std::vector<ClangTidyError> &Errors,674                        raw_ostream &OS) {675  TranslationUnitDiagnostics TUD;676  TUD.MainSourceFile = std::string(MainFilePath);677  for (const auto &Error : Errors) {678    tooling::Diagnostic Diag = Error;679    if (Error.IsWarningAsError)680      Diag.DiagLevel = tooling::Diagnostic::Error;681    TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);682  }683 684  yaml::Output YAML(OS);685  YAML << TUD;686}687 688ChecksAndOptions getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers,689                                        bool ExperimentalCustomChecks) {690  ChecksAndOptions Result;691  ClangTidyOptions Opts;692  Opts.Checks = "*";693  clang::tidy::ClangTidyContext Context(694      std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), Opts),695      AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);696  ClangTidyCheckFactories Factories;697#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS698  if (ExperimentalCustomChecks && custom::RegisterCustomChecks)699    custom::RegisterCustomChecks(Context.getOptions(), Factories);700#endif701  for (const ClangTidyModuleRegistry::entry &Module :702       ClangTidyModuleRegistry::entries()) {703    Module.instantiate()->addCheckFactories(Factories);704  }705 706  for (const auto &Factory : Factories)707    Result.Checks.insert(Factory.getKey());708 709#if CLANG_TIDY_ENABLE_STATIC_ANALYZER710  SmallString<64> Buffer(AnalyzerCheckNamePrefix);711  const size_t DefSize = Buffer.size();712  for (const auto &AnalyzerCheck : AnalyzerOptions::getRegisteredCheckers(713           AllowEnablingAnalyzerAlphaCheckers)) {714    Buffer.truncate(DefSize);715    Buffer.append(AnalyzerCheck);716    Result.Checks.insert(Buffer);717  }718 719  static constexpr llvm::StringLiteral OptionNames[] = {720#define GET_CHECKER_OPTIONS721#define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT,       \722                       RELEASE, HIDDEN)                                        \723  ANALYZER_CHECK_NAME_PREFIX CHECKER ":" OPTION_NAME,724 725#include "clang/StaticAnalyzer/Checkers/Checkers.inc"726#undef CHECKER_OPTION727#undef GET_CHECKER_OPTIONS728  };729 730  Result.Options.insert_range(OptionNames);731#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER732 733  Context.setOptionsCollector(&Result.Options);734  for (const auto &Factory : Factories) {735    Factory.getValue()(Factory.getKey(), &Context);736  }737 738  return Result;739}740} // namespace clang::tidy741