Add clang-tidy -line-filter option to filter findings by line ranges.

Summary:
This is going to be used for a clang-tidy-diff script to display
warnings in changed lines only. The option uses JSON, as its value is not
intended to be entered manually.

Reviewers: klimek

Reviewed By: klimek

Subscribers: cfe-commits

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

llvm-svn: 209450
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index a43af88..41b66d6 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -32,6 +32,7 @@
     "-clang-analyzer-alpha*,"  // Too many false positives.
     "-llvm-include-order,"     // Not implemented yet.
     "-google-*,";              // Doesn't apply to LLVM.
+
 static cl::opt<std::string>
 Checks("checks", cl::desc("Comma-separated list of globs with optional '-'\n"
                           "prefix. Globs are processed in order of appearance\n"
@@ -40,13 +41,28 @@
                           "prefix remove checks with matching names from the\n"
                           "set of enabled checks."),
        cl::init(""), cl::cat(ClangTidyCategory));
+
 static cl::opt<std::string>
 HeaderFilter("header-filter",
              cl::desc("Regular expression matching the names of the\n"
-                      "headers to output diagnostics from.\n"
-                      "Diagnostics from the main file of each\n"
-                      "translation unit are always displayed."),
+                      "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."),
              cl::init(""), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string>
+LineFilter("line-filter",
+           cl::desc("List of files with line ranges to filter the\n"
+                    "warnings. Can be used together with\n"
+                    "-header-filter. The format of the list is a JSON\n"
+                    "array of objects:\n"
+                    "  [\n"
+                    "    {\"name\":\"file1.cpp\",\"lines\":[[1,3],[5,7]]},\n"
+                    "    {\"name\":\"file2.h\"}\n"
+                    "  ]"),
+           cl::init(""), cl::cat(ClangTidyCategory));
+
 static cl::opt<bool> Fix("fix", cl::desc("Fix detected errors if possible."),
                          cl::init(false), cl::cat(ClangTidyCategory));
 
@@ -63,16 +79,18 @@
                       cl::init(false), cl::cat(ClangTidyCategory));
 
 static void printStats(const clang::tidy::ClangTidyStats &Stats) {
-  unsigned ErrorsIgnored = Stats.ErrorsIgnoredNOLINT +
-                           Stats.ErrorsIgnoredCheckFilter +
-                           Stats.ErrorsIgnoredNonUserCode;
-  if (ErrorsIgnored) {
-    llvm::errs() << "Suppressed " << ErrorsIgnored << " warnings (";
+  if (Stats.errorsIgnored()) {
+    llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings (";
     StringRef Separator = "";
     if (Stats.ErrorsIgnoredNonUserCode) {
       llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";
       Separator = ", ";
     }
+    if (Stats.ErrorsIgnoredLineFilter) {
+      llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter
+                   << " due to line filter";
+      Separator = ", ";
+    }
     if (Stats.ErrorsIgnoredNOLINT) {
       llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
       Separator = ", ";
@@ -94,6 +112,12 @@
   Options.Checks = DefaultChecks + Checks;
   Options.HeaderFilterRegex = HeaderFilter;
   Options.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+  if (llvm::error_code Err =
+          clang::tidy::parseLineFilter(LineFilter, Options)) {
+    llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";
+    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+    return 1;
+  }
 
   // FIXME: Allow using --list-checks without positional arguments.
   if (ListChecks) {