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 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
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
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
82 Checks: '-*,some-check'
84 HeaderFileExtensions: ['', 'h','hh','hpp','hxx']
85 ImplementationFileExtensions: ['c','cc','cpp','cxx']
88 InheritParentConfig: true
91 some-check.SomeOption: 'some value'
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
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
116 This option's value is appended to the value of
117 the 'WarningsAsErrors' option in .clang-tidy
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
128 Can be used together with -line-filter.
129 This option overrides the 'HeaderFilterRegex'
130 option in .clang-tidy file, if any.
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.
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]]},
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
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
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,
174 Specifying this flag will implicitly enable the
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
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.
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.
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.
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.
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.
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
236 Use along with -checks=* to include
237 configuration of all checks.
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
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.
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.
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",
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.
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.
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.
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.
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.
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.
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";
323 if (Stats
.ErrorsIgnoredLineFilter
) {
324 llvm::errs() << Separator
<< Stats
.ErrorsIgnoredLineFilter
325 << " due to line filter";
328 if (Stats
.ErrorsIgnoredNOLINT
) {
329 llvm::errs() << Separator
<< Stats
.ErrorsIgnoredNOLINT
<< " NOLINT";
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);
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
;
378 [&](StringRef Configuration
,
379 StringRef Source
) -> std::unique_ptr
<ClangTidyOptionsProvider
> {
380 llvm::ErrorOr
<ClangTidyOptions
> ParsedConfig
=
381 parseConfiguration(MemoryBufferRef(Configuration
, Source
));
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";
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";
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";
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
);
424 llvm::errs() << "Can't load virtual filesystem overlay file '"
425 << OverlayFile
<< "': " << Buffer
.getError().message()
430 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getVFSFromYAML(
431 std::move(Buffer
.get()), /*DiagHandler*/ nullptr, OverlayFile
);
433 llvm::errs() << "Error: invalid virtual filesystem overlay file '"
434 << OverlayFile
<< "'.\n";
440 static StringRef
closest(StringRef Value
, const StringSet
<> &Allowed
) {
441 unsigned MaxEdit
= 5U;
443 for (auto Item
: Allowed
.keys()) {
444 unsigned Cur
= Value
.edit_distance_insensitive(Item
, true, MaxEdit
);
453 static constexpr StringLiteral VerifyConfigWarningEnd
= " [-verify-config]\n";
455 static bool verifyChecks(const StringSet
<> &AllChecks
, StringRef CheckGlob
,
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(',')) {
464 Cur
.consume_front("-");
465 if (Cur
.startswith("clang-diagnostic"))
467 if (Cur
.contains('*')) {
468 SmallString
<128> RegexText("^");
469 StringRef
MetaChars("()^$|*+?.[]\\{}");
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
);
480 if (!Glob
.isValid(Error
)) {
482 llvm::WithColor::error(llvm::errs(), Source
)
483 << "building check glob '" << Cur
<< "' " << Error
<< "'\n";
486 if (llvm::none_of(AllChecks
.keys(),
487 [&Glob
](StringRef S
) { return Glob
.match(S
); })) {
489 llvm::WithColor::warning(llvm::errs(), Source
)
490 << "check glob '" << Cur
<< "' doesn't match any known check"
491 << VerifyConfigWarningEnd
;
494 if (AllChecks
.contains(Cur
))
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
;
508 static bool verifyFileExtensions(
509 const std::vector
<std::string
> &HeaderFileExtensions
,
510 const std::vector
<std::string
> &ImplementationFileExtensions
,
512 bool AnyInvalid
= false;
513 for (const auto &HeaderExtension
: HeaderFileExtensions
) {
514 for (const auto &ImplementationExtension
: ImplementationFileExtensions
) {
515 if (HeaderExtension
== ImplementationExtension
) {
517 auto &Output
= llvm::WithColor::warning(llvm::errs(), Source
)
518 << "HeaderFileExtension '" << HeaderExtension
<< '\''
519 << " is the same as ImplementationFileExtension '"
520 << ImplementationExtension
<< '\'';
521 Output
<< VerifyConfigWarningEnd
;
528 static SmallString
<256> makeAbsolute(llvm::StringRef Input
) {
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";
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
);
548 BaseFS
->pushOverlay(std::move(VfsFromFile
));
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
,
563 if (!OptionsParser
) {
564 llvm::WithColor::error() << llvm::toString(OptionsParser
.takeError());
568 llvm::IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> BaseFS
= createBaseFS();
572 auto OwningOptionsProvider
= createOptionsProvider(BaseFS
);
573 auto *OptionsProvider
= OwningOptionsProvider
.get();
574 if (!OptionsProvider
)
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
);
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
608 if (EnabledChecks
.empty()) {
609 llvm::errs() << "No checks enabled.\n";
612 llvm::outs() << "Enabled checks:";
613 for (const auto &CheckName
: EnabledChecks
)
614 llvm::outs() << "\n " << CheckName
;
615 llvm::outs() << "\n\n";
620 EffectiveOptions
.CheckOptions
=
621 getCheckOptions(EffectiveOptions
, AllowEnablingAnalyzerAlphaCheckers
);
622 llvm::outs() << configurationAsText(ClangTidyOptions::getDefaults().merge(
623 EffectiveOptions
, 0))
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
) {
636 AnyInvalid
|= verifyChecks(Valid
.Names
, *Opts
.Checks
, Source
);
638 if (Opts
.HeaderFileExtensions
&& Opts
.ImplementationFileExtensions
)
640 verifyFileExtensions(*Opts
.HeaderFileExtensions
,
641 *Opts
.ImplementationFileExtensions
, Source
);
643 for (auto Key
: Opts
.CheckOptions
.keys()) {
644 if (Valid
.Options
.contains(Key
))
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
;
657 llvm::outs() << "No config errors detected.\n";
661 if (EnabledChecks
.empty()) {
662 llvm::errs() << "Error: no checks enabled.\n";
663 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
667 if (PathList
.empty()) {
668 llvm::errs() << "Error: no input files specified.\n";
669 llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
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
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()) {
701 llvm::raw_fd_ostream
OS(ExportFixes
, EC
, llvm::sys::fs::OF_None
);
703 llvm::errs() << "Error opening output file: " << EC
.message() << '\n';
706 exportReplacements(FilePath
.str(), Errors
, OS
);
710 printStats(Context
.getStats());
711 if (DisableFixes
&& Behaviour
!= FB_NoFix
)
713 << "Found compiler errors, but -fix-errors was not specified.\n"
714 "Fixes have NOT been applied.\n\n";
719 StringRef Plural
= WErrorCount
== 1 ? "" : "s";
720 llvm::errs() << WErrorCount
<< " warning" << Plural
<< " treated as error"
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.
735 llvm::errs() << "Found compiler error(s).\n";
742 } // namespace clang::tidy