const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions) {
+ ConfigHandlers.emplace_back(".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
StringRef Path = llvm::sys::path::parent_path(FileName);
for (StringRef CurrentPath = Path;;
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
- llvm::ErrorOr<ClangTidyOptions> Result = std::error_code();
+ llvm::Optional<ClangTidyOptions> Result;
auto Iter = CachedOptions.find(CurrentPath);
if (Iter != CachedOptions.end())
}
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<ClangTidyOptions>
+llvm::Optional<ClangTidyOptions>
FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
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");
- 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)
- return make_error_code(llvm::errc::no_such_file_or_directory);
-
- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
- llvm::MemoryBuffer::getFile(ConfigFile.c_str());
- if (std::error_code EC = Text.getError())
- return EC;
- // Skip empty files, e.g. files opened for writing via shell output
- // redirection.
- if ((*Text)->getBuffer().empty())
- return make_error_code(llvm::errc::no_such_file_or_directory);
- llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
- parseConfiguration((*Text)->getBuffer());
- if (ParsedOptions) {
+ 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) {
+ llvm::errs() << "Error parsing " << ConfigFile << ": "
+ << ParsedOptions.getError().message() << "\n";
+ continue;
+ }
+
ClangTidyOptions Defaults = DefaultOptionsProvider::getOptions(Directory);
// Only use checks from the config file.
Defaults.Checks = None;
return Defaults.mergeWith(*ParsedOptions).mergeWith(OverrideOptions);
}
- return ParsedOptions.getError();
+ return llvm::None;
}
/// \brief Parses -line-filter option and stores it to the \c Options.
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h"
+#include <functional>
#include <map>
#include <string>
#include <system_error>
};
/// \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 the
+/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
+/// custom set of configuration file names and parsing functions can be
+/// specified using the appropriate constructor.
class FileOptionsProvider : public DefaultOptionsProvider {
public:
+ // \brief A pair of configuration file base name and a function parsing
+ // configuration from text in the corresponding format.
+ typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
+ llvm::StringRef)>> ConfigFileHandler;
+
+ /// \brief Configuration file handlers listed in the order of priority.
+ ///
+ /// Custom configuration file formats can be supported by constructing the
+ /// list of handlers and passing it to the appropriate \c FileOptionsProvider
+ /// constructor. E.g. initialization of a \c FileOptionsProvider with support
+ /// of a custom configuration file format for files named ".my-tidy-config"
+ /// could look similar to this:
+ /// \code
+ /// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
+ /// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
+ /// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
+ /// return llvm::make_unique<FileOptionsProvider>(
+ /// GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
+ /// \endcode
+ ///
+ /// With the order of handlers shown above, the ".my-tidy-config" file would
+ /// take precedence over ".clang-tidy" if both reside in the same directory.
+ typedef std::vector<ConfigFileHandler> 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. The configuration files
+ /// in each directory are searched for in the order of appearance in
+ /// \p ConfigHandlers.
+ 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<ClangTidyOptions> TryReadConfigFile(llvm::StringRef Directory);
+ /// \brief Try to read configuration files from \p Directory using registered
+ /// \c ConfigHandlers.
+ llvm::Optional<ClangTidyOptions> TryReadConfigFile(llvm::StringRef Directory);
llvm::StringMap<ClangTidyOptions> CachedOptions;
ClangTidyOptions OverrideOptions;
+ ConfigFileHandlers ConfigHandlers;
};
/// \brief Parses LineFilter from JSON and stores it to the \p Options.