[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

git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@267683 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Haojian Wu 2016-04-27 09:15:01 +00:00
Родитель 60e27af3d9
Коммит 67b26d6f44
5 изменённых файлов: 141 добавлений и 25 удалений

Просмотреть файл

@ -23,6 +23,7 @@
using clang::tidy::ClangTidyOptions; using clang::tidy::ClangTidyOptions;
using clang::tidy::FileFilter; using clang::tidy::FileFilter;
using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
@ -151,6 +152,48 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
return Result; 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<OptionsSource>
DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> 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<OptionsSource>
ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
RawOptions.emplace_back(ConfigOptions,
OptionsSourceTypeConfigCommandLineOption);
RawOptions.emplace_back(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
return RawOptions;
}
FileOptionsProvider::FileOptionsProvider( FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &DefaultOptions,
@ -158,7 +201,6 @@ FileOptionsProvider::FileOptionsProvider(
: DefaultOptionsProvider(GlobalOptions, DefaultOptions), : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions) { OverrideOptions(OverrideOptions) {
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration); ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
} }
FileOptionsProvider::FileOptionsProvider( FileOptionsProvider::FileOptionsProvider(
@ -168,13 +210,13 @@ FileOptionsProvider::FileOptionsProvider(
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers) const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions), : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) { OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
} }
// FIXME: This method has some common logic with clang::format::getStyle(). // FIXME: This method has some common logic with clang::format::getStyle().
// Consider pulling out common bits to a findParentFileWithName function or // Consider pulling out common bits to a findParentFileWithName function or
// similar. // similar.
ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) { std::vector<OptionsSource>
FileOptionsProvider::getRawOptions(StringRef FileName) {
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n"); DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
SmallString<256> FilePath(FileName); SmallString<256> FilePath(FileName);
@ -186,19 +228,23 @@ ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) {
FileName = FilePath; FileName = FilePath;
} }
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
OptionsSource CommandLineOptions(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
// Look for a suitable configuration file in all parent directories of the // Look for a suitable configuration file in all parent directories of the
// file. Start with the immediate parent directory and move up. // file. Start with the immediate parent directory and move up.
StringRef Path = llvm::sys::path::parent_path(FileName); 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)) { CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
llvm::Optional<ClangTidyOptions> Result; llvm::Optional<OptionsSource> Result;
auto Iter = CachedOptions.find(CurrentPath); auto Iter = CachedOptions.find(CurrentPath);
if (Iter != CachedOptions.end()) if (Iter != CachedOptions.end())
Result = Iter->second; Result = Iter->second;
if (!Result) if (!Result)
Result = TryReadConfigFile(CurrentPath); Result = tryReadConfigFile(CurrentPath);
if (Result) { if (Result) {
// Store cached value for all intermediate directories. // Store cached value for all intermediate directories.
@ -208,13 +254,18 @@ ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) {
CachedOptions[Path] = *Result; CachedOptions[Path] = *Result;
Path = llvm::sys::path::parent_path(Path); 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<ClangTidyOptions> llvm::Optional<OptionsSource>
FileOptionsProvider::TryReadConfigFile(StringRef Directory) { FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
assert(!Directory.empty()); assert(!Directory.empty());
if (!llvm::sys::fs::is_directory(Directory)) { if (!llvm::sys::fs::is_directory(Directory)) {
@ -255,10 +306,7 @@ FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
<< ParsedOptions.getError().message() << "\n"; << ParsedOptions.getError().message() << "\n";
continue; continue;
} }
return OptionsSource(*ParsedOptions, ConfigFile.c_str());
return DefaultOptionsProvider::getOptions(Directory)
.mergeWith(*ParsedOptions)
.mergeWith(OverrideOptions);
} }
return llvm::None; return llvm::None;
} }

Просмотреть файл

@ -99,14 +99,33 @@ struct ClangTidyOptions {
/// \brief Abstract interface for retrieving various ClangTidy options. /// \brief Abstract interface for retrieving various ClangTidy options.
class ClangTidyOptionsProvider { class ClangTidyOptionsProvider {
public: public:
static const char OptionsSourceTypeDefaultBinary[];
static const char OptionsSourceTypeCheckCommandLineOption[];
static const char OptionsSourceTypeConfigCommandLineOption[];
virtual ~ClangTidyOptionsProvider() {} virtual ~ClangTidyOptionsProvider() {}
/// \brief Returns global options, which are independent of the file. /// \brief Returns global options, which are independent of the file.
virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0; 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<ClangTidyOptions, std::string> OptionsSource;
/// \brief Returns an ordered vector of OptionsSources, in order of increasing
/// priority.
virtual std::vector<OptionsSource>
getRawOptions(llvm::StringRef FileName) = 0;
/// \brief Returns options applying to a specific translation unit with the /// \brief Returns options applying to a specific translation unit with the
/// specified \p FileName. /// specified \p FileName.
virtual ClangTidyOptions getOptions(llvm::StringRef FileName) = 0; ClangTidyOptions getOptions(llvm::StringRef FileName);
}; };
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which /// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
@ -119,15 +138,28 @@ public:
const ClangTidyGlobalOptions &getGlobalOptions() override { const ClangTidyGlobalOptions &getGlobalOptions() override {
return GlobalOptions; return GlobalOptions;
} }
ClangTidyOptions getOptions(llvm::StringRef /*FileName*/) override { std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
return DefaultOptions;
}
private: private:
ClangTidyGlobalOptions GlobalOptions; ClangTidyGlobalOptions GlobalOptions;
ClangTidyOptions DefaultOptions; 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<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
private:
ClangTidyOptions ConfigOptions;
ClangTidyOptions OverrideOptions;
};
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which /// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
/// tries to find a configuration file in the closest parent directory of each /// tries to find a configuration file in the closest parent directory of each
/// source file. /// source file.
@ -198,14 +230,14 @@ public:
const ClangTidyOptions &OverrideOptions, const ClangTidyOptions &OverrideOptions,
const ConfigFileHandlers &ConfigHandlers); const ConfigFileHandlers &ConfigHandlers);
ClangTidyOptions getOptions(llvm::StringRef FileName) override; std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
protected: protected:
/// \brief Try to read configuration files from \p Directory using registered /// \brief Try to read configuration files from \p Directory using registered
/// \c ConfigHandlers. /// \c ConfigHandlers.
llvm::Optional<ClangTidyOptions> TryReadConfigFile(llvm::StringRef Directory); llvm::Optional<OptionsSource> tryReadConfigFile(llvm::StringRef Directory);
llvm::StringMap<ClangTidyOptions> CachedOptions; llvm::StringMap<OptionsSource> CachedOptions;
ClangTidyOptions OverrideOptions; ClangTidyOptions OverrideOptions;
ConfigFileHandlers ConfigHandlers; ConfigFileHandlers ConfigHandlers;
}; };

Просмотреть файл

@ -128,6 +128,12 @@ List all enabled checks and exit. Use with
)"), )"),
cl::init(false), cl::cat(ClangTidyCategory)); cl::init(false), cl::cat(ClangTidyCategory));
static cl::opt<bool> 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<std::string> Config("config", cl::desc(R"( static cl::opt<std::string> Config("config", cl::desc(R"(
Specifies a configuration in YAML/JSON format: Specifies a configuration in YAML/JSON format:
-config="{Checks: '*', -config="{Checks: '*',
@ -280,11 +286,10 @@ static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider() {
if (!Config.empty()) { if (!Config.empty()) {
if (llvm::ErrorOr<ClangTidyOptions> ParsedConfig = if (llvm::ErrorOr<ClangTidyOptions> ParsedConfig =
parseConfiguration(Config)) { parseConfiguration(Config)) {
return llvm::make_unique<DefaultOptionsProvider>( return llvm::make_unique<ConfigOptionsProvider>(
GlobalOptions, ClangTidyOptions::getDefaults() GlobalOptions,
.mergeWith(DefaultOptions) ClangTidyOptions::getDefaults().mergeWith(DefaultOptions),
.mergeWith(*ParsedConfig) *ParsedConfig, OverrideOptions);
.mergeWith(OverrideOptions));
} else { } else {
llvm::errs() << "Error: invalid configuration specified.\n" llvm::errs() << "Error: invalid configuration specified.\n"
<< ParsedConfig.getError().message() << "\n"; << ParsedConfig.getError().message() << "\n";
@ -311,6 +316,22 @@ static int clangTidyMain(int argc, const char **argv) {
ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName); ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FileName);
std::vector<std::string> EnabledChecks = getCheckNames(EffectiveOptions); std::vector<std::string> EnabledChecks = getCheckNames(EffectiveOptions);
if (ExplainConfig) {
//FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
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) { if (ListChecks) {
llvm::outs() << "Enabled checks:"; llvm::outs() << "Enabled checks:";
for (auto CheckName : EnabledChecks) for (auto CheckName : EnabledChecks)

Просмотреть файл

@ -0,0 +1 @@
Checks: '-*,modernize-use-nullptr'

Просмотреть файл

@ -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.