123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- //===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #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/MemoryBufferRef.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/YAMLTraits.h"
- #include <optional>
- #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::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 std::string 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 "";
- }
- };
- 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.reserve(OptionMap.size());
- for (const auto &KeyValue : OptionMap)
- Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value);
- }
- ClangTidyOptions::OptionMap denormalize(IO &) {
- ClangTidyOptions::OptionMap Map;
- for (const auto &KeyValue : Options)
- Map[KeyValue.first] = ClangTidyOptions::ClangTidyValue(KeyValue.second);
- return Map;
- }
- std::vector<ClangTidyOptions::StringPair> Options;
- };
- template <>
- void yamlize(IO &IO, ClangTidyOptions::OptionMap &Options, bool,
- EmptyContext &Ctx) {
- if (IO.outputting()) {
- IO.beginMapping();
- // Only output as a map
- for (auto &Key : Options) {
- bool UseDefault;
- void *SaveInfo;
- IO.preflightKey(Key.getKey().data(), true, false, UseDefault, SaveInfo);
- StringRef S = Key.getValue().Value;
- IO.scalarString(S, needsQuotes(S));
- IO.postflightKey(SaveInfo);
- }
- IO.endMapping();
- } else {
- // We need custom logic here to support the old method of specifying check
- // options using a list of maps containing key and value keys.
- Input &I = reinterpret_cast<Input &>(IO);
- if (isa<SequenceNode>(I.getCurrentNode())) {
- MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
- IO, Options);
- EmptyContext Ctx;
- yamlize(IO, NOpts->Options, true, Ctx);
- } else if (isa<MappingNode>(I.getCurrentNode())) {
- IO.beginMapping();
- for (StringRef Key : IO.keys()) {
- IO.mapRequired(Key.data(), Options[Key].Value);
- }
- IO.endMapping();
- } else {
- IO.setError("expected a sequence or map");
- }
- }
- }
- template <> struct MappingTraits<ClangTidyOptions> {
- static void mapping(IO &IO, ClangTidyOptions &Options) {
- bool Ignored = false;
- IO.mapOptional("Checks", Options.Checks);
- IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
- IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
- IO.mapOptional("AnalyzeTemporaryDtors", Ignored); // deprecated
- IO.mapOptional("FormatStyle", Options.FormatStyle);
- IO.mapOptional("User", Options.User);
- IO.mapOptional("CheckOptions", Options.CheckOptions);
- IO.mapOptional("ExtraArgs", Options.ExtraArgs);
- IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
- IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
- IO.mapOptional("UseColor", Options.UseColor);
- }
- };
- } // namespace llvm::yaml
- namespace clang::tidy {
- ClangTidyOptions ClangTidyOptions::getDefaults() {
- ClangTidyOptions Options;
- Options.Checks = "";
- Options.WarningsAsErrors = "";
- Options.HeaderFilterRegex = "";
- Options.SystemHeaders = false;
- Options.FormatStyle = "none";
- Options.User = std::nullopt;
- for (const ClangTidyModuleRegistry::entry &Module :
- ClangTidyModuleRegistry::entries())
- Options.mergeWith(Module.instantiate()->getModuleOptions(), 0);
- return Options;
- }
- template <typename T>
- static void mergeVectors(std::optional<T> &Dest, const std::optional<T> &Src) {
- if (Src) {
- if (Dest)
- Dest->insert(Dest->end(), Src->begin(), Src->end());
- else
- Dest = Src;
- }
- }
- static void mergeCommaSeparatedLists(std::optional<std::string> &Dest,
- const std::optional<std::string> &Src) {
- if (Src)
- Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
- }
- template <typename T>
- static void overrideValue(std::optional<T> &Dest, const std::optional<T> &Src) {
- if (Src)
- Dest = Src;
- }
- ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
- unsigned Order) {
- mergeCommaSeparatedLists(Checks, Other.Checks);
- mergeCommaSeparatedLists(WarningsAsErrors, Other.WarningsAsErrors);
- overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex);
- overrideValue(SystemHeaders, Other.SystemHeaders);
- overrideValue(FormatStyle, Other.FormatStyle);
- overrideValue(User, Other.User);
- overrideValue(UseColor, Other.UseColor);
- mergeVectors(ExtraArgs, Other.ExtraArgs);
- mergeVectors(ExtraArgsBefore, Other.ExtraArgsBefore);
- for (const auto &KeyValue : Other.CheckOptions) {
- CheckOptions.insert_or_assign(
- KeyValue.getKey(),
- ClangTidyValue(KeyValue.getValue().Value,
- KeyValue.getValue().Priority + Order));
- }
- return *this;
- }
- ClangTidyOptions ClangTidyOptions::merge(const ClangTidyOptions &Other,
- unsigned Order) const {
- ClangTidyOptions Result = *this;
- Result.mergeWith(Other, Order);
- 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;
- unsigned Priority = 0;
- for (auto &Source : getRawOptions(FileName))
- Result.mergeWith(Source.first, ++Priority);
- return Result;
- }
- std::vector<OptionsSource>
- DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
- std::vector<OptionsSource> Result;
- Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
- return Result;
- }
- ConfigOptionsProvider::ConfigOptionsProvider(
- ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
- ClangTidyOptions ConfigOptions, ClangTidyOptions OverrideOptions,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
- : FileOptionsBaseProvider(std::move(GlobalOptions),
- std::move(DefaultOptions),
- std::move(OverrideOptions), std::move(FS)),
- ConfigOptions(std::move(ConfigOptions)) {}
- std::vector<OptionsSource>
- ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
- std::vector<OptionsSource> RawOptions =
- DefaultOptionsProvider::getRawOptions(FileName);
- if (ConfigOptions.InheritParentConfig.value_or(false)) {
- LLVM_DEBUG(llvm::dbgs()
- << "Getting options for file " << FileName << "...\n");
- assert(FS && "FS must be set.");
- llvm::SmallString<128> AbsoluteFilePath(FileName);
- if (!FS->makeAbsolute(AbsoluteFilePath)) {
- addRawFileOptions(AbsoluteFilePath, RawOptions);
- }
- }
- RawOptions.emplace_back(ConfigOptions,
- OptionsSourceTypeConfigCommandLineOption);
- RawOptions.emplace_back(OverrideOptions,
- OptionsSourceTypeCheckCommandLineOption);
- return RawOptions;
- }
- FileOptionsBaseProvider::FileOptionsBaseProvider(
- ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
- ClangTidyOptions OverrideOptions,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
- : DefaultOptionsProvider(std::move(GlobalOptions),
- std::move(DefaultOptions)),
- OverrideOptions(std::move(OverrideOptions)), FS(std::move(VFS)) {
- if (!FS)
- FS = llvm::vfs::getRealFileSystem();
- ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
- }
- FileOptionsBaseProvider::FileOptionsBaseProvider(
- ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
- ClangTidyOptions OverrideOptions,
- FileOptionsBaseProvider::ConfigFileHandlers ConfigHandlers)
- : DefaultOptionsProvider(std::move(GlobalOptions),
- std::move(DefaultOptions)),
- OverrideOptions(std::move(OverrideOptions)),
- ConfigHandlers(std::move(ConfigHandlers)) {}
- void FileOptionsBaseProvider::addRawFileOptions(
- llvm::StringRef AbsolutePath, std::vector<OptionsSource> &CurOptions) {
- auto CurSize = CurOptions.size();
- // 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(AbsolutePath);
- for (StringRef CurrentPath = Path; !CurrentPath.empty();
- CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
- std::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) {
- LLVM_DEBUG(llvm::dbgs()
- << "Caching configuration for path " << Path << ".\n");
- if (!CachedOptions.count(Path))
- CachedOptions[Path] = *Result;
- Path = llvm::sys::path::parent_path(Path);
- }
- CachedOptions[Path] = *Result;
- CurOptions.push_back(*Result);
- if (!Result->first.InheritParentConfig.value_or(false))
- break;
- }
- }
- // Reverse order of file configs because closer configs should have higher
- // priority.
- std::reverse(CurOptions.begin() + CurSize, CurOptions.end());
- }
- FileOptionsProvider::FileOptionsProvider(
- ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
- ClangTidyOptions OverrideOptions,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
- : FileOptionsBaseProvider(std::move(GlobalOptions),
- std::move(DefaultOptions),
- std::move(OverrideOptions), std::move(VFS)) {}
- FileOptionsProvider::FileOptionsProvider(
- ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
- ClangTidyOptions OverrideOptions,
- FileOptionsBaseProvider::ConfigFileHandlers ConfigHandlers)
- : FileOptionsBaseProvider(
- std::move(GlobalOptions), std::move(DefaultOptions),
- std::move(OverrideOptions), std::move(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) {
- LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName
- << "...\n");
- assert(FS && "FS must be set.");
- llvm::SmallString<128> AbsoluteFilePath(FileName);
- if (FS->makeAbsolute(AbsoluteFilePath))
- return {};
- std::vector<OptionsSource> RawOptions =
- DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str());
- addRawFileOptions(AbsoluteFilePath, RawOptions);
- OptionsSource CommandLineOptions(OverrideOptions,
- OptionsSourceTypeCheckCommandLineOption);
- RawOptions.push_back(CommandLineOptions);
- return RawOptions;
- }
- std::optional<OptionsSource>
- FileOptionsBaseProvider::tryReadConfigFile(StringRef Directory) {
- assert(!Directory.empty());
- llvm::ErrorOr<llvm::vfs::Status> DirectoryStatus = FS->status(Directory);
- if (!DirectoryStatus || !DirectoryStatus->isDirectory()) {
- llvm::errs() << "Error reading configuration from " << Directory
- << ": directory doesn't exist.\n";
- return std::nullopt;
- }
- for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
- SmallString<128> ConfigFile(Directory);
- llvm::sys::path::append(ConfigFile, ConfigHandler.first);
- LLVM_DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
- llvm::ErrorOr<llvm::vfs::Status> FileStatus = FS->status(ConfigFile);
- if (!FileStatus || !FileStatus->isRegularFile())
- continue;
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
- FS->getBufferForFile(ConfigFile);
- 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(), ConfigFile});
- if (!ParsedOptions) {
- if (ParsedOptions.getError())
- llvm::errs() << "Error parsing " << ConfigFile << ": "
- << ParsedOptions.getError().message() << "\n";
- continue;
- }
- return OptionsSource(*ParsedOptions, std::string(ConfigFile));
- }
- return std::nullopt;
- }
- /// 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(llvm::MemoryBufferRef Config) {
- llvm::yaml::Input Input(Config);
- ClangTidyOptions Options;
- Input >> Options;
- if (Input.error())
- return Input.error();
- return Options;
- }
- static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx) {
- (*reinterpret_cast<DiagCallback *>(Ctx))(Diag);
- }
- llvm::ErrorOr<ClangTidyOptions>
- parseConfigurationWithDiags(llvm::MemoryBufferRef Config,
- DiagCallback Handler) {
- llvm::yaml::Input Input(Config, nullptr, Handler ? diagHandlerImpl : nullptr,
- &Handler);
- 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 clang::tidy
|