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