Index: clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tidy/ClangTidyOptions.h +++ clang-tidy/ClangTidyOptions.h @@ -14,6 +14,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorOr.h" +#include #include #include #include @@ -114,32 +115,61 @@ }; /// \brief Implementation of the \c ClangTidyOptionsProvider interface, which -/// tries to find a .clang-tidy file in the closest parent directory of each -/// file. +/// tries to find a configuration file in the closest parent directory of each +/// source file. By default, files named ".clang-tidy" will be considered, and +/// will be parsed using \c clang::tidy::parseConfiguration, but a custom set of +/// configuration file names and parsing functions can be specified. class FileOptionsProvider : public DefaultOptionsProvider { public: + typedef std::pair( + llvm::StringRef)>> ConfigFileHandler; + typedef std::vector ConfigFileHandlers; + /// \brief Initializes the \c FileOptionsProvider instance. /// /// \param GlobalOptions are just stored and returned to the caller of /// \c getGlobalOptions. /// /// \param DefaultOptions are used for all settings not specified in a - /// .clang-tidy file. + /// configuration file. /// /// If any of the \param OverrideOptions fields are set, they will override /// whatever options are read from the configuration file. FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions); + + /// \brief Initializes the \c FileOptionsProvider instance with a custom set + /// of configuration file handlers. + /// + /// \param GlobalOptions are just stored and returned to the caller of + /// \c getGlobalOptions. + /// + /// \param DefaultOptions are used for all settings not specified in a + /// configuration file. + /// + /// If any of the \param OverrideOptions fields are set, they will override + /// whatever options are read from the configuration file. + /// + /// \param ConfigHandlers specifies a custom set of configuration file + /// handlers. Each handler is a pair of configuration file name and a function + /// that can parse configuration from this file type. + FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + const ConfigFileHandlers &ConfigHandlers); const ClangTidyOptions &getOptions(llvm::StringRef FileName) override; private: /// \brief Try to read configuration file from \p Directory. If \p Directory /// is empty, use the default value. - llvm::ErrorOr TryReadConfigFile(llvm::StringRef Directory); + llvm::ErrorOr + TryReadConfigFile(llvm::StringRef Directory, + const ConfigFileHandler &ConfigHandler); llvm::StringMap CachedOptions; ClangTidyOptions OverrideOptions; + ConfigFileHandlers ConfigHandlers; }; /// \brief Parses LineFilter from JSON and stores it to the \p Options. Index: clang-tidy/ClangTidyOptions.cpp =================================================================== --- clang-tidy/ClangTidyOptions.cpp +++ clang-tidy/ClangTidyOptions.cpp @@ -139,10 +139,19 @@ const ClangTidyOptions &OverrideOptions) : DefaultOptionsProvider(GlobalOptions, DefaultOptions), OverrideOptions(OverrideOptions) { + ConfigHandlers.push_back(std::make_pair(".clang-tidy", parseConfiguration)); CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions); } -static const char ConfigFileName[] = ".clang-tidy"; +FileOptionsProvider::FileOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers) + : DefaultOptionsProvider(GlobalOptions, DefaultOptions), + OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) { + CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions); +} // FIXME: This method has some common logic with clang::format::getStyle(). // Consider pulling out common bits to a findParentFileWithName function or @@ -170,8 +179,17 @@ if (Iter != CachedOptions.end()) Result = Iter->second; - if (!Result) - Result = TryReadConfigFile(CurrentPath); + if (!Result) { + for (const auto& Handler : ConfigHandlers) { + Result = TryReadConfigFile(CurrentPath, Handler); + if (Result) + break; + if (Result.getError() != llvm::errc::no_such_file_or_directory) { + llvm::errs() << "Error reading " << Handler.first << " from " << Path + << ": " << Result.getError().message() << "\n"; + } + } + } if (Result) { // Store cached value for all intermediate directories. @@ -183,22 +201,19 @@ } return CachedOptions.GetOrCreateValue(Path, *Result).getValue(); } - if (Result.getError() != llvm::errc::no_such_file_or_directory) { - llvm::errs() << "Error reading " << ConfigFileName << " from " << Path - << ": " << Result.getError().message() << "\n"; - } } } -llvm::ErrorOr -FileOptionsProvider::TryReadConfigFile(StringRef Directory) { +llvm::ErrorOr FileOptionsProvider::TryReadConfigFile( + StringRef Directory, + const FileOptionsProvider::ConfigFileHandler &ConfigHandler) { assert(!Directory.empty()); if (!llvm::sys::fs::is_directory(Directory)) return make_error_code(llvm::errc::not_a_directory); SmallString<128> ConfigFile(Directory); - llvm::sys::path::append(ConfigFile, ".clang-tidy"); + llvm::sys::path::append(ConfigFile, ConfigHandler.first); DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n"); bool IsFile = false; @@ -218,7 +233,7 @@ if ((*Text)->getBuffer().empty()) return make_error_code(llvm::errc::no_such_file_or_directory); llvm::ErrorOr ParsedOptions = - parseConfiguration((*Text)->getBuffer()); + ConfigHandler.second((*Text)->getBuffer()); if (ParsedOptions) { ClangTidyOptions Defaults = DefaultOptionsProvider::getOptions(Directory); // Only use checks from the config file.