clang-tools 20.0.0git
ClangTidyMain.cpp
Go to the documentation of this file.
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/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>
30
31using namespace clang::tooling;
32using namespace llvm;
33
34static cl::desc desc(StringRef description) { return {description.ltrim()}; }
35
36static cl::OptionCategory ClangTidyCategory("clang-tidy options");
37
38static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
39static cl::extrahelp ClangTidyHelp(R"(
40Configuration 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.
46
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'.
78
79 The effective configuration can be inspected using --dump-config:
80
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)");
96
97const char DefaultChecks[] = // Enable these checks by default:
98 "clang-diagnostic-*," // * compiler diagnostics
99 "clang-analyzer-*"; // * Static Analyzer checks
100
101static cl::opt<std::string> Checks("checks", desc(R"(
102Comma-separated list of globs with optional '-'
103prefix. Globs are processed in order of
104appearance in the list. Globs without '-'
105prefix add checks with matching names to the
106set, globs with the '-' prefix remove checks
107with matching names from the set of enabled
108checks. This option's value is appended to the
109value of the 'Checks' option in .clang-tidy
110file, if any.
111)"),
112 cl::init(""), cl::cat(ClangTidyCategory));
114static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", desc(R"(
115Upgrades warnings to errors. Same format as
116'-checks'.
117This option's value is appended to the value of
118the 'WarningsAsErrors' option in .clang-tidy
119file, if any.
120)"),
121 cl::init(""),
123
124static cl::opt<std::string> HeaderFilter("header-filter", desc(R"(
125Regular expression matching the names of the
126headers to output diagnostics from. Diagnostics
127from the main file of each translation unit are
128always displayed.
129Can be used together with -line-filter.
130This option overrides the 'HeaderFilterRegex'
131option in .clang-tidy file, if any.
132)"),
133 cl::init(""),
134 cl::cat(ClangTidyCategory));
136static cl::opt<std::string> ExcludeHeaderFilter("exclude-header-filter",
137 desc(R"(
138Regular expression matching the names of the
139headers to exclude diagnostics from. Diagnostics
140from the main file of each translation unit are
141always displayed.
142Must be used together with --header-filter.
143Can be used together with -line-filter.
144This option overrides the 'ExcludeHeaderFilterRegex'
145option in .clang-tidy file, if any.
146)"),
147 cl::init(""),
148 cl::cat(ClangTidyCategory));
149
150static cl::opt<bool> SystemHeaders("system-headers", desc(R"(
151Display the errors from system headers.
152This option overrides the 'SystemHeaders' option
153in .clang-tidy file, if any.
154)"),
155 cl::init(false), cl::cat(ClangTidyCategory));
156
157static cl::opt<std::string> LineFilter("line-filter", desc(R"(
158List of files with line ranges to filter the
159warnings. Can be used together with
160-header-filter. The format of the list is a
161JSON array of objects:
162 [
163 {"name":"file1.cpp","lines":[[1,3],[5,7]]},
164 {"name":"file2.h"}
165 ]
166)"),
167 cl::init(""),
168 cl::cat(ClangTidyCategory));
170static cl::opt<bool> Fix("fix", desc(R"(
171Apply suggested fixes. Without -fix-errors
172clang-tidy will bail out if any compilation
173errors were found.
174)"),
175 cl::init(false), cl::cat(ClangTidyCategory));
176
177static cl::opt<bool> FixErrors("fix-errors", desc(R"(
178Apply suggested fixes even if compilation
179errors were found. If compiler errors have
180attached fix-its, clang-tidy will apply them as
181well.
182)"),
183 cl::init(false), cl::cat(ClangTidyCategory));
184
185static cl::opt<bool> FixNotes("fix-notes", desc(R"(
186If a warning has no fix, but a single fix can
187be found through an associated diagnostic note,
188apply the fix.
189Specifying this flag will implicitly enable the
190'--fix' flag.
191)"),
192 cl::init(false), cl::cat(ClangTidyCategory));
193
194static cl::opt<std::string> FormatStyle("format-style", desc(R"(
195Style 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'
203See clang-format documentation for the up-to-date
204information about formatting styles and options.
205This option overrides the 'FormatStyle` option in
206.clang-tidy file, if any.
207)"),
208 cl::init("none"),
209 cl::cat(ClangTidyCategory));
210
211static cl::opt<bool> ListChecks("list-checks", desc(R"(
212List all enabled checks and exit. Use with
213-checks=* to list all available checks.
214)"),
215 cl::init(false), cl::cat(ClangTidyCategory));
216
217static cl::opt<bool> ExplainConfig("explain-config", desc(R"(
218For each enabled check explains, where it is
219enabled, i.e. in clang-tidy binary, command
220line or a specific configuration file.
221)"),
222 cl::init(false), cl::cat(ClangTidyCategory));
223
224static cl::opt<std::string> Config("config", desc(R"(
225Specifies a configuration in YAML/JSON format:
226 -config="{Checks: '*',
227 CheckOptions: {x: y}}"
228When the value is empty, clang-tidy will
229attempt to find a file named .clang-tidy for
230each source file in its parent directories.
231)"),
232 cl::init(""), cl::cat(ClangTidyCategory));
233
234static cl::opt<std::string> ConfigFile("config-file", desc(R"(
235Specify the path of .clang-tidy or custom config file:
236 e.g. --config-file=/some/path/myTidyConfigFile
237This option internally works exactly the same way as
238 --config option after reading specified config file.
239Use either --config-file or --config, not both.
240)"),
241 cl::init(""),
242 cl::cat(ClangTidyCategory));
243
244static cl::opt<bool> DumpConfig("dump-config", desc(R"(
245Dumps configuration in the YAML format to
246stdout. This option can be used along with a
247file name (and '--' if the file is outside of a
248project with configured compilation database).
249The configuration used for this file will be
250printed.
251Use along with -checks=* to include
252configuration of all checks.
253)"),
254 cl::init(false), cl::cat(ClangTidyCategory));
255
256static cl::opt<bool> EnableCheckProfile("enable-check-profile", desc(R"(
257Enable per-check timing profiles, and print a
258report to stderr.
259)"),
260 cl::init(false),
261 cl::cat(ClangTidyCategory));
262
263static cl::opt<std::string> StoreCheckProfile("store-check-profile", desc(R"(
264By default reports are printed in tabulated
265format to stderr. When this option is passed,
266these per-TU profiles are instead stored as JSON.
267)"),
268 cl::value_desc("prefix"),
270
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.
274static cl::opt<bool>
275 AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers",
276 cl::init(false), cl::Hidden,
277 cl::cat(ClangTidyCategory));
278
279static cl::opt<bool> EnableModuleHeadersParsing("enable-module-headers-parsing",
280 desc(R"(
281Enables preprocessor-level module header parsing
282for C++20 and above, empowering specific checks
283to detect macro definitions within modules. This
284feature may cause performance and parsing issues
285and is therefore considered experimental.
286)"),
287 cl::init(false),
288 cl::cat(ClangTidyCategory));
289
290static cl::opt<std::string> ExportFixes("export-fixes", desc(R"(
291YAML file to store suggested fixes in. The
292stored fixes can be applied to the input source
293code with clang-apply-replacements.
294)"),
295 cl::value_desc("filename"),
296 cl::cat(ClangTidyCategory));
297
298static cl::opt<bool> Quiet("quiet", desc(R"(
299Run clang-tidy in quiet mode. This suppresses
300printing statistics about ignored warnings and
301warnings treated as errors if the respective
302options are specified.
303)"),
304 cl::init(false), cl::cat(ClangTidyCategory));
305
306static cl::opt<std::string> VfsOverlay("vfsoverlay", desc(R"(
307Overlay the virtual filesystem described by file
308over the real file system.
309)"),
310 cl::value_desc("filename"),
311 cl::cat(ClangTidyCategory));
313static cl::opt<bool> UseColor("use-color", desc(R"(
314Use colors in diagnostics. If not set, colors
315will be used if the terminal connected to
316standard output supports colors.
317This option overrides the 'UseColor' option in
318.clang-tidy file, if any.
319)"),
320 cl::init(false), cl::cat(ClangTidyCategory));
321
322static cl::opt<bool> VerifyConfig("verify-config", desc(R"(
323Check the config files to ensure each check and
324option is recognized.
325)"),
326 cl::init(false), cl::cat(ClangTidyCategory));
327
328static cl::opt<bool> AllowNoChecks("allow-no-checks", desc(R"(
329Allow empty enabled checks. This suppresses
330the "no checks enabled" error when disabling
331all of the checks.
332)"),
333 cl::init(false),
334 cl::cat(ClangTidyCategory));
335
336namespace clang::tidy {
337
338static 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 = ", ";
345 }
346 if (Stats.ErrorsIgnoredLineFilter) {
347 llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter
348 << " due to line filter";
349 Separator = ", ";
350 }
351 if (Stats.ErrorsIgnoredNOLINT) {
352 llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
353 Separator = ", ";
354 }
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";
363 }
364}
365
366static 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;
373 }
374
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");
386
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;
402
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;
416 };
417
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;
423 }
424
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;
431 }
432
433 return LoadConfig((*Text)->getBuffer(), ConfigFile);
434 }
435
436 if (Config.getNumOccurrences() > 0)
437 return LoadConfig(Config, "<command-line-config>");
438
439 return std::make_unique<FileOptionsProvider>(
440 std::move(GlobalOptions), std::move(DefaultOptions),
441 std::move(OverrideOptions), std::move(FS));
442}
443
444llvm::IntrusiveRefCntPtr<vfs::FileSystem>
445getVfsFromFile(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;
454 }
455
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;
462 }
463 return FS;
464}
465
466static 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;
474 }
475 }
476 return Closest;
477}
478
479static constexpr StringLiteral VerifyConfigWarningEnd = " [-verify-config]\n";
480
481static 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"
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 << '\'';
503 }
504 }
505 }
506 return AnyInvalid;
507}
508
509static 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 << '\'';
523 }
524 }
525 }
526 return AnyInvalid;
527}
528
529static 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";
536 }
537 return AbsolutePath;
538}
539
540static llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> createBaseFS() {
541 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS(
542 new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
543
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));
550 }
551 return BaseFS;
552}
553
554int clangTidyMain(int argc, const char **argv) {
555 llvm::InitLLVM X(argc, argv);
556
557 // Enable help for -load option, if plugins are enabled.
558 if (cl::Option *LoadOpt = cl::getRegisteredOptions().lookup("load"))
559 LoadOpt->addCategory(ClangTidyCategory);
560
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;
567 }
568
569 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS = createBaseFS();
570 if (!BaseFS)
571 return 1;
572
573 auto OwningOptionsProvider = createOptionsProvider(BaseFS);
574 auto *OptionsProvider = OwningOptionsProvider.get();
575 if (!OptionsProvider)
576 return 1;
577
578 SmallString<256> ProfilePrefix = makeAbsolute(StoreCheckProfile);
579
580 StringRef FileName("dummy");
581 auto PathList = OptionsParser->getSourcePathList();
582 if (!PathList.empty()) {
583 FileName = PathList.front();
584 }
585
586 SmallString<256> FilePath = makeAbsolute(FileName);
587 ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
588
589 std::vector<std::string> EnabledChecks =
591
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;
602 }
603 }
604 }
605 return 0;
606 }
607
608 if (ListChecks) {
609 if (EnabledChecks.empty() && !AllowNoChecks) {
610 llvm::errs() << "No checks enabled.\n";
611 return 1;
612 }
613 llvm::outs() << "Enabled checks:";
614 for (const auto &CheckName : EnabledChecks)
615 llvm::outs() << "\n " << CheckName;
616 llvm::outs() << "\n\n";
617 return 0;
618 }
619
620 if (DumpConfig) {
621 EffectiveOptions.CheckOptions =
624 EffectiveOptions, 0))
625 << "\n";
626 return 0;
627 }
628
629 if (VerifyConfig) {
630 std::vector<ClangTidyOptionsProvider::OptionsSource> RawOptions =
631 OptionsProvider->getRawOptions(FileName);
632 NamesAndOptions Valid =
634 bool AnyInvalid = false;
635 for (const auto &[Opts, Source] : RawOptions) {
636 if (Opts.Checks)
637 AnyInvalid |= verifyChecks(Valid.Names, *Opts.Checks, Source);
638
639 if (Opts.HeaderFileExtensions && Opts.ImplementationFileExtensions)
640 AnyInvalid |=
641 verifyFileExtensions(*Opts.HeaderFileExtensions,
642 *Opts.ImplementationFileExtensions, Source);
643
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 << '\'';
654 }
655 }
656 if (AnyInvalid)
657 return 1;
658 llvm::outs() << "No config errors detected.\n";
659 return 0;
660 }
661
662 if (EnabledChecks.empty()) {
663 if (AllowNoChecks) {
664 llvm::outs() << "No checks enabled.\n";
665 return 0;
666 }
667 llvm::errs() << "Error: no checks enabled.\n";
668 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
669 return 1;
670 }
671
672 if (PathList.empty()) {
673 llvm::errs() << "Error: no input files specified.\n";
674 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
675 return 1;
676 }
677
678 llvm::InitializeAllTargetInfos();
679 llvm::InitializeAllTargetMCs();
680 llvm::InitializeAllAsmParsers();
681
682 ClangTidyContext Context(std::move(OwningOptionsProvider),
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;
690 });
691
692 // --fix-errors and --fix-notes imply --fix.
693 FixBehaviour Behaviour = FixNotes ? FB_FixNotes
694 : (Fix || FixErrors) ? FB_Fix
695 : FB_NoFix;
696
697 const bool DisableFixes = FoundErrors && !FixErrors;
698
699 unsigned WErrorCount = 0;
700
701 handleErrors(Errors, Context, DisableFixes ? FB_NoFix : Behaviour,
702 WErrorCount, BaseFS);
703
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;
710 }
711 exportReplacements(FilePath.str(), Errors, OS);
712 }
713
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";
720 }
721
722 if (WErrorCount) {
723 if (!Quiet) {
724 StringRef Plural = WErrorCount == 1 ? "" : "s";
725 llvm::errs() << WErrorCount << " warning" << Plural << " treated as error"
726 << Plural << "\n";
727 }
728 return 1;
729 }
730
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;
742 }
743
744 return 0;
745}
746
747} // namespace clang::tidy
const Expr * E
static cl::opt< bool > UseColor("use-color", cl::desc(R"(Use colors in detailed AST output. If not set, colors will be used if the terminal connected to standard output supports colors.)"), cl::init(false), cl::cat(ClangQueryCategory))
static cl::opt< bool > EnableCheckProfile("enable-check-profile", desc(R"( Enable per-check timing profiles, and print a report to stderr. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > VfsOverlay("vfsoverlay", desc(R"( Overlay the virtual filesystem described by file over the real file system. )"), cl::value_desc("filename"), cl::cat(ClangTidyCategory))
static cl::opt< bool > FixNotes("fix-notes", desc(R"( If a warning has no fix, but a single fix can be found through an associated diagnostic note, apply the fix. Specifying this flag will implicitly enable the '--fix' flag. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > Fix("fix", desc(R"( Apply suggested fixes. Without -fix-errors clang-tidy will bail out if any compilation errors were found. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > ExplainConfig("explain-config", desc(R"( For each enabled check explains, where it is enabled, i.e. in clang-tidy binary, command line or a specific configuration file. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > UseColor("use-color", desc(R"( Use colors in diagnostics. If not set, colors will be used if the terminal connected to standard output supports colors. This option overrides the 'UseColor' option in .clang-tidy file, if any. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > VerifyConfig("verify-config", desc(R"( Check the config files to ensure each check and option is recognized. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > ExcludeHeaderFilter("exclude-header-filter", desc(R"( Regular expression matching the names of the headers to exclude diagnostics from. Diagnostics from the main file of each translation unit are always displayed. Must be used together with --header-filter. Can be used together with -line-filter. This option overrides the 'ExcludeHeaderFilterRegex' option in .clang-tidy file, if any. )"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< bool > AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers", cl::init(false), cl::Hidden, cl::cat(ClangTidyCategory))
This option allows enabling the experimental alpha checkers from the static analyzer.
static cl::opt< std::string > ConfigFile("config-file", desc(R"( Specify the path of .clang-tidy or custom config file: e.g. --config-file=/some/path/myTidyConfigFile This option internally works exactly the same way as --config option after reading specified config file. Use either --config-file or --config, not both. )"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< bool > EnableModuleHeadersParsing("enable-module-headers-parsing", desc(R"( Enables preprocessor-level module header parsing for C++20 and above, empowering specific checks to detect macro definitions within modules. This feature may cause performance and parsing issues and is therefore considered experimental. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Config("config", desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:{x:y}}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::OptionCategory ClangTidyCategory("clang-tidy options")
static cl::opt< std::string > LineFilter("line-filter", desc(R"( List of files with line ranges to filter the warnings. Can be used together with -header-filter. The format of the list is a JSON array of objects: [ {"name":"file1.cpp","lines":[[1,3],[5,7]]}, {"name":"file2.h"} ] )"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< bool > FixErrors("fix-errors", desc(R"( Apply suggested fixes even if compilation errors were found. If compiler errors have attached fix-its, clang-tidy will apply them as well. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::extrahelp ClangTidyHelp(R"( Configuration files: clang-tidy attempts to read configuration for each source file from a .clang-tidy file located in the closest parent directory of the source file. The .clang-tidy file is specified in YAML format. If any configuration options have a corresponding command-line option, command-line option takes precedence. The following configuration options may be used in a .clang-tidy file: CheckOptions - List of key-value pairs defining check-specific options. Example: CheckOptions: some-check.SomeOption: 'some value' Checks - Same as '--checks'. Additionally, the list of globs can be specified as a list instead of a string. ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'. ExtraArgs - Same as '--extra-args'. ExtraArgsBefore - Same as '--extra-args-before'. FormatStyle - Same as '--format-style'. HeaderFileExtensions - File extensions to consider to determine if a given diagnostic is located in a header file. HeaderFilterRegex - Same as '--header-filter-regex'. ImplementationFileExtensions - File extensions to consider to determine if a given diagnostic is located in an implementation file. InheritParentConfig - If this option is true in a config file, the configuration file in the parent directory (if any exists) will be taken and the current config file will be applied on top of the parent one. SystemHeaders - Same as '--system-headers'. UseColor - Same as '--use-color'. User - Specifies the name or e-mail of the user running clang-tidy. This option is used, for example, to place the correct user name in TODO() comments in the relevant check. WarningsAsErrors - Same as '--warnings-as-errors'. The effective configuration can be inspected using --dump-config: $ clang-tidy --dump-config --- Checks: '-*,some-check' WarningsAsErrors: '' HeaderFileExtensions: ['', 'h','hh','hpp','hxx'] ImplementationFileExtensions: ['c','cc','cpp','cxx'] HeaderFilterRegex: '' FormatStyle: none InheritParentConfig: true User: user CheckOptions: some-check.SomeOption: 'some value' ... )")
static cl::opt< std::string > HeaderFilter("header-filter", desc(R"( Regular expression matching the names of the headers to output diagnostics from. Diagnostics from the main file of each translation unit are always displayed. Can be used together with -line-filter. This option overrides the 'HeaderFilterRegex' option in .clang-tidy file, if any. )"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< bool > ListChecks("list-checks", desc(R"( List all enabled checks and exit. Use with -checks=* to list all available checks. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > DumpConfig("dump-config", desc(R"( Dumps configuration in the YAML format to stdout. This option can be used along with a file name (and '--' if the file is outside of a project with configured compilation database). The configuration used for this file will be printed. Use along with -checks=* to include configuration of all checks. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< bool > SystemHeaders("system-headers", desc(R"( Display the errors from system headers. This option overrides the 'SystemHeaders' option in .clang-tidy file, if any. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::desc desc(StringRef description)
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage)
static cl::opt< bool > AllowNoChecks("allow-no-checks", desc(R"( Allow empty enabled checks. This suppresses the "no checks enabled" error when disabling all of the checks. )"), cl::init(false), cl::cat(ClangTidyCategory))
const char DefaultChecks[]
static cl::opt< bool > Quiet("quiet", desc(R"( Run clang-tidy in quiet mode. This suppresses printing statistics about ignored warnings and warnings treated as errors if the respective options are specified. )"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > ExportFixes("export-fixes", desc(R"( YAML file to store suggested fixes in. The stored fixes can be applied to the input source code with clang-apply-replacements. )"), cl::value_desc("filename"), cl::cat(ClangTidyCategory))
static cl::opt< std::string > FormatStyle("format-style", desc(R"( Style for formatting code around applied fixes: - 'none' (default) turns off formatting - 'file' (literally 'file', not a placeholder) uses .clang-format file in the closest parent directory - '{ <json> }' specifies options inline, e.g. -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - 'llvm', 'google', 'webkit', 'mozilla' See clang-format documentation for the up-to-date information about formatting styles and options. This option overrides the 'FormatStyle` option in .clang-tidy file, if any. )"), cl::init("none"), cl::cat(ClangTidyCategory))
static cl::opt< std::string > StoreCheckProfile("store-check-profile", desc(R"( By default reports are printed in tabulated format to stderr. When this option is passed, these per-TU profiles are instead stored as JSON. )"), cl::value_desc("prefix"), cl::cat(ClangTidyCategory))
int Errors
llvm::raw_ostream & OS
std::string Text
StringRef FileName
int X
std::string Output
Definition: TraceTests.cpp:159
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
Definition: TestIndex.cpp:151
std::error_code parseLineFilter(StringRef LineFilter, clang::tidy::ClangTidyGlobalOptions &Options)
Parses -line-filter option and stores it to the Options.
std::vector< std::string > getCheckNames(const ClangTidyOptions &Options, bool AllowEnablingAnalyzerAlphaCheckers)
Fills the list of check names that are enabled when the provided filters are applied.
Definition: ClangTidy.cpp:501
FixBehaviour
Controls what kind of fixes clang-tidy is allowed to apply.
Definition: ClangTidy.h:95
@ FB_NoFix
Don't try to apply any fix.
Definition: ClangTidy.h:97
@ FB_FixNotes
Apply fixes found in notes.
Definition: ClangTidy.h:101
@ FB_Fix
Only apply fixes added to warnings.
Definition: ClangTidy.h:99
int clangTidyMain(int argc, const char **argv)
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options, bool AllowEnablingAnalyzerAlphaCheckers)
Returns the effective check-specific options.
Definition: ClangTidy.cpp:512
static void printStats(const ClangTidyStats &Stats)
static llvm::IntrusiveRefCntPtr< vfs::OverlayFileSystem > createBaseFS()
llvm::IntrusiveRefCntPtr< vfs::FileSystem > getVfsFromFile(const std::string &OverlayFile, llvm::IntrusiveRefCntPtr< vfs::FileSystem > BaseFS)
void handleErrors(llvm::ArrayRef< ClangTidyError > Errors, ClangTidyContext &Context, FixBehaviour Fix, unsigned &WarningsAsErrorsCount, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > BaseFS)
Displays the found Errors to the users.
Definition: ClangTidy.cpp:606
static constexpr StringLiteral VerifyConfigWarningEnd
static SmallString< 256 > makeAbsolute(llvm::StringRef Input)
static std::unique_ptr< ClangTidyOptionsProvider > createOptionsProvider(llvm::IntrusiveRefCntPtr< vfs::FileSystem > FS)
std::vector< ClangTidyError > runClangTidy(clang::tidy::ClangTidyContext &Context, const CompilationDatabase &Compilations, ArrayRef< std::string > InputFiles, llvm::IntrusiveRefCntPtr< llvm::vfs::OverlayFileSystem > BaseFS, bool ApplyAnyFix, bool EnableCheckProfile, llvm::StringRef StoreCheckProfile)
Definition: ClangTidy.cpp:528
static bool verifyFileExtensions(const std::vector< std::string > &HeaderFileExtensions, const std::vector< std::string > &ImplementationFileExtensions, StringRef Source)
NamesAndOptions getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers)
Definition: ClangTidy.cpp:650
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
llvm::ErrorOr< ClangTidyOptions > parseConfiguration(llvm::MemoryBufferRef Config)
Parses configuration from JSON and returns ClangTidyOptions or an error.
void exportReplacements(const llvm::StringRef MainFilePath, const std::vector< ClangTidyError > &Errors, raw_ostream &OS)
Definition: ClangTidy.cpp:633
static bool verifyChecks(const StringSet<> &AllChecks, StringRef CheckGlob, StringRef Source)
static StringRef closest(StringRef Value, const StringSet<> &Allowed)
Some operations such as code completion produce a set of candidates.
Definition: Generators.h:58
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
Contains displayed and ignored diagnostic counters for a ClangTidy run.