diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp')
-rw-r--r-- | contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp new file mode 100644 index 0000000000..808929b11f --- /dev/null +++ b/contrib/libs/clang16/tools/extra/clang-tidy/ClangTidyOptions.cpp @@ -0,0 +1,452 @@ +//===--- 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 |