1 //===--- tools/extra/clang-tidy/ClangTidyMain.cpp - Clang tidy tool -------===//
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
7 //===----------------------------------------------------------------------===//
9 /// \file This file implements a clang-tidy tool.
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.
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"
31 using namespace clang::tooling
;
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
"(
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
47 The following configuration options may be used in a .clang-tidy file:
49 CheckOptions - List of key-value pairs defining check-specific
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
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
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
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
83 Checks: '-*,some-check'
85 HeaderFileExtensions: ['', 'h','hh','hpp','hxx']
86 ImplementationFileExtensions: ['c','cc','cpp','cxx']
89 InheritParentConfig: true
92 some-check.SomeOption: 'some value'
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
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
117 This option's value is appended to the value of
118 the 'WarningsAsErrors' option in .clang-tidy
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
129 Can be used together with -line-filter.
130 This option overrides the 'HeaderFilterRegex'
131 option in .clang-tidy file, if any.
134 cl::cat(ClangTidyCategory
));
136 static cl::opt
<std::string
> ExcludeHeaderFilter("exclude-header-filter",
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
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.
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.
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]]},
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
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
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,
189 Specifying this flag will implicitly enable the
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
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.
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.
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.
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.
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.
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
251 Use along with -checks=* to include
252 configuration of all checks.
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
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.
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.
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",
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.
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.
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.
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.
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.
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.
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
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";
346 if (Stats
.ErrorsIgnoredLineFilter
) {
347 llvm::errs() << Separator
<< Stats
.ErrorsIgnoredLineFilter
348 << " due to line filter";
351 if (Stats
.ErrorsIgnoredNOLINT
) {
352 llvm::errs() << Separator
<< Stats
.ErrorsIgnoredNOLINT
<< " NOLINT";
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);
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
;
404 [&](StringRef Configuration
,
405 StringRef Source
) -> std::unique_ptr
<ClangTidyOptionsProvider
> {
406 llvm::ErrorOr
<ClangTidyOptions
> ParsedConfig
=
407 parseConfiguration(MemoryBufferRef(Configuration
, Source
));
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";
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";
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";
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
);
450 llvm::errs() << "Can't load virtual filesystem overlay file '"
451 << OverlayFile
<< "': " << Buffer
.getError().message()
456 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getVFSFromYAML(
457 std::move(Buffer
.get()), /*DiagHandler*/ nullptr, OverlayFile
);
459 llvm::errs() << "Error: invalid virtual filesystem overlay file '"
460 << OverlayFile
<< "'.\n";
466 static StringRef
closest(StringRef Value
, const StringSet
<> &Allowed
) {
467 unsigned MaxEdit
= 5U;
469 for (auto Item
: Allowed
.keys()) {
470 unsigned Cur
= Value
.edit_distance_insensitive(Item
, true, MaxEdit
);
479 static constexpr StringLiteral VerifyConfigWarningEnd
= " [-verify-config]\n";
481 static bool verifyChecks(const StringSet
<> &AllChecks
, StringRef CheckGlob
,
483 GlobList
Globs(CheckGlob
);
484 bool AnyInvalid
= false;
485 for (const auto &Item
: Globs
.getItems()) {
486 if (Item
.Text
.starts_with("clang-diagnostic"))
488 if (llvm::none_of(AllChecks
.keys(),
489 [&Item
](StringRef S
) { return Item
.Regex
.match(S
); })) {
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
;
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
;
509 static bool verifyFileExtensions(
510 const std::vector
<std::string
> &HeaderFileExtensions
,
511 const std::vector
<std::string
> &ImplementationFileExtensions
,
513 bool AnyInvalid
= false;
514 for (const auto &HeaderExtension
: HeaderFileExtensions
) {
515 for (const auto &ImplementationExtension
: ImplementationFileExtensions
) {
516 if (HeaderExtension
== ImplementationExtension
) {
518 auto &Output
= llvm::WithColor::warning(llvm::errs(), Source
)
519 << "HeaderFileExtension '" << HeaderExtension
<< '\''
520 << " is the same as ImplementationFileExtension '"
521 << ImplementationExtension
<< '\'';
522 Output
<< VerifyConfigWarningEnd
;
529 static SmallString
<256> makeAbsolute(llvm::StringRef Input
) {
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";
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
);
549 BaseFS
->pushOverlay(std::move(VfsFromFile
));
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
,
564 if (!OptionsParser
) {
565 llvm::WithColor::error() << llvm::toString(OptionsParser
.takeError());
569 llvm::IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> BaseFS
= createBaseFS();
573 auto OwningOptionsProvider
= createOptionsProvider(BaseFS
);
574 auto *OptionsProvider
= OwningOptionsProvider
.get();
575 if (!OptionsProvider
)
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
);
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
609 if (EnabledChecks
.empty() && !AllowNoChecks
) {
610 llvm::errs() << "No checks enabled.\n";
613 llvm::outs() << "Enabled checks:";
614 for (const auto &CheckName
: EnabledChecks
)
615 llvm::outs() << "\n " << CheckName
;
616 llvm::outs() << "\n\n";
621 EffectiveOptions
.CheckOptions
=
622 getCheckOptions(EffectiveOptions
, AllowEnablingAnalyzerAlphaCheckers
);
623 llvm::outs() << configurationAsText(ClangTidyOptions::getDefaults().merge(
624 EffectiveOptions
, 0))
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
) {
637 AnyInvalid
|= verifyChecks(Valid
.Names
, *Opts
.Checks
, Source
);
639 if (Opts
.HeaderFileExtensions
&& Opts
.ImplementationFileExtensions
)
641 verifyFileExtensions(*Opts
.HeaderFileExtensions
,
642 *Opts
.ImplementationFileExtensions
, Source
);
644 for (auto Key
: Opts
.CheckOptions
.keys()) {
645 if (Valid
.Options
.contains(Key
))
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
;
658 llvm::outs() << "No config errors detected.\n";
662 if (EnabledChecks
.empty()) {
664 llvm::outs() << "No checks enabled.\n";
667 llvm::errs() << "Error: no checks enabled.\n";
668 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
672 if (PathList
.empty()) {
673 llvm::errs() << "Error: no input files specified.\n";
674 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
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
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()) {
706 llvm::raw_fd_ostream
OS(ExportFixes
, EC
, llvm::sys::fs::OF_None
);
708 llvm::errs() << "Error opening output file: " << EC
.message() << '\n';
711 exportReplacements(FilePath
.str(), Errors
, OS
);
715 printStats(Context
.getStats());
716 if (DisableFixes
&& Behaviour
!= FB_NoFix
)
718 << "Found compiler errors, but -fix-errors was not specified.\n"
719 "Fixes have NOT been applied.\n\n";
724 StringRef Plural
= WErrorCount
== 1 ? "" : "s";
725 llvm::errs() << WErrorCount
<< " warning" << Plural
<< " treated as error"
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.
740 llvm::errs() << "Found compiler error(s).\n";
747 } // namespace clang::tidy