Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -59,6 +59,10 @@ /// of this instance overridden by the fields of \p Other that have a value. ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const; + /// \brief Inserts checks' source in CheckSource which is used to record + /// relationship between each check and its source. + void insertCheckSource(llvm::StringRef Checks, llvm::StringRef Source); + /// \brief Checks filter. llvm::Optional Checks; @@ -87,6 +91,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 @@ -100,6 +100,13 @@ namespace clang { namespace tidy { +namespace { +SmallVector SplitChecks(StringRef Checks) { + SmallVector Result; + Checks.split(Result, ','); + return Result; +} +} // namespace ClangTidyOptions ClangTidyOptions::getDefaults() { ClangTidyOptions Options; @@ -121,10 +128,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; + + SmallVector Checks = SplitChecks(*Other.Checks); + for (StringRef Check: Checks) { + const auto it = Other.CheckSources.find(Check); + if (it != Other.CheckSources.end()) + Result.CheckSources[Check] = it->second; + } + } if (Other.WarningsAsErrors) Result.WarningsAsErrors = (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty() @@ -151,6 +166,13 @@ return Result; } +void ClangTidyOptions::insertCheckSource(StringRef Checks, StringRef Source) { + SmallVector SC = SplitChecks(Checks); + for (StringRef Check: SC) { + CheckSources[Check] = Source; + } +} + FileOptionsProvider::FileOptionsProvider( const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, @@ -255,7 +277,10 @@ << ParsedOptions.getError().message() << "\n"; continue; } - + if (ParsedOptions->Checks) { + ParsedOptions->insertCheckSource(*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,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.insertCheckSource(DefaultChecks, "clang-tidy 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.insertCheckSource(Checks, "command-line option '-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->insertCheckSource(*ParsedConfig->Checks, + "command-line option '-config'"); + } return llvm::make_unique( GlobalOptions, ClangTidyOptions::getDefaults() .mergeWith(DefaultOptions) @@ -311,6 +324,19 @@ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainConfig) { + for (const std::string& Check : EnabledChecks) { + for (const ClangTidyOptions::StringPair &CheckSource: + EffectiveOptions.CheckSources) { + if (GlobList(CheckSource.first).contains(Check)) { + llvm::outs() << "'" << Check << "' is enabled in the " + << CheckSource.second << ".\n"; + } + } + } + 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 -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'.