Print stats on displayed and ignored warnings.

Summary:
Also displays a hint to use -header-filter='.*' in case any warnings
are in non-user code. This will help discoverability of this option.

Reviewers: klimek

Reviewed By: klimek

Subscribers: cfe-commits

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

llvm-svn: 208174
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp
index 0445710..e44dab3 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp
@@ -298,10 +298,10 @@
   return Factory.getCheckNames();
 }
 
-void runClangTidy(const ClangTidyOptions &Options,
-                  const tooling::CompilationDatabase &Compilations,
-                  ArrayRef<std::string> Ranges,
-                  SmallVectorImpl<ClangTidyError> *Errors) {
+ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
+                            const tooling::CompilationDatabase &Compilations,
+                            ArrayRef<std::string> Ranges,
+                            SmallVectorImpl<ClangTidyError> *Errors) {
   // FIXME: Ranges are currently full files. Support selecting specific
   // (line-)ranges.
   ClangTool Tool(Compilations, Ranges);
@@ -333,6 +333,7 @@
   };
 
   Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context, Options)));
+  return Context.getStats();
 }
 
 void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {
diff --git a/clang-tools-extra/clang-tidy/ClangTidy.h b/clang-tools-extra/clang-tidy/ClangTidy.h
index 8d71eeb..1bc3e29 100644
--- a/clang-tools-extra/clang-tidy/ClangTidy.h
+++ b/clang-tools-extra/clang-tidy/ClangTidy.h
@@ -121,10 +121,10 @@
 std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
 
 /// \brief Run a set of clang-tidy checks on a set of files.
-void runClangTidy(const ClangTidyOptions &Options,
-                  const tooling::CompilationDatabase &Compilations,
-                  ArrayRef<std::string> Ranges,
-                  SmallVectorImpl<ClangTidyError> *Errors);
+ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
+                            const tooling::CompilationDatabase &Compilations,
+                            ArrayRef<std::string> Ranges,
+                            SmallVectorImpl<ClangTidyError> *Errors);
 
 // FIXME: This interface will need to be significantly extended to be useful.
 // FIXME: Implement confidence levels for displaying/fixing errors.
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
index fdf1f0c..ad30898 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp
@@ -41,8 +41,6 @@
                              ArrayRef<CharSourceRange> Ranges,
                              const SourceManager *SM,
                              DiagOrStoredDiag Info) override {
-    if (Level == DiagnosticsEngine::Ignored)
-      return;
     ClangTidyMessage TidyMessage = Loc.isValid()
                                        ? ClangTidyMessage(Message, *SM, Loc)
                                        : ClangTidyMessage(Message);
@@ -111,8 +109,7 @@
   FileOffset = Sources.getFileOffset(Loc);
 }
 
-ClangTidyError::ClangTidyError(StringRef CheckName)
-    : CheckName(CheckName) {}
+ClangTidyError::ClangTidyError(StringRef CheckName) : CheckName(CheckName) {}
 
 ChecksFilter::ChecksFilter(const ClangTidyOptions &Options)
     : EnableChecks(Options.EnableChecksRegex),
@@ -139,8 +136,10 @@
       ++P;
     StringRef RestOfLine(CharacterData, P - CharacterData + 1);
     // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
-    if (RestOfLine.find("NOLINT") != StringRef::npos)
+    if (RestOfLine.find("NOLINT") != StringRef::npos) {
       Level = DiagnosticIDs::Ignored;
+      ++Stats.ErrorsIgnoredNOLINT;
+    }
   }
   unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
       Level, (Description + " [" + CheckName + "]").str());
@@ -181,8 +180,18 @@
 }
 
 void ClangTidyDiagnosticConsumer::finalizeLastError() {
-  if (!LastErrorRelatesToUserCode && !Errors.empty())
-    Errors.pop_back();
+  if (!Errors.empty()) {
+    ClangTidyError &Error = Errors.back();
+    if (!Context.getChecksFilter().isCheckEnabled(Error.CheckName)) {
+      ++Context.Stats.ErrorsIgnoredCheckFilter;
+      Errors.pop_back();
+    } else if (!LastErrorRelatesToUserCode) {
+      ++Context.Stats.ErrorsIgnoredNonUserCode;
+      Errors.pop_back();
+    } else {
+      ++Context.Stats.ErrorsDisplayed;
+    }
+  }
   LastErrorRelatesToUserCode = false;
 }
 
@@ -259,10 +268,9 @@
 void ClangTidyDiagnosticConsumer::finish() {
   finalizeLastError();
   std::set<const ClangTidyError*, LessClangTidyError> UniqueErrors;
-  for (const ClangTidyError &Error : Errors) {
-    if (Context.getChecksFilter().isCheckEnabled(Error.CheckName))
-      UniqueErrors.insert(&Error);
-  }
+  for (const ClangTidyError &Error : Errors)
+    UniqueErrors.insert(&Error);
+
   for (const ClangTidyError *Error : UniqueErrors)
     Context.storeError(*Error);
   Errors.clear();
diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
index d944836..c030a67 100644
--- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
+++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h
@@ -68,6 +68,17 @@
   llvm::Regex DisableChecks;
 };
 
+struct ClangTidyStats {
+  ClangTidyStats()
+      : ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
+        ErrorsIgnoredNonUserCode(0) {}
+
+  unsigned ErrorsDisplayed;
+  unsigned ErrorsIgnoredCheckFilter;
+  unsigned ErrorsIgnoredNOLINT;
+  unsigned ErrorsIgnoredNonUserCode;
+};
+
 /// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticEngine
 /// provided by this context.
 ///
@@ -108,6 +119,7 @@
 
   ChecksFilter &getChecksFilter() { return Filter; }
   const ClangTidyOptions &getOptions() const { return Options; }
+  const ClangTidyStats &getStats() const { return Stats; }
 
 private:
   friend class ClangTidyDiagnosticConsumer; // Calls storeError().
@@ -119,6 +131,7 @@
   DiagnosticsEngine *DiagEngine;
   ClangTidyOptions Options;
   ChecksFilter Filter;
+  ClangTidyStats Stats;
 
   llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
 };
diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
index 17d1c78..624e6c0 100644
--- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
+++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp
@@ -59,6 +59,31 @@
     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 (";
+    StringRef Separator = "";
+    if (Stats.ErrorsIgnoredNonUserCode) {
+      llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";
+      Separator = ", ";
+    }
+    if (Stats.ErrorsIgnoredNOLINT) {
+      llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
+      Separator = ", ";
+    }
+    if (Stats.ErrorsIgnoredCheckFilter)
+      llvm::errs() << Separator << Stats.ErrorsIgnoredCheckFilter
+                   << " with check filters";
+    llvm::errs() << ").\n";
+    if (Stats.ErrorsIgnoredNonUserCode)
+      llvm::errs() << "Use -header-filter='.*' to display errors from all "
+                      "non-system headers.\n";
+  }
+}
+
 int main(int argc, const char **argv) {
   CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory);
 
@@ -78,10 +103,12 @@
   }
 
   SmallVector<clang::tidy::ClangTidyError, 16> Errors;
-  clang::tidy::runClangTidy(Options, OptionsParser.getCompilations(),
-                            OptionsParser.getSourcePathList(), &Errors);
+  clang::tidy::ClangTidyStats Stats =
+      clang::tidy::runClangTidy(Options, OptionsParser.getCompilations(),
+                                OptionsParser.getSourcePathList(), &Errors);
   clang::tidy::handleErrors(Errors, Fix);
 
+  printStats(Stats);
   return 0;
 }