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