Implemented clang-tidy configurability via .clang-tidy files.

Summary:
This adds a support for the .clang-tidy file reading using
FileOptionsProvider, -dump-config option, and changes tests to not depend on
default checks set.

Reviewers: klimek, bkramer, djasper

Reviewed By: djasper

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D5186

llvm-svn: 217155
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index d49994d..6048217 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -26,6 +26,13 @@
 static cl::OptionCategory ClangTidyCategory("clang-tidy options");
 
 static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::extrahelp ClangTidyHelp(
+    "Configuration files:\n"
+    "  clang-tidy attempts to read configuration for each source file from a\n"
+    "  .clang-tidy file located in the closest parent directory of the source\n"
+    "  file. If any configuration options have a corresponding command-line\n"
+    "  option, command-line option takes precedence. The effective\n"
+    "  configuration can be inspected using -dump-config.\n\n");
 
 const char DefaultChecks[] =
     "*,"                       // Enable all checks, except these:
@@ -39,7 +46,9 @@
                           "in the list. Globs without '-' prefix add checks\n"
                           "with matching names to the set, globs with the '-'\n"
                           "prefix remove checks with matching names from the\n"
-                          "set of enabled checks."),
+                          "set of enabled checks.\n"
+                          "This option's value is appended to the value read\n"
+                          "from a .clang-tidy file, if any."),
        cl::init(""), cl::cat(ClangTidyCategory));
 
 static cl::opt<std::string>
@@ -48,7 +57,9 @@
                       "headers to output diagnostics from. Diagnostics\n"
                       "from the main file of each translation unit are\n"
                       "always displayed.\n"
-                      "Can be used together with -line-filter."),
+                      "Can be used together with -line-filter.\n"
+                      "This option overrides the value read from a\n"
+                      ".clang-tidy file."),
              cl::init(""), cl::cat(ClangTidyCategory));
 
 static cl::opt<std::string>
@@ -73,10 +84,17 @@
            cl::init(false), cl::cat(ClangTidyCategory));
 
 static cl::opt<bool>
-AnalyzeTemporaryDtors("analyze-temporary-dtors",
-                      cl::desc("Enable temporary destructor-aware analysis in\n"
-                               "clang-analyzer- checks."),
-                      cl::init(false), cl::cat(ClangTidyCategory));
+DumpConfig("dump-config",
+           cl::desc("Dumps configuration in the YAML format to stdout."),
+           cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> AnalyzeTemporaryDtors(
+    "analyze-temporary-dtors",
+    cl::desc("Enable temporary destructor-aware analysis in\n"
+             "clang-analyzer- checks.\n"
+             "This option overrides the value read from a\n"
+             ".clang-tidy file."),
+    cl::init(false), cl::cat(ClangTidyCategory));
 
 static cl::opt<std::string> ExportFixes(
     "export-fixes",
@@ -123,12 +141,25 @@
     return 1;
   }
 
-  clang::tidy::ClangTidyOptions Options;
-  Options.Checks = DefaultChecks + Checks;
-  Options.HeaderFilterRegex = HeaderFilter;
-  Options.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+  clang::tidy::ClangTidyOptions FallbackOptions;
+  FallbackOptions.Checks = DefaultChecks;
+  FallbackOptions.HeaderFilterRegex = HeaderFilter;
+  FallbackOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
 
-  std::vector<std::string> EnabledChecks = clang::tidy::getCheckNames(Options);
+  clang::tidy::ClangTidyOptions OverrideOptions;
+  if (Checks.getNumOccurrences() > 0)
+    OverrideOptions.Checks = Checks;
+  if (HeaderFilter.getNumOccurrences() > 0)
+    OverrideOptions.HeaderFilterRegex = HeaderFilter;
+  if (AnalyzeTemporaryDtors.getNumOccurrences() > 0)
+    OverrideOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+
+  auto OptionsProvider = llvm::make_unique<clang::tidy::FileOptionsProvider>(
+      GlobalOptions, FallbackOptions, OverrideOptions);
+
+  std::string FileName = OptionsParser.getSourcePathList().front();
+  std::vector<std::string> EnabledChecks =
+      clang::tidy::getCheckNames(OptionsProvider->getOptions(FileName));
 
   // FIXME: Allow using --list-checks without positional arguments.
   if (ListChecks) {
@@ -139,18 +170,23 @@
     return 0;
   }
 
+  if (DumpConfig) {
+    llvm::outs() << clang::tidy::configurationAsText(
+                        clang::tidy::ClangTidyOptions::getDefaults()
+                            .mergeWith(OptionsProvider->getOptions(FileName)))
+                 << "\n";
+    return 0;
+  }
+
   if (EnabledChecks.empty()) {
     llvm::errs() << "Error: no checks enabled.\n";
     llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
     return 1;
   }
 
-  // TODO: Implement configuration file reading and a "real" options provider.
-  auto OptionsProvider =
-      new clang::tidy::DefaultOptionsProvider(GlobalOptions, Options);
   std::vector<clang::tidy::ClangTidyError> Errors;
   clang::tidy::ClangTidyStats Stats = clang::tidy::runClangTidy(
-      OptionsProvider, OptionsParser.getCompilations(),
+      std::move(OptionsProvider), OptionsParser.getCompilations(),
       OptionsParser.getSourcePathList(), &Errors);
   clang::tidy::handleErrors(Errors, Fix);