From: Haojian Wu Date: Wed, 27 Apr 2016 09:15:01 +0000 (+0000) Subject: [ClangTidy] Add an 'explain-checks' option to diagnose where each checks comes from. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=12e6b8f92963b2a49c913d24fff0d75b03679b19;p=platform%2Fupstream%2Fllvm.git [ClangTidy] Add an 'explain-checks' option to diagnose where each checks comes from. Reviewers: alexfh Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D18694 llvm-svn: 267683 --- diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp index 9f6be18..e18e02e 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -23,6 +23,7 @@ using clang::tidy::ClangTidyOptions; using clang::tidy::FileFilter; +using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource; LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange) @@ -151,6 +152,48 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const { return Result; } +const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] = + "clang-tidy binary"; +const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] = + "command-line option '-checks'"; +const char + ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] = + "command-line option '-config'"; + +ClangTidyOptions +ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) { + ClangTidyOptions Result; + for (const auto &Source : getRawOptions(FileName)) + Result = Result.mergeWith(Source.first); + return Result; +} + +std::vector +DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) { + std::vector Result; + Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary); + return Result; +} + +ConfigOptionsProvider::ConfigOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &ConfigOptions, + const ClangTidyOptions &OverrideOptions) + : DefaultOptionsProvider(GlobalOptions, DefaultOptions), + ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {} + +std::vector +ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) { + std::vector RawOptions = + DefaultOptionsProvider::getRawOptions(FileName); + RawOptions.emplace_back(ConfigOptions, + OptionsSourceTypeConfigCommandLineOption); + RawOptions.emplace_back(OverrideOptions, + OptionsSourceTypeCheckCommandLineOption); + return RawOptions; +} + FileOptionsProvider::FileOptionsProvider( const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, @@ -158,7 +201,6 @@ FileOptionsProvider::FileOptionsProvider( : DefaultOptionsProvider(GlobalOptions, DefaultOptions), OverrideOptions(OverrideOptions) { ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration); - CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions); } FileOptionsProvider::FileOptionsProvider( @@ -168,13 +210,13 @@ FileOptionsProvider::FileOptionsProvider( const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers) : DefaultOptionsProvider(GlobalOptions, DefaultOptions), OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) { - CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions); } // FIXME: This method has some common logic with clang::format::getStyle(). // Consider pulling out common bits to a findParentFileWithName function or // similar. -ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) { +std::vector +FileOptionsProvider::getRawOptions(StringRef FileName) { DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n"); SmallString<256> FilePath(FileName); @@ -186,19 +228,23 @@ ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) { FileName = FilePath; } + std::vector RawOptions = + DefaultOptionsProvider::getRawOptions(FileName); + OptionsSource CommandLineOptions(OverrideOptions, + OptionsSourceTypeCheckCommandLineOption); // Look for a suitable configuration file in all parent directories of the // file. Start with the immediate parent directory and move up. StringRef Path = llvm::sys::path::parent_path(FileName); - for (StringRef CurrentPath = Path;; + for (StringRef CurrentPath = Path; !CurrentPath.empty(); CurrentPath = llvm::sys::path::parent_path(CurrentPath)) { - llvm::Optional Result; + llvm::Optional Result; auto Iter = CachedOptions.find(CurrentPath); if (Iter != CachedOptions.end()) Result = Iter->second; if (!Result) - Result = TryReadConfigFile(CurrentPath); + Result = tryReadConfigFile(CurrentPath); if (Result) { // Store cached value for all intermediate directories. @@ -208,13 +254,18 @@ ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) { CachedOptions[Path] = *Result; Path = llvm::sys::path::parent_path(Path); } - return CachedOptions[Path] = *Result; + CachedOptions[Path] = *Result; + + RawOptions.push_back(*Result); + break; } } + RawOptions.push_back(CommandLineOptions); + return RawOptions; } -llvm::Optional -FileOptionsProvider::TryReadConfigFile(StringRef Directory) { +llvm::Optional +FileOptionsProvider::tryReadConfigFile(StringRef Directory) { assert(!Directory.empty()); if (!llvm::sys::fs::is_directory(Directory)) { @@ -255,10 +306,7 @@ FileOptionsProvider::TryReadConfigFile(StringRef Directory) { << ParsedOptions.getError().message() << "\n"; continue; } - - return DefaultOptionsProvider::getOptions(Directory) - .mergeWith(*ParsedOptions) - .mergeWith(OverrideOptions); + return OptionsSource(*ParsedOptions, ConfigFile.c_str()); } return llvm::None; } diff --git a/clang-tools-extra/clang-tidy/ClangTidyOptions.h b/clang-tools-extra/clang-tidy/ClangTidyOptions.h index 10619cc..74a483f 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ b/clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -99,14 +99,33 @@ struct ClangTidyOptions { /// \brief Abstract interface for retrieving various ClangTidy options. class ClangTidyOptionsProvider { public: + static const char OptionsSourceTypeDefaultBinary[]; + static const char OptionsSourceTypeCheckCommandLineOption[]; + static const char OptionsSourceTypeConfigCommandLineOption[]; + virtual ~ClangTidyOptionsProvider() {} /// \brief Returns global options, which are independent of the file. virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0; + /// \brief ClangTidyOptions and its source. + // + /// clang-tidy has 3 types of the sources in order of increasing priority: + /// * clang-tidy binary. + /// * '-config' commandline option or a specific configuration file. If the + /// commandline option is specified, clang-tidy will ignore the + /// configuration file. + /// * '-checks' commandline option. + typedef std::pair OptionsSource; + + /// \brief Returns an ordered vector of OptionsSources, in order of increasing + /// priority. + virtual std::vector + getRawOptions(llvm::StringRef FileName) = 0; + /// \brief Returns options applying to a specific translation unit with the /// specified \p FileName. - virtual ClangTidyOptions getOptions(llvm::StringRef FileName) = 0; + ClangTidyOptions getOptions(llvm::StringRef FileName); }; /// \brief Implementation of the \c ClangTidyOptionsProvider interface, which @@ -119,15 +138,28 @@ public: const ClangTidyGlobalOptions &getGlobalOptions() override { return GlobalOptions; } - ClangTidyOptions getOptions(llvm::StringRef /*FileName*/) override { - return DefaultOptions; - } + std::vector getRawOptions(llvm::StringRef FileName) override; private: ClangTidyGlobalOptions GlobalOptions; ClangTidyOptions DefaultOptions; }; +/// \brief Implementation of ClangTidyOptions interface, which is used for +/// '-config' command-line option. +class ConfigOptionsProvider : public DefaultOptionsProvider { +public: + ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &ConfigOptions, + const ClangTidyOptions &OverrideOptions); + std::vector getRawOptions(llvm::StringRef FileName) override; + +private: + ClangTidyOptions ConfigOptions; + ClangTidyOptions OverrideOptions; +}; + /// \brief Implementation of the \c ClangTidyOptionsProvider interface, which /// tries to find a configuration file in the closest parent directory of each /// source file. @@ -198,14 +230,14 @@ public: const ClangTidyOptions &OverrideOptions, const ConfigFileHandlers &ConfigHandlers); - ClangTidyOptions getOptions(llvm::StringRef FileName) override; + std::vector getRawOptions(llvm::StringRef FileName) override; protected: /// \brief Try to read configuration files from \p Directory using registered /// \c ConfigHandlers. - llvm::Optional TryReadConfigFile(llvm::StringRef Directory); + llvm::Optional tryReadConfigFile(llvm::StringRef Directory); - llvm::StringMap CachedOptions; + llvm::StringMap CachedOptions; ClangTidyOptions OverrideOptions; ConfigFileHandlers ConfigHandlers; }; diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 9b7d1b6..19e9165 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -128,6 +128,12 @@ List all enabled checks and exit. Use with )"), 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: '*', @@ -280,11 +286,10 @@ static std::unique_ptr createOptionsProvider() { if (!Config.empty()) { if (llvm::ErrorOr ParsedConfig = parseConfiguration(Config)) { - return llvm::make_unique( - GlobalOptions, ClangTidyOptions::getDefaults() - .mergeWith(DefaultOptions) - .mergeWith(*ParsedConfig) - .mergeWith(OverrideOptions)); + return llvm::make_unique( + GlobalOptions, + ClangTidyOptions::getDefaults().mergeWith(DefaultOptions), + *ParsedConfig, OverrideOptions); } else { llvm::errs() << "Error: invalid configuration specified.\n" << ParsedConfig.getError().message() << "\n"; @@ -311,6 +316,22 @@ static int clangTidyMain(int argc, const char **argv) { ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); std::vector EnabledChecks = getCheckNames(EffectiveOptions); + if (ExplainConfig) { + //FIXME: Show other ClangTidyOptions' fields, like ExtraArg. + std::vector + RawOptions = OptionsProvider->getRawOptions(FileName); + for (const std::string &Check : EnabledChecks) { + for (auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) { + if (It->first.Checks && GlobList(*It->first.Checks).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) diff --git a/clang-tools-extra/test/clang-tidy/Inputs/explain-config/.clang-tidy b/clang-tools-extra/test/clang-tidy/Inputs/explain-config/.clang-tidy new file mode 100644 index 0000000..4fbd09c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/Inputs/explain-config/.clang-tidy @@ -0,0 +1 @@ +Checks: '-*,modernize-use-nullptr' diff --git a/clang-tools-extra/test/clang-tidy/explain-checks.cpp b/clang-tools-extra/test/clang-tidy/explain-checks.cpp new file mode 100644 index 0000000..b1865e7 --- /dev/null +++ b/clang-tools-extra/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 -config="{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.