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