This is a short-term fix for PR33650 aimed to get the modules build bots green again. Remove all the places where we use the LLVM_YAML_IS_(FLOW_)?SEQUENCE_VECTOR macros to try to locally specialize a global template for a global type. That's not how C++ works. Instead, we now centrally define how to format vectors of fundamental types and of string (std::string and StringRef). We use flow formatting for the former cases, since that's the obvious right thing to do; in the latter case, it's less clear what the right choice is, but flow formatting is really bad for some cases (due to very long strings), so we pick block formatting. (Many of the cases that were using flow formatting for strings are improved by this change.) Other than the flow -> block formatting change for some vectors of strings, this should result in no functionality change. Differential Revision: https://reviews.llvm.org/D34907 Corresponding LLVM change is r306878. llvm-svn: 306879
341 lines
12 KiB
C++
341 lines
12 KiB
C++
//===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ClangTidyOptions.h"
|
|
#include "ClangTidyModuleRegistry.h"
|
|
#include "clang/Basic/LLVM.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/YAMLTraits.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <utility>
|
|
|
|
#define DEBUG_TYPE "clang-tidy-options"
|
|
|
|
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)
|
|
|
|
namespace llvm {
|
|
namespace yaml {
|
|
|
|
// Map std::pair<int, int> to a JSON array of size 2.
|
|
template <> struct SequenceTraits<FileFilter::LineRange> {
|
|
static size_t size(IO &IO, FileFilter::LineRange &Range) {
|
|
return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
|
|
}
|
|
static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
|
|
if (Index > 1)
|
|
IO.setError("Too many elements in line range.");
|
|
return Index == 0 ? Range.first : Range.second;
|
|
}
|
|
};
|
|
|
|
template <> struct MappingTraits<FileFilter> {
|
|
static void mapping(IO &IO, FileFilter &File) {
|
|
IO.mapRequired("name", File.Name);
|
|
IO.mapOptional("lines", File.LineRanges);
|
|
}
|
|
static StringRef validate(IO &io, FileFilter &File) {
|
|
if (File.Name.empty())
|
|
return "No file name specified";
|
|
for (const FileFilter::LineRange &Range : File.LineRanges) {
|
|
if (Range.first <= 0 || Range.second <= 0)
|
|
return "Invalid line range";
|
|
}
|
|
return StringRef();
|
|
}
|
|
};
|
|
|
|
template <> struct MappingTraits<ClangTidyOptions::StringPair> {
|
|
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
|
|
IO.mapRequired("key", KeyValue.first);
|
|
IO.mapRequired("value", KeyValue.second);
|
|
}
|
|
};
|
|
|
|
struct NOptionMap {
|
|
NOptionMap(IO &) {}
|
|
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
|
|
: Options(OptionMap.begin(), OptionMap.end()) {}
|
|
ClangTidyOptions::OptionMap denormalize(IO &) {
|
|
ClangTidyOptions::OptionMap Map;
|
|
for (const auto &KeyValue : Options)
|
|
Map[KeyValue.first] = KeyValue.second;
|
|
return Map;
|
|
}
|
|
std::vector<ClangTidyOptions::StringPair> Options;
|
|
};
|
|
|
|
template <> struct MappingTraits<ClangTidyOptions> {
|
|
static void mapping(IO &IO, ClangTidyOptions &Options) {
|
|
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
|
|
IO, Options.CheckOptions);
|
|
IO.mapOptional("Checks", Options.Checks);
|
|
IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
|
|
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
|
|
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
|
|
IO.mapOptional("FormatStyle", Options.FormatStyle);
|
|
IO.mapOptional("User", Options.User);
|
|
IO.mapOptional("CheckOptions", NOpts->Options);
|
|
IO.mapOptional("ExtraArgs", Options.ExtraArgs);
|
|
IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
|
|
}
|
|
};
|
|
|
|
} // namespace yaml
|
|
} // namespace llvm
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
|
|
ClangTidyOptions ClangTidyOptions::getDefaults() {
|
|
ClangTidyOptions Options;
|
|
Options.Checks = "";
|
|
Options.WarningsAsErrors = "";
|
|
Options.HeaderFilterRegex = "";
|
|
Options.SystemHeaders = false;
|
|
Options.AnalyzeTemporaryDtors = false;
|
|
Options.FormatStyle = "none";
|
|
Options.User = llvm::None;
|
|
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
|
|
E = ClangTidyModuleRegistry::end();
|
|
I != E; ++I)
|
|
Options = Options.mergeWith(I->instantiate()->getModuleOptions());
|
|
return Options;
|
|
}
|
|
|
|
template <typename T>
|
|
static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
|
|
if (Src) {
|
|
if (Dest)
|
|
Dest->insert(Dest->end(), Src->begin(), Src->end());
|
|
else
|
|
Dest = Src;
|
|
}
|
|
}
|
|
|
|
static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
|
|
const Optional<std::string> &Src) {
|
|
if (Src)
|
|
Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
|
|
}
|
|
|
|
template <typename T>
|
|
static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
|
|
if (Src)
|
|
Dest = Src;
|
|
}
|
|
|
|
ClangTidyOptions
|
|
ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
|
|
ClangTidyOptions Result = *this;
|
|
|
|
mergeCommaSeparatedLists(Result.Checks, Other.Checks);
|
|
mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
|
|
overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
|
|
overrideValue(Result.SystemHeaders, Other.SystemHeaders);
|
|
overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
|
|
overrideValue(Result.FormatStyle, Other.FormatStyle);
|
|
overrideValue(Result.User, Other.User);
|
|
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
|
|
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
|
|
|
|
for (const auto &KeyValue : Other.CheckOptions)
|
|
Result.CheckOptions[KeyValue.first] = KeyValue.second;
|
|
|
|
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(
|
|
const ClangTidyGlobalOptions &GlobalOptions,
|
|
const ClangTidyOptions &DefaultOptions,
|
|
const ClangTidyOptions &OverrideOptions)
|
|
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
|
|
OverrideOptions(OverrideOptions) {
|
|
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
|
|
}
|
|
|
|
FileOptionsProvider::FileOptionsProvider(
|
|
const ClangTidyGlobalOptions &GlobalOptions,
|
|
const ClangTidyOptions &DefaultOptions,
|
|
const ClangTidyOptions &OverrideOptions,
|
|
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
|
|
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
|
|
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
|
|
|
|
// FIXME: This method has some common logic with clang::format::getStyle().
|
|
// Consider pulling out common bits to a findParentFileWithName function or
|
|
// similar.
|
|
std::vector<OptionsSource>
|
|
FileOptionsProvider::getRawOptions(StringRef FileName) {
|
|
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
|
|
|
|
std::vector<OptionsSource> 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; !CurrentPath.empty();
|
|
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
|
|
llvm::Optional<OptionsSource> Result;
|
|
|
|
auto Iter = CachedOptions.find(CurrentPath);
|
|
if (Iter != CachedOptions.end())
|
|
Result = Iter->second;
|
|
|
|
if (!Result)
|
|
Result = tryReadConfigFile(CurrentPath);
|
|
|
|
if (Result) {
|
|
// Store cached value for all intermediate directories.
|
|
while (Path != CurrentPath) {
|
|
DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
|
|
<< ".\n");
|
|
CachedOptions[Path] = *Result;
|
|
Path = llvm::sys::path::parent_path(Path);
|
|
}
|
|
CachedOptions[Path] = *Result;
|
|
|
|
RawOptions.push_back(*Result);
|
|
break;
|
|
}
|
|
}
|
|
RawOptions.push_back(CommandLineOptions);
|
|
return RawOptions;
|
|
}
|
|
|
|
llvm::Optional<OptionsSource>
|
|
FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
|
|
assert(!Directory.empty());
|
|
|
|
if (!llvm::sys::fs::is_directory(Directory)) {
|
|
llvm::errs() << "Error reading configuration from " << Directory
|
|
<< ": directory doesn't exist.\n";
|
|
return llvm::None;
|
|
}
|
|
|
|
for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
|
|
SmallString<128> ConfigFile(Directory);
|
|
llvm::sys::path::append(ConfigFile, ConfigHandler.first);
|
|
DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
|
|
|
|
bool IsFile = false;
|
|
// Ignore errors from is_regular_file: we only need to know if we can read
|
|
// the file or not.
|
|
llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
|
|
if (!IsFile)
|
|
continue;
|
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
|
|
llvm::MemoryBuffer::getFile(ConfigFile.c_str());
|
|
if (std::error_code EC = Text.getError()) {
|
|
llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
|
|
<< "\n";
|
|
continue;
|
|
}
|
|
|
|
// Skip empty files, e.g. files opened for writing via shell output
|
|
// redirection.
|
|
if ((*Text)->getBuffer().empty())
|
|
continue;
|
|
llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
|
|
ConfigHandler.second((*Text)->getBuffer());
|
|
if (!ParsedOptions) {
|
|
if (ParsedOptions.getError())
|
|
llvm::errs() << "Error parsing " << ConfigFile << ": "
|
|
<< ParsedOptions.getError().message() << "\n";
|
|
continue;
|
|
}
|
|
return OptionsSource(*ParsedOptions, ConfigFile.c_str());
|
|
}
|
|
return llvm::None;
|
|
}
|
|
|
|
/// \brief Parses -line-filter option and stores it to the \c Options.
|
|
std::error_code parseLineFilter(StringRef LineFilter,
|
|
clang::tidy::ClangTidyGlobalOptions &Options) {
|
|
llvm::yaml::Input Input(LineFilter);
|
|
Input >> Options.LineFilter;
|
|
return Input.error();
|
|
}
|
|
|
|
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
|
|
llvm::yaml::Input Input(Config);
|
|
ClangTidyOptions Options;
|
|
Input >> Options;
|
|
if (Input.error())
|
|
return Input.error();
|
|
return Options;
|
|
}
|
|
|
|
std::string configurationAsText(const ClangTidyOptions &Options) {
|
|
std::string Text;
|
|
llvm::raw_string_ostream Stream(Text);
|
|
llvm::yaml::Output Output(Stream);
|
|
// We use the same mapping method for input and output, so we need a non-const
|
|
// reference here.
|
|
ClangTidyOptions NonConstValue = Options;
|
|
Output << NonConstValue;
|
|
return Stream.str();
|
|
}
|
|
|
|
} // namespace tidy
|
|
} // namespace clang
|