Added/updated comments, moved the loop over config handlers inside
TryReadConfigFile.

http://reviews.llvm.org/D5821

Files:
  clang-tidy/ClangTidyOptions.cpp
  clang-tidy/ClangTidyOptions.h
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.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
@@ -164,14 +173,14 @@
   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())
       Result = Iter->second;
 
     if (!Result)
-      Result = TryReadConfigFile(CurrentPath);
+        Result = TryReadConfigFile(CurrentPath);
 
     if (Result) {
       // Store cached value for all intermediate directories.
@@ -183,49 +192,57 @@
       }
       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);
+  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;
+    }
 
-  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) {
     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.
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 <functional>
 #include <map>
 #include <string>
 #include <system_error>
@@ -114,32 +115,85 @@
 };
 
 /// \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.
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to