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