Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -87,6 +87,17 @@ /// \brief Key-value mapping used to store check-specific options. OptionMap CheckOptions; + /// \brief The source of the option. + // + /// clang-tidy has 3 types of the sources (from low to high): + /// * clang-tidy binary + /// * '-config' commandline option or a specific configuration file + /// * '-checks' commandline option + std::string Source; + + /// \brief Stores all options that have higher priority than current one. + std::vector HigherPriorityOptions; + 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 @@ -120,6 +120,11 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const { ClangTidyOptions Result = *this; + Result.HigherPriorityOptions.push_back(Other); + Result.HigherPriorityOptions.insert(Result.HigherPriorityOptions.end(), + Other.HigherPriorityOptions.begin(), + Other.HigherPriorityOptions.end()); + // Merge comma-separated glob lists by appending the new value after a comma. if (Other.Checks) Result.Checks = @@ -255,7 +260,7 @@ << ParsedOptions.getError().message() << "\n"; continue; } - + ParsedOptions->Source = 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 constexpr char CheckSourceTypeDefaultBinary[] = "clang-tidy binary"; +static constexpr char CheckSourceTypeCheckCommandLineOption[] = + "command-line option '-checks'"; +static constexpr char CheckSourceTypeConfigCommandLineOption[] = + "command-line option '-config'"; + static void printStats(const ClangTidyStats &Stats) { if (Stats.errorsIgnored()) { llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings ("; @@ -256,6 +268,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.Source = CheckSourceTypeDefaultBinary; DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; @@ -266,6 +279,7 @@ DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME"); ClangTidyOptions OverrideOptions; + OverrideOptions.Source = CheckSourceTypeCheckCommandLineOption; if (Checks.getNumOccurrences() > 0) OverrideOptions.Checks = Checks; if (WarningsAsErrors.getNumOccurrences() > 0) @@ -280,6 +294,7 @@ if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { + ParsedConfig->Source = CheckSourceTypeConfigCommandLineOption; return llvm::make_unique( GlobalOptions, ClangTidyOptions::getDefaults() .mergeWith(DefaultOptions) @@ -311,6 +326,28 @@ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainConfig) { + // FIXME: Figure out a more elegant way to show other ClangTidyOptions' + // fields like ExtraArg. + for (const std::string &Check : EnabledChecks) { + bool IsFindMatch = false; + for (auto It = EffectiveOptions.HigherPriorityOptions.rbegin(); + It != EffectiveOptions.HigherPriorityOptions.rend(); ++It) { + if (It->Checks && GlobList(*(It->Checks)).contains(Check)) { + llvm::outs() << "'" << Check << "' is enabled in the " << It->Source + << ".\n"; + IsFindMatch = true; + break; + } + } + if (!IsFindMatch) { + llvm::outs() << "'" << Check << "' is enabled in the " + << EffectiveOptions.Source << ".\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,14 @@ +// 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 -checks=modernize-use-nullptr -explain-config | FileCheck --check-prefix=CHECK-MESSAGE6 %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'. +// CHECK-MESSAGE6: 'clang-analyzer-unix.API' is enabled in the clang-tidy binary.