Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -87,6 +87,15 @@ /// \brief Key-value mapping used to store check-specific options. OptionMap CheckOptions; + /// \brief Stores each check filter and its source for every check source + /// type. + /// + /// clang-tidy has 3 types of check sources: + /// * clang-tidy binary + /// * '-config' commandline option or a specific configuration file + /// * '-checks' commandline option + std::vector CheckSources; + typedef std::vector ArgList; /// \brief Add extra compilation arguments to the end of the list. Index: clang-tidy/ClangTidyOptions.cpp =================================================================== --- clang-tidy/ClangTidyOptions.cpp +++ clang-tidy/ClangTidyOptions.cpp @@ -121,10 +121,14 @@ ClangTidyOptions Result = *this; // Merge comma-separated glob lists by appending the new value after a comma. - if (Other.Checks) + if (Other.Checks) { Result.Checks = (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") + *Other.Checks; + + for (const auto &CheckSource : Other.CheckSources) + Result.CheckSources.push_back(CheckSource); + } if (Other.WarningsAsErrors) Result.WarningsAsErrors = (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty() @@ -255,7 +259,10 @@ << ParsedOptions.getError().message() << "\n"; continue; } - + if (ParsedOptions->Checks) { + ParsedOptions->CheckSources.emplace_back( + *ParsedOptions->Checks, ConfigFile.c_str()); + } return DefaultOptionsProvider::getOptions(Directory) .mergeWith(*ParsedOptions) .mergeWith(OverrideOptions); Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -128,6 +128,12 @@ )"), cl::init(false), cl::cat(ClangTidyCategory)); +static cl::opt ExplainConfig("explain-config", cl::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 Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks: '*', @@ -179,6 +185,12 @@ namespace clang { namespace tidy { +static const char* CheckSourceTypeDefaultBinary = "clang-tidy binary"; +static const char* CheckSourceTypeCheckCommandLineOption = + "command-line option '-checks'"; +static const char* CheckSourceTypeConfigCommandLineOption = + "command-line option '-config'"; + static void printStats(const ClangTidyStats &Stats) { if (Stats.errorsIgnored()) { llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings ("; @@ -256,6 +268,8 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.CheckSources.emplace_back(DefaultChecks, + CheckSourceTypeDefaultBinary); DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; @@ -266,8 +280,11 @@ DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME"); ClangTidyOptions OverrideOptions; - if (Checks.getNumOccurrences() > 0) + if (Checks.getNumOccurrences() > 0) { OverrideOptions.Checks = Checks; + OverrideOptions.CheckSources.emplace_back( + Checks, CheckSourceTypeCheckCommandLineOption); + } if (WarningsAsErrors.getNumOccurrences() > 0) OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0) @@ -280,6 +297,10 @@ if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { + if (ParsedConfig->Checks) { + ParsedConfig->CheckSources.emplace_back( + *ParsedConfig->Checks, CheckSourceTypeConfigCommandLineOption); + } return llvm::make_unique( GlobalOptions, ClangTidyOptions::getDefaults() .mergeWith(DefaultOptions) @@ -311,6 +332,20 @@ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainConfig) { + for (const std::string &Check : EnabledChecks) { + for (auto It = EffectiveOptions.CheckSources.rbegin(); + It != EffectiveOptions.CheckSources.rend(); ++It) { + if (GlobList(It->first).contains(Check)) { + llvm::outs() << "'" << Check << "' is enabled in the " << It->second + << ".\n"; + break; + } + } + } + return 0; + } + if (ListChecks) { llvm::outs() << "Enabled checks:"; for (auto CheckName : EnabledChecks) Index: test/clang-tidy/Inputs/explain-config/.clang-tidy =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/explain-config/.clang-tidy @@ -0,0 +1 @@ +Checks: '-*,modernize-use-nullptr' Index: test/clang-tidy/explain-checks.cpp =================================================================== --- /dev/null +++ test/clang-tidy/explain-checks.cpp @@ -0,0 +1,12 @@ +// RUN: clang-tidy -checks=-*,modernize-use-nullptr -explain-config | FileCheck --check-prefix=CHECK-MESSAGE1 %s +// RUN: clang-tidy -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE2 %s +// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE3 %s +// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,-modernize-use-nullptr'}" %S/Inputs/explain-config/a.cc -explain-config | FileCheck --check-prefix=CHECK-MESSAGE4 %s +// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-*'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE5 %s +// RUN: clang-tidy -explain-config %S/Inputs/explain-config/a.cc | grep "'modernize-use-nullptr' is enabled in the %S/Inputs/explain-config/.clang-tidy." + +// CHECK-MESSAGE1: 'modernize-use-nullptr' is enabled in the command-line option '-checks'. +// CHECK-MESSAGE2: 'modernize-use-nullptr' is enabled in the command-line option '-config'. +// CHECK-MESSAGE3: 'modernize-use-nullptr' is enabled in the command-line option '-checks'. +// CHECK-MESSAGE4: 'modernize-use-nullptr' is enabled in the command-line option '-checks'. +// CHECK-MESSAGE5: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.