Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -87,6 +87,19 @@ /// \brief Key-value mapping used to store check-specific options. OptionMap CheckOptions; + enum CheckSourceType { + DefaultBinary, + ConfigCommandlineOptionOrFile, + ChecksCommandlineOption, + CheckSourceTypeEnd + }; + // \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[CheckSourceTypeEnd]; + 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,18 @@ 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 (auto SourceType : {ClangTidyOptions::ChecksCommandlineOption, + ClangTidyOptions::ConfigCommandlineOptionOrFile, + ClangTidyOptions::DefaultBinary}) { + for (const auto &CheckSource : Other.CheckSources[SourceType]) + Result.CheckSources[SourceType].push_back(CheckSource); + } + } if (Other.WarningsAsErrors) Result.WarningsAsErrors = (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty() @@ -255,7 +263,12 @@ << ParsedOptions.getError().message() << "\n"; continue; } - + if (ParsedOptions->Checks) { + ParsedOptions + ->CheckSources[ClangTidyOptions::ConfigCommandlineOptionOrFile] + .push_back(ClangTidyOptions::StringPair(*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: '*', @@ -256,6 +262,8 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.CheckSources[ClangTidyOptions::DefaultBinary].push_back( + ClangTidyOptions::StringPair(DefaultChecks, "clang-tidy binary")); DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; @@ -266,8 +274,12 @@ DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME"); ClangTidyOptions OverrideOptions; - if (Checks.getNumOccurrences() > 0) + if (Checks.getNumOccurrences() > 0) { OverrideOptions.Checks = Checks; + OverrideOptions.CheckSources[ClangTidyOptions::ChecksCommandlineOption] + .push_back(ClangTidyOptions::StringPair( + Checks, "command-line option '-checks'")); + } if (WarningsAsErrors.getNumOccurrences() > 0) OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0) @@ -280,6 +292,12 @@ if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { + if (ParsedConfig->Checks) { + ParsedConfig + ->CheckSources[ClangTidyOptions::ConfigCommandlineOptionOrFile] + .push_back(ClangTidyOptions::StringPair( + *ParsedConfig->Checks, "command-line option '-config'")); + } return llvm::make_unique( GlobalOptions, ClangTidyOptions::getDefaults() .mergeWith(DefaultOptions) @@ -311,6 +329,29 @@ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainConfig) { + for (const std::string& Check : EnabledChecks) { + // Keeps the processing order of different check sources. + for (auto SourceType : {ClangTidyOptions::ChecksCommandlineOption, + ClangTidyOptions::ConfigCommandlineOptionOrFile, + ClangTidyOptions::DefaultBinary}) { + bool FindMatch = false; + for (auto It = EffectiveOptions.CheckSources[SourceType].rbegin(); + It != EffectiveOptions.CheckSources[SourceType].rend(); ++It) { + if (GlobList(It->first).contains(Check)) { + llvm::outs() << "'" << Check << "' is enabled in the " + << It->second << ".\n"; + FindMatch = true; + break; + } + if (FindMatch) + 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'.