[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:
Родитель
60e27af3d9
Коммит
67b26d6f44
|
@ -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.
|
Загрузка…
Ссылка в новой задаче