llvm-project/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h

266 lines
8.9 KiB
C
Raw Normal View History

//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#include "ClangTidyOptions.h"
[clang-tidy] Store checks profiling info as JSON files Summary: Continuation of D46504. Example output: ``` $ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp $ # Note that there won't be timings table printed to the console. $ cat *.json { "file": "/path/to/source.cpp", "timestamp": "2018-05-16 16:13:18.717446360", "profile": { "time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00, "time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01, "time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01 } } ``` There are two arguments that control profile storage: * `-store-check-profile=<prefix>` By default reports are printed in tabulated format to stderr. When this option is passed, these per-TU profiles are instead stored as JSON. If the prefix is not an absolute path, it is considered to be relative to the directory from where you have run :program:`clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks are resolved. Example: Let's suppose you have a source file named `example.cpp`, located in `/source` directory. * If you specify `-store-check-profile=/tmp`, then the profile will be saved to `/tmp/<timestamp>-example.cpp.json` * If you run :program:`clang-tidy` from within `/foo` directory, and specify `-store-check-profile=.`, then the profile will still be saved to `/foo/<timestamp>-example.cpp.json` Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman Reviewed By: alexfh, george.karpenkov, aaron.ballman Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D46602 llvm-svn: 334101
2018-06-06 23:07:51 +08:00
#include "ClangTidyProfiling.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
namespace clang {
class ASTContext;
class CompilerInstance;
namespace ast_matchers {
class MatchFinder;
}
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief A detected error complete with information to display diagnostic and
/// automatic fix.
///
/// This is used as an intermediate format to transport Diagnostics without a
/// dependency on a SourceManager.
///
/// FIXME: Make Diagnostics flexible enough to support this directly.
struct ClangTidyError : tooling::Diagnostic {
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
bool IsWarningAsError);
bool IsWarningAsError;
};
/// \brief Read-only set of strings represented as a list of positive and
/// negative globs. Positive globs add all matched strings to the set, negative
/// globs remove them in the order of appearance in the list.
class GlobList {
public:
/// \brief \p GlobList is a comma-separated list of globs (only '*'
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
GlobList(StringRef Globs);
/// \brief Returns \c true if the pattern matches \p S. The result is the last
/// matching glob's Positive flag.
bool contains(StringRef S) { return contains(S, false); }
private:
bool contains(StringRef S, bool Contains);
bool Positive;
llvm::Regex Regex;
std::unique_ptr<GlobList> NextGlob;
};
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
/// run.
struct ClangTidyStats {
ClangTidyStats()
: ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {}
unsigned ErrorsDisplayed;
unsigned ErrorsIgnoredCheckFilter;
unsigned ErrorsIgnoredNOLINT;
unsigned ErrorsIgnoredNonUserCode;
unsigned ErrorsIgnoredLineFilter;
unsigned errorsIgnored() const {
return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
}
};
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
/// provided by this context.
///
/// A \c ClangTidyCheck always has access to the active context to report
/// warnings like:
/// \code
/// Context->Diag(Loc, "Single-argument constructors must be explicit")
/// << FixItHint::CreateInsertion(Loc, "explicit ");
/// \endcode
class ClangTidyContext {
public:
/// \brief Initializes \c ClangTidyContext instance.
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
bool AllowEnablingAnalyzerAlphaCheckers = false);
/// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
// FIXME: this is required initialization, and should be a constructor param.
// Fix the context -> diag engine -> consumer -> context initialization cycle.
void setDiagnosticsEngine(DiagnosticsEngine *DiagEngine) {
this->DiagEngine = DiagEngine;
}
[clang-tidy] Optimize GlobList::contains With large lists of checks and large number of warnings GlobList::contains starts being ridiculously CPU hungry, since it runs regexp match per glob. Caching results of glob matching in a StringMap significantly speeds up check filtering even for small GlobLists. /tmp/q.cc: void f() { int I; {int I;} {int I;} {int I;} ... // 200k times } Before the patch: GlobList with 2 entries: $ time clang-tidy-old -checks=-*,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m3.826s user 0m3.176s sys 0m0.504s GlobList with 28 entries: $ time clang-tidy-old -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m5.000s user 0m4.744s sys 0m0.060s GlobList with 158 entries: $ time clang-tidy-old -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m13.920s user 0m13.636s sys 0m0.104s With the patch runtime is practically independent from the length of the GlobList: $ time clang-tidy-new -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m2.300s user 0m2.104s sys 0m0.044s llvm-svn: 303321
2017-05-18 09:13:51 +08:00
~ClangTidyContext();
/// \brief Report any errors detected using this method.
///
/// This is still under heavy development and will likely change towards using
/// tablegen'd diagnostic IDs.
/// FIXME: Figure out a way to manage ID spaces.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
StringRef Message,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
///
/// This is called from the \c ClangTidyCheck base class.
void setSourceManager(SourceManager *SourceMgr);
/// \brief Should be called when starting to process new translation unit.
void setCurrentFile(StringRef File);
/// \brief Returns the main file name of the current translation unit.
StringRef getCurrentFile() const { return CurrentFile; }
/// \brief Sets ASTContext for the current translation unit.
void setASTContext(ASTContext *Context);
/// \brief Gets the language options from the AST context.
const LangOptions &getLangOpts() const { return LangOpts; }
/// \brief Returns the name of the clang-tidy check which produced this
/// diagnostic ID.
StringRef getCheckName(unsigned DiagnosticID) const;
/// \brief Returns \c true if the check is enabled for the \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
bool isCheckEnabled(StringRef CheckName) const;
/// \brief Returns \c true if the check should be upgraded to error for the
/// \c CurrentFile.
bool treatAsError(StringRef CheckName) const;
/// \brief Returns global options.
const ClangTidyGlobalOptions &getGlobalOptions() const;
/// \brief Returns options for \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
const ClangTidyOptions &getOptions() const;
/// \brief Returns options for \c File. Does not change or depend on
/// \c CurrentFile.
ClangTidyOptions getOptionsForFile(StringRef File) const;
/// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
/// counters.
const ClangTidyStats &getStats() const { return Stats; }
/// \brief Control profile collection in clang-tidy.
void setEnableProfiling(bool Profile);
bool getEnableProfiling() const { return Profile; }
[clang-tidy] Store checks profiling info as JSON files Summary: Continuation of D46504. Example output: ``` $ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp $ # Note that there won't be timings table printed to the console. $ cat *.json { "file": "/path/to/source.cpp", "timestamp": "2018-05-16 16:13:18.717446360", "profile": { "time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00, "time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01, "time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01 } } ``` There are two arguments that control profile storage: * `-store-check-profile=<prefix>` By default reports are printed in tabulated format to stderr. When this option is passed, these per-TU profiles are instead stored as JSON. If the prefix is not an absolute path, it is considered to be relative to the directory from where you have run :program:`clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks are resolved. Example: Let's suppose you have a source file named `example.cpp`, located in `/source` directory. * If you specify `-store-check-profile=/tmp`, then the profile will be saved to `/tmp/<timestamp>-example.cpp.json` * If you run :program:`clang-tidy` from within `/foo` directory, and specify `-store-check-profile=.`, then the profile will still be saved to `/foo/<timestamp>-example.cpp.json` Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman Reviewed By: alexfh, george.karpenkov, aaron.ballman Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D46602 llvm-svn: 334101
2018-06-06 23:07:51 +08:00
/// \brief Control storage of profile date.
void setProfileStoragePrefix(StringRef ProfilePrefix);
llvm::Optional<ClangTidyProfiling::StorageParams>
getProfileStorageParams() const;
/// \brief Should be called when starting to process new translation unit.
void setCurrentBuildDirectory(StringRef BuildDirectory) {
CurrentBuildDirectory = BuildDirectory;
}
/// \brief Returns build directory of the current translation unit.
const std::string &getCurrentBuildDirectory() {
return CurrentBuildDirectory;
}
/// \brief If the experimental alpha checkers from the static analyzer can be
/// enabled.
bool canEnableAnalyzerAlphaCheckers() const {
return AllowEnablingAnalyzerAlphaCheckers;
}
private:
// Writes to Stats.
friend class ClangTidyDiagnosticConsumer;
DiagnosticsEngine *DiagEngine;
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
std::string CurrentFile;
ClangTidyOptions CurrentOptions;
[clang-tidy] Optimize GlobList::contains With large lists of checks and large number of warnings GlobList::contains starts being ridiculously CPU hungry, since it runs regexp match per glob. Caching results of glob matching in a StringMap significantly speeds up check filtering even for small GlobLists. /tmp/q.cc: void f() { int I; {int I;} {int I;} {int I;} ... // 200k times } Before the patch: GlobList with 2 entries: $ time clang-tidy-old -checks=-*,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m3.826s user 0m3.176s sys 0m0.504s GlobList with 28 entries: $ time clang-tidy-old -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m5.000s user 0m4.744s sys 0m0.060s GlobList with 158 entries: $ time clang-tidy-old -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m13.920s user 0m13.636s sys 0m0.104s With the patch runtime is practically independent from the length of the GlobList: $ time clang-tidy-new -checks=-*,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,modernize-use-override /tmp/q.cc -- -Wshadow 200000 warnings generated. Suppressed 200000 warnings (200000 with check filters). real 0m2.300s user 0m2.104s sys 0m0.044s llvm-svn: 303321
2017-05-18 09:13:51 +08:00
class CachedGlobList;
std::unique_ptr<CachedGlobList> CheckFilter;
std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
LangOptions LangOpts;
ClangTidyStats Stats;
std::string CurrentBuildDirectory;
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
bool Profile;
[clang-tidy] Store checks profiling info as JSON files Summary: Continuation of D46504. Example output: ``` $ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp $ # Note that there won't be timings table printed to the console. $ cat *.json { "file": "/path/to/source.cpp", "timestamp": "2018-05-16 16:13:18.717446360", "profile": { "time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00, "time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01, "time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01 } } ``` There are two arguments that control profile storage: * `-store-check-profile=<prefix>` By default reports are printed in tabulated format to stderr. When this option is passed, these per-TU profiles are instead stored as JSON. If the prefix is not an absolute path, it is considered to be relative to the directory from where you have run :program:`clang-tidy`. All `.` and `..` patterns in the path are collapsed, and symlinks are resolved. Example: Let's suppose you have a source file named `example.cpp`, located in `/source` directory. * If you specify `-store-check-profile=/tmp`, then the profile will be saved to `/tmp/<timestamp>-example.cpp.json` * If you run :program:`clang-tidy` from within `/foo` directory, and specify `-store-check-profile=.`, then the profile will still be saved to `/foo/<timestamp>-example.cpp.json` Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman Reviewed By: alexfh, george.karpenkov, aaron.ballman Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D46602 llvm-svn: 334101
2018-06-06 23:07:51 +08:00
std::string ProfilePrefix;
bool AllowEnablingAnalyzerAlphaCheckers;
};
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
/// \c SourceManager-independent \c ClangTidyError.
//
// FIXME: If we move away from unit-tests, this can be moved to a private
// implementation file.
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
public:
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
bool RemoveIncompatibleErrors = true);
// FIXME: The concept of converting between FixItHints and Replacements is
// more generic and should be pulled out into a more useful Diagnostics
// library.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) override;
// Retrieve the diagnostics that were captured.
std::vector<ClangTidyError> take();
private:
void finalizeLastError();
void removeIncompatibleErrors();
/// \brief Returns the \c HeaderFilter constructed for the options set in the
/// context.
llvm::Regex *getHeaderFilter();
/// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
/// according to the diagnostic \p Location.
void checkFilters(SourceLocation Location, const SourceManager& Sources);
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
ClangTidyContext &Context;
bool RemoveIncompatibleErrors;
std::vector<ClangTidyError> Errors;
std::unique_ptr<llvm::Regex> HeaderFilter;
bool LastErrorRelatesToUserCode;
bool LastErrorPassesLineFilter;
bool LastErrorWasIgnored;
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H