Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -87,6 +87,10 @@ /// \brief Key-value mapping used to store check-specific options. OptionMap CheckOptions; + /// \brief Key-value mapping used to store relationship between check filters + /// and the source file where the filters come from. + OptionMap 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; + const auto it = Other.CheckSources.find(*Other.Checks); + if (it != Other.CheckSources.end()) + Result.CheckSources[*Other.Checks] = it->second; + } if (Other.WarningsAsErrors) Result.WarningsAsErrors = (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty() @@ -255,7 +259,10 @@ << ParsedOptions.getError().message() << "\n"; continue; } - + if (ParsedOptions->Checks) { + ParsedOptions->CheckSources[*ParsedOptions->Checks] = + std::string(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 ExplainChecks("explain-checks", cl::desc(R"( +explains where each check comes from, i.e. command line, .clang-tidy +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,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.CheckSources[DefaultChecks] = "ClangTidy binary"; DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; @@ -266,8 +273,10 @@ DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME"); ClangTidyOptions OverrideOptions; - if (Checks.getNumOccurrences() > 0) + if (Checks.getNumOccurrences() > 0) { OverrideOptions.Checks = Checks; + OverrideOptions.CheckSources[Checks] = "command-line argument 'checks'"; + } if (WarningsAsErrors.getNumOccurrences() > 0) OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0) @@ -280,6 +289,10 @@ if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { + if (ParsedConfig->Checks) { + ParsedConfig->CheckSources[*ParsedConfig->Checks] = + "command-line argument 'config'"; + } return llvm::make_unique( GlobalOptions, ClangTidyOptions::getDefaults() .mergeWith(DefaultOptions) @@ -311,6 +324,20 @@ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainChecks) { + for (const std::string& Check : EnabledChecks) { + for (const ClangTidyOptions::StringPair &CheckSource: + EffectiveOptions.CheckSources) { + GlobList filter(CheckSource.first); + if (filter.contains(Check)) { + llvm::outs() << "'" << Check << "' comes from " << CheckSource.second + << ".\n"; + } + } + } + return 0; + } + if (ListChecks) { llvm::outs() << "Enabled checks:"; for (auto CheckName : EnabledChecks) Index: test/clang-tidy/Inputs/explain-checks/.clang-tidy =================================================================== --- /dev/null +++ test/clang-tidy/Inputs/explain-checks/.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,11 @@ +// RUN: clang-tidy -checks=-*,modernize-use-nullptr -explain-checks | FileCheck --check-prefix=CHECK-MESSAGE1 %s +// RUN: clang-tidy -config="{Checks: '-*,modernize-use-nullptr'}" -explain-checks | FileCheck --check-prefix=CHECK-MESSAGE2 %s +// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-use-nullptr'}" -explain-checks | FileCheck --check-prefix=CHECK-MESSAGE3 %s +// RUN: clang-tidy -explain-checks | grep "'.*' comes from ClangTidy binary" +// RUN: clang-tidy -explain-checks %S/Inputs/explain-checks/a.cc | grep "'modernize-use-nullptr' comes from %S/Inputs/explain-checks/.clang-tidy." + +// CHECK-MESSAGE1: 'modernize-use-nullptr' comes from command-line argument 'checks'. +// CHECK-MESSAGE2: 'modernize-use-nullptr' comes from command-line argument 'config'. + +// CHECK-MESSAGE3: 'modernize-use-nullptr' comes from command-line argument 'config'. +// CHECK-MESSAGE3: 'modernize-use-nullptr' comes from command-line argument 'checks'.