Index: clang-tidy/ClangTidy.h =================================================================== --- clang-tidy/ClangTidy.h +++ clang-tidy/ClangTidy.h @@ -121,10 +121,10 @@ std::vector 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 Ranges, - SmallVectorImpl *Errors); +ClangTidyStats runClangTidy(const ClangTidyOptions &Options, + const tooling::CompilationDatabase &Compilations, + ArrayRef Ranges, + SmallVectorImpl *Errors); // FIXME: This interface will need to be significantly extended to be useful. // FIXME: Implement confidence levels for displaying/fixing errors. Index: clang-tidy/ClangTidy.cpp =================================================================== --- clang-tidy/ClangTidy.cpp +++ clang-tidy/ClangTidy.cpp @@ -298,10 +298,10 @@ return Factory.getCheckNames(); } -void runClangTidy(const ClangTidyOptions &Options, - const tooling::CompilationDatabase &Compilations, - ArrayRef Ranges, - SmallVectorImpl *Errors) { +ClangTidyStats runClangTidy(const ClangTidyOptions &Options, + const tooling::CompilationDatabase &Compilations, + ArrayRef Ranges, + SmallVectorImpl *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 &Errors, bool Fix) { Index: clang-tidy/ClangTidyDiagnosticConsumer.h =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.h +++ 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 CheckNamesByDiagnosticID; }; Index: clang-tidy/ClangTidyDiagnosticConsumer.cpp =================================================================== --- clang-tidy/ClangTidyDiagnosticConsumer.cpp +++ clang-tidy/ClangTidyDiagnosticConsumer.cpp @@ -41,8 +41,6 @@ ArrayRef 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 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(); Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -59,6 +59,21 @@ cl::init(false), cl::cat(ClangTidyCategory)); +static void printStats(const clang::tidy::ClangTidyStats &Stats) { + llvm::errs() << "Warnings: displayed " << Stats.ErrorsDisplayed; + if (Stats.ErrorsIgnoredNonUserCode) + llvm::errs() << ", in non-user code " << Stats.ErrorsIgnoredNonUserCode; + if (Stats.ErrorsIgnoredNOLINT) + llvm::errs() << ", suppressed with NOLINT " << Stats.ErrorsIgnoredNOLINT; + if (Stats.ErrorsIgnoredCheckFilter) + llvm::errs() << ", suppressed with check filters " + << Stats.ErrorsIgnoredCheckFilter; + 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 +93,12 @@ } SmallVector 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; } Index: test/clang-tidy/file-filter.cpp =================================================================== --- test/clang-tidy/file-filter.cpp +++ test/clang-tidy/file-filter.cpp @@ -1,6 +1,6 @@ -// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='' %s -- -I %S/Inputs/file-filter | FileCheck %s -// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='.*' %s -- -I %S/Inputs/file-filter | FileCheck --check-prefix=CHECK2 %s -// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='header2\.h' %s -- -I %S/Inputs/file-filter | FileCheck --check-prefix=CHECK3 %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='' %s -- -I %S/Inputs/file-filter 2>&1 | FileCheck %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='.*' %s -- -I %S/Inputs/file-filter 2>&1 | FileCheck --check-prefix=CHECK2 %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' -header-filter='header2\.h' %s -- -I %S/Inputs/file-filter 2>&1 | FileCheck --check-prefix=CHECK3 %s #include "header1.h" // CHECK-NOT: warning: @@ -20,3 +20,10 @@ // CHECK-NOT: warning: // CHECK2-NOT: warning: // CHECK3-NOT: warning: + +// CHECK: Warnings: displayed 1, in non-user code 2 +// CHECK: Use -header-filter='.*' to display errors from all non-system headers. +// CHECK2: Warnings: displayed 3 +// CHECK-NOT: Use -header-filter='.*' {{.*}} +// CHECK3: Warnings: displayed 2, in non-user code 1 +// CHECK3: Use -header-filter='.*' {{.*}} Index: test/clang-tidy/nolint.cpp =================================================================== --- test/clang-tidy/nolint.cpp +++ test/clang-tidy/nolint.cpp @@ -1,4 +1,4 @@ -// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' %s -- | FileCheck %s +// RUN: clang-tidy -checks=google-explicit-constructor -disable-checks='' %s -- 2>&1 | FileCheck %s class A { A(int i); }; // CHECK: :[[@LINE-1]]:11: warning: Single-argument constructors must be explicit [google-explicit-constructor] @@ -8,3 +8,4 @@ class C { C(int i); }; // NOLINT(we-dont-care-about-categories-yet) // CHECK-NOT: :[[@LINE-1]]:11: warning: Single-argument constructors must be explicit [google-explicit-constructor] +// CHECK: Warnings: displayed 1, suppressed with NOLINT 2 Index: test/clang-tidy/redundant-smartptr-get.cpp =================================================================== --- test/clang-tidy/redundant-smartptr-get.cpp +++ test/clang-tidy/redundant-smartptr-get.cpp @@ -78,7 +78,7 @@ // CHECK: nullptr != sp->get(); } -// CHECK-NOT: warning +// CHECK-NOT: warning: void Negative() { struct NegPtr {