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
This commit is contained in:
Alexander Kornienko 2014-05-07 09:06:53 +00:00
parent d103130ee0
commit 5d174547a9
8 changed files with 83 additions and 26 deletions

View File

@ -298,10 +298,10 @@ std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
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 @@ void runClangTidy(const ClangTidyOptions &Options,
};
Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context, Options)));
return Context.getStats();
}
void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {

View File

@ -121,10 +121,10 @@ private:
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.

View File

@ -41,8 +41,6 @@ protected:
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 @@ ClangTidyMessage::ClangTidyMessage(StringRef Message,
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 @@ DiagnosticBuilder ClangTidyContext::diag(
++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 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
}
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 @@ struct LessClangTidyError {
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();

View File

@ -68,6 +68,17 @@ private:
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 @@ public:
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 @@ private:
DiagnosticsEngine *DiagEngine;
ClangTidyOptions Options;
ChecksFilter Filter;
ClangTidyStats Stats;
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
};

View File

@ -59,6 +59,31 @@ static cl::opt<bool> AnalyzeTemporaryDtors(
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 @@ int main(int argc, const char **argv) {
}
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;
}

View File

@ -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 @@ class A { A(int); };
// CHECK-NOT: warning:
// CHECK2-NOT: warning:
// CHECK3-NOT: warning:
// CHECK: Suppressed 2 warnings (2 in non-user code)
// CHECK: Use -header-filter='.*' to display errors from all non-system headers.
// CHECK2-NOT: Suppressed {{.*}} warnings
// CHECK2-NOT: Use -header-filter='.*' {{.*}}
// CHECK3: Suppressed 1 warnings (1 in non-user code)
// CHECK3: Use -header-filter='.*' {{.*}}

View File

@ -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 B { B(int i); }; // NOLINT
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: Suppressed 2 warnings (2 NOLINT)

View File

@ -78,7 +78,7 @@ void Positive() {
// CHECK: nullptr != sp->get();
}
// CHECK-NOT: warning
// CHECK-NOT: warning:
void Negative() {
struct NegPtr {