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"
35 static cl::extrahelp
CommonHelp(CommonOptionsParser::HelpMessage);
38 clang-tidy attempts to read configuration for each source file from a
39 .clang-tidy file located in the closest parent directory of the source
40 file. If InheritParentConfig is true in a config file, the configuration file
41 in the parent directory (if any exists) will be taken and current config file
42 will be applied on top of the parent one. If any configuration options have
43 a corresponding command-line option, command-line option takes precedence.
44 The effective configuration can be inspected using -dump-config:
46 $ clang-tidy -dump-config
48 Checks: '-*,some-check'
52 InheritParentConfig: true
55 some-check.SomeOption: 'some value'
64 static cl::opt<std::string>
Checks(
"checks", cl::desc(R
"(
65 Comma-separated list of globs with optional '-'
66 prefix. Globs are processed in order of
67 appearance in the list. Globs without '-'
68 prefix add checks with matching names to the
69 set, globs with the '-' prefix remove checks
70 with matching names from the set of enabled
71 checks. This option's value is appended to the
72 value of the 'Checks' option in .clang-tidy
77 static cl::opt<std::string>
WarningsAsErrors(
"warnings-as-errors", cl::desc(R
"(
78 Upgrades warnings to errors. Same format as
80 This option's value is appended to the value of
81 the 'WarningsAsErrors' option in .clang-tidy
87 static cl::opt<std::string>
HeaderFilter(
"header-filter", cl::desc(R
"(
88 Regular expression matching the names of the
89 headers to output diagnostics from. Diagnostics
90 from the main file of each translation unit are
92 Can be used together with -line-filter.
93 This option overrides the 'HeaderFilterRegex'
94 option in .clang-tidy file, if any.
101 cl::desc(
"Display the errors from system headers."),
103 static cl::opt<std::string>
LineFilter(
"line-filter", cl::desc(R
"(
104 List of files with line ranges to filter the
105 warnings. Can be used together with
106 -header-filter. The format of the list is a
107 JSON array of objects:
109 {"name":"file1.cpp","lines":[[1,3],[5,7]]},
116 static cl::opt<bool>
Fix(
"fix", cl::desc(R
"(
117 Apply suggested fixes. Without -fix-errors
118 clang-tidy will bail out if any compilation
123 static cl::opt<bool>
FixErrors(
"fix-errors", cl::desc(R
"(
124 Apply suggested fixes even if compilation
125 errors were found. If compiler errors have
126 attached fix-its, clang-tidy will apply them as
131 static cl::opt<bool>
FixNotes(
"fix-notes", cl::desc(R
"(
132 If a warning has no fix, but a single fix can
133 be found through an associated diagnostic note,
135 Specifying this flag will implicitly enable the
140 static cl::opt<std::string>
FormatStyle(
"format-style", cl::desc(R
"(
141 Style for formatting code around applied fixes:
142 - 'none' (default) turns off formatting
143 - 'file' (literally 'file', not a placeholder)
144 uses .clang-format file in the closest parent
146 - '{ <json> }' specifies options inline, e.g.
147 -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
148 - 'llvm', 'google', 'webkit', 'mozilla'
149 See clang-format documentation for the up-to-date
150 information about formatting styles and options.
151 This option overrides the 'FormatStyle` option in
152 .clang-tidy file, if any.
157 static cl::opt<bool>
ListChecks(
"list-checks", cl::desc(R
"(
158 List all enabled checks and exit. Use with
159 -checks=* to list all available checks.
163 static cl::opt<bool>
ExplainConfig(
"explain-config", cl::desc(R
"(
164 For each enabled check explains, where it is
165 enabled, i.e. in clang-tidy binary, command
166 line or a specific configuration file.
170 static cl::opt<std::string>
Config(
"config", cl::desc(R
"(
171 Specifies a configuration in YAML/JSON format:
172 -config="{Checks: '*',
173 CheckOptions: {x: y}}"
174 When the value is empty, clang-tidy will
175 attempt to find a file named .clang-tidy for
176 each source file in its parent directories.
180 static cl::opt<std::string>
ConfigFile(
"config-file", cl::desc(R
"(
181 Specify the path of .clang-tidy or custom config file:
182 e.g. --config-file=/some/path/myTidyConfigFile
183 This option internally works exactly the same way as
184 --config option after reading specified config file.
185 Use either --config-file or --config, not both.
190 static cl::opt<bool>
DumpConfig(
"dump-config", cl::desc(R
"(
191 Dumps configuration in the YAML format to
192 stdout. This option can be used along with a
193 file name (and '--' if the file is outside of a
194 project with configured compilation database).
195 The configuration used for this file will be
197 Use along with -checks=* to include
198 configuration of all checks.
203 Enable per-check timing profiles, and print a
211 By default reports are printed in tabulated
212 format to stderr. When this option is passed,
213 these per-TU profiles are instead stored as JSON.
215 cl::value_desc("prefix"),
223 cl::init(
false), cl::Hidden,
226 static cl::opt<std::string>
ExportFixes(
"export-fixes", cl::desc(R
"(
227 YAML file to store suggested fixes in. The
228 stored fixes can be applied to the input source
229 code with clang-apply-replacements.
231 cl::value_desc("filename"),
234 static cl::opt<bool>
Quiet(
"quiet", cl::desc(R
"(
235 Run clang-tidy in quiet mode. This suppresses
236 printing statistics about ignored warnings and
237 warnings treated as errors if the respective
238 options are specified.
243 static cl::opt<std::string>
VfsOverlay(
"vfsoverlay", cl::desc(R
"(
244 Overlay the virtual filesystem described by file
245 over the real file system.
247 cl::value_desc("filename"),
250 static cl::opt<bool>
UseColor(
"use-color", cl::desc(R
"(
251 Use colors in diagnostics. If not set, colors
252 will be used if the terminal connected to
253 standard output supports colors.
254 This option overrides the 'UseColor' option in
255 .clang-tidy file, if any.
259 static cl::opt<bool>
VerifyConfig(
"verify-config", cl::desc(R
"(
260 Check the config files to ensure each check and
261 option is recognized.
269 if (Stats.errorsIgnored()) {
270 llvm::errs() <<
"Suppressed " << Stats.errorsIgnored() <<
" warnings (";
272 if (Stats.ErrorsIgnoredNonUserCode) {
273 llvm::errs() << Stats.ErrorsIgnoredNonUserCode <<
" in non-user code";
276 if (Stats.ErrorsIgnoredLineFilter) {
277 llvm::errs() <<
Separator << Stats.ErrorsIgnoredLineFilter
278 <<
" due to line filter";
281 if (Stats.ErrorsIgnoredNOLINT) {
282 llvm::errs() <<
Separator << Stats.ErrorsIgnoredNOLINT <<
" NOLINT";
285 if (Stats.ErrorsIgnoredCheckFilter)
286 llvm::errs() <<
Separator << Stats.ErrorsIgnoredCheckFilter
287 <<
" with check filters";
288 llvm::errs() <<
").\n";
289 if (Stats.ErrorsIgnoredNonUserCode)
290 llvm::errs() <<
"Use -header-filter=.* to display errors from all "
291 "non-system headers. Use -system-headers to display "
292 "errors from system headers as well.\n";
297 llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) {
298 ClangTidyGlobalOptions GlobalOptions;
300 llvm::errs() <<
"Invalid LineFilter: " << Err.message() <<
"\n\nUsage:\n";
301 llvm::cl::PrintHelpMessage(
false,
true);
305 ClangTidyOptions DefaultOptions;
307 DefaultOptions.WarningsAsErrors =
"";
311 DefaultOptions.User = llvm::sys::Process::GetEnv(
"USER");
313 if (!DefaultOptions.User)
314 DefaultOptions.User = llvm::sys::Process::GetEnv(
"USERNAME");
316 ClangTidyOptions OverrideOptions;
317 if (
Checks.getNumOccurrences() > 0)
318 OverrideOptions.Checks =
Checks;
327 if (
UseColor.getNumOccurrences() > 0)
328 OverrideOptions.UseColor =
UseColor;
331 [&](StringRef Configuration,
332 StringRef Source) -> std::unique_ptr<ClangTidyOptionsProvider> {
333 llvm::ErrorOr<ClangTidyOptions> ParsedConfig =
336 return std::make_unique<ConfigOptionsProvider>(
337 std::move(GlobalOptions),
338 ClangTidyOptions::getDefaults().merge(DefaultOptions, 0),
339 std::move(*ParsedConfig), std::move(OverrideOptions), std::move(FS));
340 llvm::errs() <<
"Error: invalid configuration specified.\n"
341 << ParsedConfig.getError().message() <<
"\n";
346 if (
Config.getNumOccurrences() > 0) {
347 llvm::errs() <<
"Error: --config-file and --config are "
348 "mutually exclusive. Specify only one.\n";
352 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
Text =
354 if (std::error_code EC =
Text.getError()) {
355 llvm::errs() <<
"Error: can't read config-file '" <<
ConfigFile
356 <<
"': " << EC.message() <<
"\n";
360 return LoadConfig((*Text)->getBuffer(),
ConfigFile);
363 if (
Config.getNumOccurrences() > 0)
364 return LoadConfig(
Config,
"<command-line-config>");
366 return std::make_unique<FileOptionsProvider>(
367 std::move(GlobalOptions), std::move(DefaultOptions),
368 std::move(OverrideOptions), std::move(FS));
371 llvm::IntrusiveRefCntPtr<vfs::FileSystem>
373 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS) {
374 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
375 BaseFS->getBufferForFile(OverlayFile);
377 llvm::errs() <<
"Can't load virtual filesystem overlay file '"
378 << OverlayFile <<
"': " << Buffer.getError().message()
383 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getVFSFromYAML(
384 std::move(Buffer.get()),
nullptr, OverlayFile);
386 llvm::errs() <<
"Error: invalid virtual filesystem overlay file '"
387 << OverlayFile <<
"'.\n";
393 static StringRef
closest(StringRef Value,
const StringSet<> &Allowed) {
394 unsigned MaxEdit = 5U;
396 for (
auto Item : Allowed.keys()) {
397 unsigned Cur = Value.edit_distance_insensitive(Item,
true, MaxEdit);
408 static bool verifyChecks(
const StringSet<> &AllChecks, StringRef CheckGlob,
410 llvm::StringRef Cur, Rest;
411 bool AnyInvalid =
false;
412 for (std::tie(Cur, Rest) = CheckGlob.split(
',');
413 !(Cur.empty() && Rest.empty()); std::tie(Cur, Rest) = Rest.split(
',')) {
417 Cur.consume_front(
"-");
418 if (Cur.startswith(
"clang-diagnostic"))
420 if (Cur.contains(
'*')) {
421 SmallString<128> RegexText(
"^");
422 StringRef MetaChars(
"()^$|*+?.[]\\{}");
425 RegexText.push_back(
'.');
426 else if (MetaChars.contains(
C))
427 RegexText.push_back(
'\\');
428 RegexText.push_back(
C);
430 RegexText.push_back(
'$');
431 llvm::Regex Glob(RegexText);
433 if (!Glob.isValid(Error)) {
436 <<
"building check glob '" << Cur <<
"' " <<
Error <<
"'\n";
439 if (llvm::none_of(AllChecks.keys(),
440 [&Glob](StringRef S) { return Glob.match(S); })) {
442 llvm::WithColor::warning(llvm::errs(), Source)
443 <<
"check glob '" << Cur <<
"' doesn't match any known check"
447 if (AllChecks.contains(Cur))
450 llvm::raw_ostream &
Output = llvm::WithColor::warning(llvm::errs(), Source)
451 <<
"unknown check '" << Cur <<
'\'';
452 llvm::StringRef Closest =
closest(Cur, AllChecks);
453 if (!Closest.empty())
454 Output <<
"; did you mean '" << Closest <<
'\'';
462 llvm::InitLLVM
X(argc, argv);
465 if (cl::Option *LoadOpt = cl::getRegisteredOptions().
lookup(
"load"))
468 llvm::Expected<CommonOptionsParser> OptionsParser =
471 if (!OptionsParser) {
476 llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS(
477 new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
480 IntrusiveRefCntPtr<vfs::FileSystem> VfsFromFile =
484 BaseFS->pushOverlay(std::move(VfsFromFile));
488 auto *OptionsProvider = OwningOptionsProvider.get();
489 if (!OptionsProvider)
492 auto MakeAbsolute = [](
const std::string &Input) -> SmallString<256> {
495 SmallString<256> AbsolutePath(Input);
497 llvm::errs() <<
"Can't make absolute path from " << Input <<
": "
498 << EC.message() <<
"\n";
506 auto PathList = OptionsParser->getSourcePathList();
507 if (!PathList.empty()) {
511 SmallString<256> FilePath = MakeAbsolute(std::string(
FileName));
513 ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
514 std::vector<std::string> EnabledChecks =
519 std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
520 RawOptions = OptionsProvider->getRawOptions(FilePath);
521 for (
const std::string &Check : EnabledChecks) {
522 for (
auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) {
523 if (It->first.Checks && GlobList(*It->first.Checks).contains(Check)) {
524 llvm::outs() <<
"'" << Check <<
"' is enabled in the " << It->second
534 if (EnabledChecks.empty()) {
535 llvm::errs() <<
"No checks enabled.\n";
538 llvm::outs() <<
"Enabled checks:";
539 for (
const auto &CheckName : EnabledChecks)
540 llvm::outs() <<
"\n " << CheckName;
541 llvm::outs() <<
"\n\n";
546 EffectiveOptions.CheckOptions =
549 EffectiveOptions, 0))
555 std::vector<ClangTidyOptionsProvider::OptionsSource> RawOptions =
556 OptionsProvider->getRawOptions(
FileName);
557 NamesAndOptions Valid =
559 bool AnyInvalid =
false;
560 for (
const std::pair<ClangTidyOptions, std::string> &OptionWithSource :
562 const ClangTidyOptions &Opts = OptionWithSource.first;
565 verifyChecks(Valid.Names, *Opts.Checks, OptionWithSource.second);
567 for (
auto Key : Opts.CheckOptions.keys()) {
568 if (Valid.Options.contains(Key))
572 llvm::WithColor::warning(llvm::errs(), OptionWithSource.second)
573 <<
"unknown check option '" <<
Key <<
'\'';
574 llvm::StringRef Closest =
closest(Key, Valid.Options);
575 if (!Closest.empty())
576 Output <<
"; did you mean '" << Closest <<
'\'';
582 llvm::outs() <<
"No config errors detected.\n";
586 if (EnabledChecks.empty()) {
587 llvm::errs() <<
"Error: no checks enabled.\n";
588 llvm::cl::PrintHelpMessage(
false,
true);
592 if (PathList.empty()) {
593 llvm::errs() <<
"Error: no input files specified.\n";
594 llvm::cl::PrintHelpMessage(
false,
true);
598 llvm::InitializeAllTargetInfos();
599 llvm::InitializeAllTargetMCs();
600 llvm::InitializeAllAsmParsers();
602 ClangTidyContext Context(std::move(OwningOptionsProvider),
604 std::vector<ClangTidyError> Errors =
605 runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
607 bool FoundErrors = llvm::any_of(Errors, [](
const ClangTidyError &
E) {
616 const bool DisableFixes = FoundErrors && !
FixErrors;
618 unsigned WErrorCount = 0;
621 WErrorCount, BaseFS);
625 llvm::raw_fd_ostream
OS(
ExportFixes, EC, llvm::sys::fs::OF_None);
627 llvm::errs() <<
"Error opening output file: " << EC.message() <<
'\n';
635 if (DisableFixes && Behaviour !=
FB_NoFix)
637 <<
"Found compiler errors, but -fix-errors was not specified.\n"
638 "Fixes have NOT been applied.\n\n";
643 StringRef Plural = WErrorCount == 1 ?
"" :
"s";
644 llvm::errs() << WErrorCount <<
" warning" << Plural <<
" treated as error"
659 llvm::errs() <<
"Found compiler error(s).\n";