brintos

brintos / llvm-project-archived public Read only

0
0
Text · 29.9 KiB · bc6bd16 Raw
796 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 "ClangTidyMain.h"18#include "../ClangTidy.h"19#include "../ClangTidyForceLinker.h"20#include "../GlobList.h"21#include "clang/Tooling/CommonOptionsParser.h"22#include "llvm/ADT/StringSet.h"23#include "llvm/Support/CommandLine.h"24#include "llvm/Support/InitLLVM.h"25#include "llvm/Support/PluginLoader.h"26#include "llvm/Support/Process.h"27#include "llvm/Support/Signals.h"28#include "llvm/Support/TargetSelect.h"29#include "llvm/Support/WithColor.h"30#include "llvm/TargetParser/Host.h"31#include <optional>32 33using namespace clang::tooling;34using namespace llvm;35 36static cl::desc desc(StringRef Description) { return {Description.ltrim()}; }37 38static cl::OptionCategory ClangTidyCategory("clang-tidy options");39 40static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);41static cl::extrahelp ClangTidyParameterFileHelp(R"(42Parameters files:43  A large number of options or source files can be passed as parameter files44  by use '@parameter-file' in the command line.45)");46static cl::extrahelp ClangTidyHelp(R"(47Configuration files:48  clang-tidy attempts to read configuration for each source file from a49  .clang-tidy file located in the closest parent directory of the source50  file. The .clang-tidy file is specified in YAML format. If any configuration51  options have a corresponding command-line option, command-line option takes52  precedence.53 54  The following configuration options may be used in a .clang-tidy file:55 56  CheckOptions                 - List of key-value pairs defining check-specific57                                 options. Example:58                                   CheckOptions:59                                     some-check.SomeOption: 'some value'60  Checks                       - Same as '--checks'. Additionally, the list of61                                 globs can be specified as a list instead of a62                                 string.63  CustomChecks                 - Array of user defined checks based on64                                 Clang-Query syntax.65  ExcludeHeaderFilterRegex     - Same as '--exclude-header-filter'.66  ExtraArgs                    - Same as '--extra-arg'.67  ExtraArgsBefore              - Same as '--extra-arg-before'.68  FormatStyle                  - Same as '--format-style'.69  HeaderFileExtensions         - File extensions to consider to determine if a70                                 given diagnostic is located in a header file.71  HeaderFilterRegex            - Same as '--header-filter'.72  ImplementationFileExtensions - File extensions to consider to determine if a73                                 given diagnostic is located in an74                                 implementation file.75  InheritParentConfig          - If this option is true in a config file, the76                                 configuration file in the parent directory77                                 (if any exists) will be taken and the current78                                 config file will be applied on top of the79                                 parent one.80  SystemHeaders                - Same as '--system-headers'.81  UseColor                     - Same as '--use-color'.82  User                         - Specifies the name or e-mail of the user83                                 running clang-tidy. This option is used, for84                                 example, to place the correct user name in85                                 TODO() comments in the relevant check.86  WarningsAsErrors             - Same as '--warnings-as-errors'.87 88  The effective configuration can be inspected using --dump-config:89 90    $ clang-tidy --dump-config91    ---92    Checks:                       '-*,some-check'93    WarningsAsErrors:             ''94    HeaderFileExtensions:         ['', 'h','hh','hpp','hxx']95    ImplementationFileExtensions: ['c','cc','cpp','cxx']96    HeaderFilterRegex:            '.*'97    FormatStyle:                  none98    InheritParentConfig:          true99    User:                         user100    CheckOptions:101      some-check.SomeOption: 'some value'102    ...103 104)");105 106const char DefaultChecks[] = // Enable these checks by default:107    "clang-diagnostic-*,"    //   * compiler diagnostics108    "clang-analyzer-*";      //   * Static Analyzer checks109 110static cl::opt<std::string> Checks("checks", desc(R"(111Comma-separated list of globs with optional '-'112prefix. Globs are processed in order of113appearance in the list. Globs without '-'114prefix add checks with matching names to the115set, globs with the '-' prefix remove checks116with matching names from the set of enabled117checks. This option's value is appended to the118value of the 'Checks' option in .clang-tidy119file, if any.120)"),121                                   cl::init(""), cl::cat(ClangTidyCategory));122 123static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", desc(R"(124Upgrades warnings to errors. Same format as125'-checks'.126This option's value is appended to the value of127the 'WarningsAsErrors' option in .clang-tidy128file, if any.129)"),130                                             cl::init(""),131                                             cl::cat(ClangTidyCategory));132 133static cl::opt<std::string> HeaderFilter("header-filter", desc(R"(134Regular expression matching the names of the135headers to output diagnostics from. The default136value is '.*', i.e. diagnostics from all non-system137headers are displayed by default. Diagnostics138from the main file of each translation unit are139always displayed.140Can be used together with -line-filter.141This option overrides the 'HeaderFilterRegex'142option in .clang-tidy file, if any.143)"),144                                         cl::init(".*"),145                                         cl::cat(ClangTidyCategory));146 147static cl::opt<std::string> ExcludeHeaderFilter("exclude-header-filter",148                                                desc(R"(149Regular expression matching the names of the150headers to exclude diagnostics from. Diagnostics151from the main file of each translation unit are152always displayed.153Must be used together with --header-filter.154Can be used together with -line-filter.155This option overrides the 'ExcludeHeaderFilterRegex'156option in .clang-tidy file, if any.157)"),158                                                cl::init(""),159                                                cl::cat(ClangTidyCategory));160 161static cl::opt<bool> SystemHeaders("system-headers", desc(R"(162Display the errors from system headers.163This option overrides the 'SystemHeaders' option164in .clang-tidy file, if any.165)"),166                                   cl::init(false), cl::cat(ClangTidyCategory));167 168static cl::opt<std::string> LineFilter("line-filter", desc(R"(169List of files and line ranges to output diagnostics from.170The range is inclusive on both ends. Can be used together171with -header-filter. The format of the list is a JSON172array of objects. For example:173 174  [175    {"name":"file1.cpp","lines":[[1,3],[5,7]]},176    {"name":"file2.h"}177  ]178 179This will output diagnostics from 'file1.cpp' only for180the line ranges [1,3] and [5,7], as well as all from the181entire 'file2.h'.182)"),183                                       cl::init(""),184                                       cl::cat(ClangTidyCategory));185 186static cl::opt<bool> Fix("fix", desc(R"(187Apply suggested fixes. Without -fix-errors188clang-tidy will bail out if any compilation189errors were found.190)"),191                         cl::init(false), cl::cat(ClangTidyCategory));192 193static cl::opt<bool> FixErrors("fix-errors", desc(R"(194Apply suggested fixes even if compilation195errors were found. If compiler errors have196attached fix-its, clang-tidy will apply them as197well.198)"),199                               cl::init(false), cl::cat(ClangTidyCategory));200 201static cl::opt<bool> FixNotes("fix-notes", desc(R"(202If a warning has no fix, but a single fix can203be found through an associated diagnostic note,204apply the fix.205Specifying this flag will implicitly enable the206'--fix' flag.207)"),208                              cl::init(false), cl::cat(ClangTidyCategory));209 210static cl::opt<std::string> FormatStyle("format-style", desc(R"(211Style for formatting code around applied fixes:212  - 'none' (default) turns off formatting213  - 'file' (literally 'file', not a placeholder)214    uses .clang-format file in the closest parent215    directory216  - '{ <json> }' specifies options inline, e.g.217    -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'218  - 'llvm', 'google', 'webkit', 'mozilla'219See clang-format documentation for the up-to-date220information about formatting styles and options.221This option overrides the 'FormatStyle` option in222.clang-tidy file, if any.223)"),224                                        cl::init("none"),225                                        cl::cat(ClangTidyCategory));226 227static cl::opt<bool> ListChecks("list-checks", desc(R"(228List all enabled checks and exit. Use with229-checks=* to list all available checks.230)"),231                                cl::init(false), cl::cat(ClangTidyCategory));232 233static cl::opt<bool> ExplainConfig("explain-config", desc(R"(234For each enabled check explains, where it is235enabled, i.e. in clang-tidy binary, command236line or a specific configuration file.237)"),238                                   cl::init(false), cl::cat(ClangTidyCategory));239 240static cl::opt<std::string> Config("config", desc(R"(241Specifies a configuration in YAML/JSON format:242  -config="{Checks: '*',243            CheckOptions: {x: y}}"244When the value is empty, clang-tidy will245attempt to find a file named .clang-tidy for246each source file in its parent directories.247)"),248                                   cl::init(""), cl::cat(ClangTidyCategory));249 250static cl::opt<std::string> ConfigFile("config-file", desc(R"(251Specify the path of .clang-tidy or custom config file:252 e.g. --config-file=/some/path/myTidyConfigFile253This option internally works exactly the same way as254 --config option after reading specified config file.255Use either --config-file or --config, not both.256)"),257                                       cl::init(""),258                                       cl::cat(ClangTidyCategory));259 260static cl::opt<bool> DumpConfig("dump-config", desc(R"(261Dumps configuration in the YAML format to262stdout. This option can be used along with a263file name (and '--' if the file is outside of a264project with configured compilation database).265The configuration used for this file will be266printed.267Use along with -checks=* to include268configuration of all checks.269)"),270                                cl::init(false), cl::cat(ClangTidyCategory));271 272static cl::opt<bool> EnableCheckProfile("enable-check-profile", desc(R"(273Enable per-check timing profiles, and print a274report to stderr.275)"),276                                        cl::init(false),277                                        cl::cat(ClangTidyCategory));278 279static cl::opt<std::string> StoreCheckProfile("store-check-profile", desc(R"(280By default reports are printed in tabulated281format to stderr. When this option is passed,282these per-TU profiles are instead stored as JSON.283)"),284                                              cl::value_desc("prefix"),285                                              cl::cat(ClangTidyCategory));286 287/// This option allows enabling the experimental alpha checkers from the static288/// analyzer. This option is set to false and not visible in help, because it is289/// highly not recommended for users.290static cl::opt<bool>291    AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers",292                                       cl::init(false), cl::Hidden,293                                       cl::cat(ClangTidyCategory));294 295static cl::opt<bool> EnableModuleHeadersParsing("enable-module-headers-parsing",296                                                desc(R"(297Enables preprocessor-level module header parsing298for C++20 and above, empowering specific checks299to detect macro definitions within modules. This300feature may cause performance and parsing issues301and is therefore considered experimental.302)"),303                                                cl::init(false),304                                                cl::cat(ClangTidyCategory));305 306static cl::opt<std::string> ExportFixes("export-fixes", desc(R"(307YAML file to store suggested fixes in. The308stored fixes can be applied to the input source309code with clang-apply-replacements.310)"),311                                        cl::value_desc("filename"),312                                        cl::cat(ClangTidyCategory));313 314static cl::opt<bool> Quiet("quiet", desc(R"(315Run clang-tidy in quiet mode. This suppresses316printing statistics about ignored warnings and317warnings treated as errors if the respective318options are specified.319)"),320                           cl::init(false), cl::cat(ClangTidyCategory));321 322static cl::opt<std::string> VfsOverlay("vfsoverlay", desc(R"(323Overlay the virtual filesystem described by file324over the real file system.325)"),326                                       cl::value_desc("filename"),327                                       cl::cat(ClangTidyCategory));328 329static cl::opt<bool> UseColor("use-color", desc(R"(330Use colors in diagnostics. If not set, colors331will be used if the terminal connected to332standard output supports colors.333This option overrides the 'UseColor' option in334.clang-tidy file, if any.335)"),336                              cl::init(false), cl::cat(ClangTidyCategory));337 338static cl::opt<bool> VerifyConfig("verify-config", desc(R"(339Check the config files to ensure each check and340option is recognized without running any checks.341)"),342                                  cl::init(false), cl::cat(ClangTidyCategory));343 344static cl::opt<bool> AllowNoChecks("allow-no-checks", desc(R"(345Allow empty enabled checks. This suppresses346the "no checks enabled" error when disabling347all of the checks.348)"),349                                   cl::init(false), cl::cat(ClangTidyCategory));350 351static cl::opt<bool> ExperimentalCustomChecks("experimental-custom-checks",352                                              desc(R"(353Enable experimental clang-query based354custom checks.355see https://clang.llvm.org/extra/clang-tidy/QueryBasedCustomChecks.html.356)"),357                                              cl::init(false),358                                              cl::cat(ClangTidyCategory));359 360namespace clang::tidy {361 362static void printStats(const ClangTidyStats &Stats) {363  if (Stats.errorsIgnored()) {364    llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings (";365    StringRef Separator = "";366    if (Stats.ErrorsIgnoredNonUserCode) {367      llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";368      Separator = ", ";369    }370    if (Stats.ErrorsIgnoredLineFilter) {371      llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter372                   << " due to line filter";373      Separator = ", ";374    }375    if (Stats.ErrorsIgnoredNOLINT) {376      llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";377      Separator = ", ";378    }379    if (Stats.ErrorsIgnoredCheckFilter)380      llvm::errs() << Separator << Stats.ErrorsIgnoredCheckFilter381                   << " with check filters";382    llvm::errs() << ").\n";383    if (Stats.ErrorsIgnoredNonUserCode)384      llvm::errs() << "Use -header-filter=.* or leave it as default to display "385                      "errors from all non-system headers. Use -system-headers "386                      "to display errors from system headers as well.\n";387  }388}389 390static std::unique_ptr<ClangTidyOptionsProvider>391createOptionsProvider(llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) {392  ClangTidyGlobalOptions GlobalOptions;393  if (const std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) {394    llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";395    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);396    return nullptr;397  }398 399  ClangTidyOptions DefaultOptions;400  DefaultOptions.Checks = DefaultChecks;401  DefaultOptions.WarningsAsErrors = "";402  DefaultOptions.HeaderFilterRegex = HeaderFilter;403  DefaultOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;404  DefaultOptions.SystemHeaders = SystemHeaders;405  DefaultOptions.FormatStyle = FormatStyle;406  DefaultOptions.User = llvm::sys::Process::GetEnv("USER");407  // USERNAME is used on Windows.408  if (!DefaultOptions.User)409    DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME");410 411  ClangTidyOptions OverrideOptions;412  if (Checks.getNumOccurrences() > 0)413    OverrideOptions.Checks = Checks;414  if (WarningsAsErrors.getNumOccurrences() > 0)415    OverrideOptions.WarningsAsErrors = WarningsAsErrors;416  if (HeaderFilter.getNumOccurrences() > 0)417    OverrideOptions.HeaderFilterRegex = HeaderFilter;418  if (ExcludeHeaderFilter.getNumOccurrences() > 0)419    OverrideOptions.ExcludeHeaderFilterRegex = ExcludeHeaderFilter;420  if (SystemHeaders.getNumOccurrences() > 0)421    OverrideOptions.SystemHeaders = SystemHeaders;422  if (FormatStyle.getNumOccurrences() > 0)423    OverrideOptions.FormatStyle = FormatStyle;424  if (UseColor.getNumOccurrences() > 0)425    OverrideOptions.UseColor = UseColor;426 427  auto LoadConfig =428      [&](StringRef Configuration,429          StringRef Source) -> std::unique_ptr<ClangTidyOptionsProvider> {430    llvm::ErrorOr<ClangTidyOptions> ParsedConfig =431        parseConfiguration(MemoryBufferRef(Configuration, Source));432    if (ParsedConfig)433      return std::make_unique<ConfigOptionsProvider>(434          std::move(GlobalOptions),435          ClangTidyOptions::getDefaults().merge(DefaultOptions, 0),436          std::move(*ParsedConfig), std::move(OverrideOptions), std::move(FS));437    llvm::errs() << "Error: invalid configuration specified.\n"438                 << ParsedConfig.getError().message() << "\n";439    return nullptr;440  };441 442  if (ConfigFile.getNumOccurrences() > 0) {443    if (Config.getNumOccurrences() > 0) {444      llvm::errs() << "Error: --config-file and --config are "445                      "mutually exclusive. Specify only one.\n";446      return nullptr;447    }448 449    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =450        llvm::MemoryBuffer::getFile(ConfigFile);451    if (const std::error_code EC = Text.getError()) {452      llvm::errs() << "Error: can't read config-file '" << ConfigFile453                   << "': " << EC.message() << "\n";454      return nullptr;455    }456 457    return LoadConfig((*Text)->getBuffer(), ConfigFile);458  }459 460  if (Config.getNumOccurrences() > 0)461    return LoadConfig(Config, "<command-line-config>");462 463  return std::make_unique<FileOptionsProvider>(464      std::move(GlobalOptions), std::move(DefaultOptions),465      std::move(OverrideOptions), std::move(FS));466}467 468static llvm::IntrusiveRefCntPtr<vfs::FileSystem>469getVfsFromFile(const std::string &OverlayFile, vfs::FileSystem &BaseFS) {470  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =471      BaseFS.getBufferForFile(OverlayFile);472  if (!Buffer) {473    llvm::errs() << "Can't load virtual filesystem overlay file '"474                 << OverlayFile << "': " << Buffer.getError().message()475                 << ".\n";476    return nullptr;477  }478 479  IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getVFSFromYAML(480      std::move(Buffer.get()), /*DiagHandler*/ nullptr, OverlayFile);481  if (!FS) {482    llvm::errs() << "Error: invalid virtual filesystem overlay file '"483                 << OverlayFile << "'.\n";484    return nullptr;485  }486  return FS;487}488 489static StringRef closest(StringRef Value, const StringSet<> &Allowed) {490  unsigned MaxEdit = 5U;491  StringRef Closest;492  for (auto Item : Allowed.keys()) {493    const unsigned Cur = Value.edit_distance_insensitive(Item, true, MaxEdit);494    if (Cur < MaxEdit) {495      Closest = Item;496      MaxEdit = Cur;497    }498  }499  return Closest;500}501 502static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";503 504static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob,505                         StringRef Source) {506  const GlobList Globs(CheckGlob);507  bool AnyInvalid = false;508  for (const auto &Item : Globs.getItems()) {509    if (Item.Text.starts_with("clang-diagnostic"))510      continue;511    if (llvm::none_of(AllChecks.keys(),512                      [&Item](StringRef S) { return Item.Regex.match(S); })) {513      AnyInvalid = true;514      if (Item.Text.contains('*'))515        llvm::WithColor::warning(llvm::errs(), Source)516            << "check glob '" << Item.Text << "' doesn't match any known check"517            << VerifyConfigWarningEnd;518      else {519        llvm::raw_ostream &Output =520            llvm::WithColor::warning(llvm::errs(), Source)521            << "unknown check '" << Item.Text << '\'';522        const llvm::StringRef Closest = closest(Item.Text, AllChecks);523        if (!Closest.empty())524          Output << "; did you mean '" << Closest << '\'';525        Output << VerifyConfigWarningEnd;526      }527    }528  }529  return AnyInvalid;530}531 532static bool verifyFileExtensions(533    const std::vector<std::string> &HeaderFileExtensions,534    const std::vector<std::string> &ImplementationFileExtensions,535    StringRef Source) {536  bool AnyInvalid = false;537  for (const auto &HeaderExtension : HeaderFileExtensions) {538    for (const auto &ImplementationExtension : ImplementationFileExtensions) {539      if (HeaderExtension == ImplementationExtension) {540        AnyInvalid = true;541        auto &Output = llvm::WithColor::warning(llvm::errs(), Source)542                       << "HeaderFileExtension '" << HeaderExtension << '\''543                       << " is the same as ImplementationFileExtension '"544                       << ImplementationExtension << '\'';545        Output << VerifyConfigWarningEnd;546      }547    }548  }549  return AnyInvalid;550}551 552static bool verifyOptions(const llvm::StringSet<> &ValidOptions,553                          const ClangTidyOptions::OptionMap &OptionMap,554                          StringRef Source) {555  bool AnyInvalid = false;556  for (auto Key : OptionMap.keys()) {557    if (ValidOptions.contains(Key))558      continue;559    AnyInvalid = true;560    auto &Output = llvm::WithColor::warning(llvm::errs(), Source)561                   << "unknown check option '" << Key << '\'';562    const llvm::StringRef Closest = closest(Key, ValidOptions);563    if (!Closest.empty())564      Output << "; did you mean '" << Closest << '\'';565    Output << VerifyConfigWarningEnd;566  }567  return AnyInvalid;568}569 570static SmallString<256> makeAbsolute(llvm::StringRef Input) {571  if (Input.empty())572    return {};573  SmallString<256> AbsolutePath(Input);574  if (const std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath)) {575    llvm::errs() << "Can't make absolute path from " << Input << ": "576                 << EC.message() << "\n";577  }578  return AbsolutePath;579}580 581static llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> createBaseFS() {582  llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS(583      new vfs::OverlayFileSystem(vfs::getRealFileSystem()));584 585  if (!VfsOverlay.empty()) {586    IntrusiveRefCntPtr<vfs::FileSystem> VfsFromFile =587        getVfsFromFile(VfsOverlay, *BaseFS);588    if (!VfsFromFile)589      return nullptr;590    BaseFS->pushOverlay(std::move(VfsFromFile));591  }592  return BaseFS;593}594 595int clangTidyMain(int argc, const char **argv) {596  const llvm::InitLLVM X(argc, argv);597  SmallVector<const char *> Args{argv, argv + argc};598 599  // expand parameters file to argc and argv.600  llvm::BumpPtrAllocator Alloc;601  llvm::cl::TokenizerCallback Tokenizer =602      llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows()603          ? llvm::cl::TokenizeWindowsCommandLine604          : llvm::cl::TokenizeGNUCommandLine;605  llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);606  if (llvm::Error Err = ECtx.expandResponseFiles(Args)) {607    llvm::WithColor::error() << llvm::toString(std::move(Err)) << "\n";608    return 1;609  }610  argc = static_cast<int>(Args.size());611  argv = Args.data();612 613  // Enable help for -load option, if plugins are enabled.614  if (cl::Option *LoadOpt = cl::getRegisteredOptions().lookup("load"))615    LoadOpt->addCategory(ClangTidyCategory);616 617  llvm::Expected<CommonOptionsParser> OptionsParser =618      CommonOptionsParser::create(argc, argv, ClangTidyCategory,619                                  cl::ZeroOrMore);620  if (!OptionsParser) {621    llvm::WithColor::error() << llvm::toString(OptionsParser.takeError());622    return 1;623  }624 625  const llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS =626      createBaseFS();627  if (!BaseFS)628    return 1;629 630  auto OwningOptionsProvider = createOptionsProvider(BaseFS);631  auto *OptionsProvider = OwningOptionsProvider.get();632  if (!OptionsProvider)633    return 1;634 635  const SmallString<256> ProfilePrefix = makeAbsolute(StoreCheckProfile);636 637  StringRef FileName("dummy");638  auto PathList = OptionsParser->getSourcePathList();639  if (!PathList.empty()) {640    FileName = PathList.front();641  }642 643  const SmallString<256> FilePath = makeAbsolute(FileName);644  ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);645 646  const std::vector<std::string> EnabledChecks =647      getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers,648                    ExperimentalCustomChecks);649 650  if (ExplainConfig) {651    // FIXME: Show other ClangTidyOptions' fields, like ExtraArg.652    std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>653        RawOptions = OptionsProvider->getRawOptions(FilePath);654    for (const std::string &Check : EnabledChecks) {655      for (const auto &[Opts, Source] : llvm::reverse(RawOptions)) {656        if (Opts.Checks && GlobList(*Opts.Checks).contains(Check)) {657          llvm::outs() << "'" << Check << "' is enabled in the " << Source658                       << ".\n";659          break;660        }661      }662    }663    return 0;664  }665 666  if (ListChecks) {667    if (EnabledChecks.empty() && !AllowNoChecks) {668      llvm::errs() << "No checks enabled.\n";669      return 1;670    }671    llvm::outs() << "Enabled checks:";672    for (const auto &CheckName : EnabledChecks)673      llvm::outs() << "\n    " << CheckName;674    llvm::outs() << "\n\n";675    return 0;676  }677 678  if (DumpConfig) {679    EffectiveOptions.CheckOptions =680        getCheckOptions(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers,681                        ExperimentalCustomChecks);682    ClangTidyOptions OptionsToDump =683        ClangTidyOptions::getDefaults().merge(EffectiveOptions, 0);684    filterCheckOptions(OptionsToDump, EnabledChecks);685    llvm::outs() << configurationAsText(OptionsToDump) << "\n";686    return 0;687  }688 689  if (VerifyConfig) {690    const std::vector<ClangTidyOptionsProvider::OptionsSource> RawOptions =691        OptionsProvider->getRawOptions(FileName);692    const ChecksAndOptions Valid = getAllChecksAndOptions(693        AllowEnablingAnalyzerAlphaCheckers, ExperimentalCustomChecks);694    bool AnyInvalid = false;695    for (const auto &[Opts, Source] : RawOptions) {696      if (Opts.Checks)697        AnyInvalid |= verifyChecks(Valid.Checks, *Opts.Checks, Source);698      if (Opts.HeaderFileExtensions && Opts.ImplementationFileExtensions)699        AnyInvalid |=700            verifyFileExtensions(*Opts.HeaderFileExtensions,701                                 *Opts.ImplementationFileExtensions, Source);702      AnyInvalid |= verifyOptions(Valid.Options, Opts.CheckOptions, Source);703    }704    if (AnyInvalid)705      return 1;706    llvm::outs() << "No config errors detected.\n";707    return 0;708  }709 710  if (EnabledChecks.empty()) {711    if (AllowNoChecks) {712      llvm::outs() << "No checks enabled.\n";713      return 0;714    }715    llvm::errs() << "Error: no checks enabled.\n";716    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);717    return 1;718  }719 720  if (PathList.empty()) {721    llvm::errs() << "Error: no input files specified.\n";722    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);723    return 1;724  }725 726  llvm::InitializeAllTargetInfos();727  llvm::InitializeAllTargetMCs();728  llvm::InitializeAllAsmParsers();729 730  ClangTidyContext Context(731      std::move(OwningOptionsProvider), AllowEnablingAnalyzerAlphaCheckers,732      EnableModuleHeadersParsing, ExperimentalCustomChecks);733  std::vector<ClangTidyError> Errors =734      runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,735                   FixNotes, EnableCheckProfile, ProfilePrefix, Quiet);736  const bool FoundErrors = llvm::any_of(Errors, [](const ClangTidyError &E) {737    return E.DiagLevel == ClangTidyError::Error;738  });739 740  // --fix-errors and --fix-notes imply --fix.741  const FixBehaviour Behaviour = FixNotes             ? FB_FixNotes742                                 : (Fix || FixErrors) ? FB_Fix743                                                      : FB_NoFix;744 745  const bool DisableFixes = FoundErrors && !FixErrors;746 747  unsigned WErrorCount = 0;748 749  handleErrors(Errors, Context, DisableFixes ? FB_NoFix : Behaviour,750               WErrorCount, BaseFS);751 752  if (!ExportFixes.empty() && !Errors.empty()) {753    std::error_code EC;754    llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None);755    if (EC) {756      llvm::errs() << "Error opening output file: " << EC.message() << '\n';757      return 1;758    }759    exportReplacements(FilePath.str(), Errors, OS);760  }761 762  if (!Quiet) {763    printStats(Context.getStats());764    if (DisableFixes && Behaviour != FB_NoFix)765      llvm::errs()766          << "Found compiler errors, but -fix-errors was not specified.\n"767             "Fixes have NOT been applied.\n\n";768  }769 770  if (WErrorCount) {771    if (!Quiet) {772      const StringRef Plural = WErrorCount == 1 ? "" : "s";773      llvm::errs() << WErrorCount << " warning" << Plural << " treated as error"774                   << Plural << "\n";775    }776    return 1;777  }778 779  if (FoundErrors) {780    // TODO: Figure out when zero exit code should be used with -fix-errors:781    //   a. when a fix has been applied for an error782    //   b. when a fix has been applied for all errors783    //   c. some other condition.784    // For now always returning zero when -fix-errors is used.785    if (FixErrors)786      return 0;787    if (!Quiet)788      llvm::errs() << "Found compiler error(s).\n";789    return 1;790  }791 792  return 0;793}794 795} // namespace clang::tidy796