2017-05-09 22:56:28 +08:00
|
|
|
//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-05-09 22:56:28 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///
|
|
|
|
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
|
|
|
|
/// and ClangTidyError classes.
|
|
|
|
///
|
|
|
|
/// This tool uses the Clang Tooling infrastructure, see
|
|
|
|
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
|
|
|
|
/// for details on setting it up with LLVM source tree.
|
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "ClangTidyDiagnosticConsumer.h"
|
|
|
|
#include "ClangTidyOptions.h"
|
|
|
|
#include "clang/AST/ASTDiagnostic.h"
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
#include "clang/Basic/Diagnostic.h"
|
2017-05-09 22:56:28 +08:00
|
|
|
#include "clang/Basic/DiagnosticOptions.h"
|
|
|
|
#include "clang/Frontend/DiagnosticRenderer.h"
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
#include "clang/Tooling/Core/Diagnostic.h"
|
2017-12-15 00:13:57 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2017-05-09 22:56:28 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
|
|
#include <tuple>
|
|
|
|
#include <vector>
|
|
|
|
using namespace clang;
|
|
|
|
using namespace tidy;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
|
|
|
|
public:
|
|
|
|
ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
|
|
|
|
DiagnosticOptions *DiagOpts,
|
|
|
|
ClangTidyError &Error)
|
|
|
|
: DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
|
|
|
|
|
|
|
|
protected:
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
|
2017-05-09 22:56:28 +08:00
|
|
|
DiagnosticsEngine::Level Level, StringRef Message,
|
|
|
|
ArrayRef<CharSourceRange> Ranges,
|
|
|
|
DiagOrStoredDiag Info) override {
|
|
|
|
// Remove check name from the message.
|
|
|
|
// FIXME: Remove this once there's a better way to pass check names than
|
|
|
|
// appending the check name to the message in ClangTidyContext::diag and
|
|
|
|
// using getCustomDiagID.
|
|
|
|
std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
|
|
|
|
if (Message.endswith(CheckNameInMessage))
|
|
|
|
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
|
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
auto TidyMessage =
|
|
|
|
Loc.isValid()
|
|
|
|
? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
|
|
|
|
: tooling::DiagnosticMessage(Message);
|
2017-05-09 22:56:28 +08:00
|
|
|
if (Level == DiagnosticsEngine::Note) {
|
|
|
|
Error.Notes.push_back(TidyMessage);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
|
|
|
|
Error.Message = TidyMessage;
|
|
|
|
}
|
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
|
2017-05-09 22:56:28 +08:00
|
|
|
DiagnosticsEngine::Level Level,
|
2017-06-27 18:04:04 +08:00
|
|
|
ArrayRef<CharSourceRange> Ranges) override {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
|
2017-05-09 22:56:28 +08:00
|
|
|
SmallVectorImpl<CharSourceRange> &Ranges,
|
2017-06-27 18:04:04 +08:00
|
|
|
ArrayRef<FixItHint> Hints) override {
|
2017-05-09 22:56:28 +08:00
|
|
|
assert(Loc.isValid());
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
tooling::DiagnosticMessage *DiagWithFix =
|
|
|
|
Level == DiagnosticsEngine::Note ? &Error.Notes.back() : &Error.Message;
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
for (const auto &FixIt : Hints) {
|
|
|
|
CharSourceRange Range = FixIt.RemoveRange;
|
|
|
|
assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
|
|
|
|
"Invalid range in the fix-it hint.");
|
|
|
|
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
|
|
|
|
"Only file locations supported in fix-it hints.");
|
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
tooling::Replacement Replacement(Loc.getManager(), Range,
|
|
|
|
FixIt.CodeToInsert);
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
llvm::Error Err =
|
|
|
|
DiagWithFix->Fix[Replacement.getFilePath()].add(Replacement);
|
2017-05-09 22:56:28 +08:00
|
|
|
// FIXME: better error handling (at least, don't let other replacements be
|
|
|
|
// applied).
|
|
|
|
if (Err) {
|
|
|
|
llvm::errs() << "Fix conflicts with existing fix! "
|
|
|
|
<< llvm::toString(std::move(Err)) << "\n";
|
|
|
|
assert(false && "Fix conflicts with existing fix!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
|
|
|
StringRef ModuleName) override {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
2017-06-27 18:04:04 +08:00
|
|
|
void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
|
|
|
StringRef ModuleName) override {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
|
|
|
void endDiagnostic(DiagOrStoredDiag D,
|
|
|
|
DiagnosticsEngine::Level Level) override {
|
|
|
|
assert(!Error.Message.Message.empty() && "Message has not been set");
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ClangTidyError &Error;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
ClangTidyError::ClangTidyError(StringRef CheckName,
|
|
|
|
ClangTidyError::Level DiagLevel,
|
|
|
|
StringRef BuildDirectory, bool IsWarningAsError)
|
|
|
|
: tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
|
|
|
|
IsWarningAsError(IsWarningAsError) {}
|
|
|
|
|
|
|
|
// Returns true if GlobList starts with the negative indicator ('-'), removes it
|
|
|
|
// from the GlobList.
|
|
|
|
static bool ConsumeNegativeIndicator(StringRef &GlobList) {
|
2017-08-10 00:00:31 +08:00
|
|
|
GlobList = GlobList.trim(" \r\n");
|
2017-05-09 22:56:28 +08:00
|
|
|
if (GlobList.startswith("-")) {
|
|
|
|
GlobList = GlobList.substr(1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Converts first glob from the comma-separated list of globs to Regex and
|
|
|
|
// removes it and the trailing comma from the GlobList.
|
|
|
|
static llvm::Regex ConsumeGlob(StringRef &GlobList) {
|
|
|
|
StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
|
|
|
|
StringRef Glob = UntrimmedGlob.trim(' ');
|
|
|
|
GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
|
|
|
|
SmallString<128> RegexText("^");
|
|
|
|
StringRef MetaChars("()^$|*+?.[]\\{}");
|
|
|
|
for (char C : Glob) {
|
|
|
|
if (C == '*')
|
|
|
|
RegexText.push_back('.');
|
|
|
|
else if (MetaChars.find(C) != StringRef::npos)
|
|
|
|
RegexText.push_back('\\');
|
|
|
|
RegexText.push_back(C);
|
|
|
|
}
|
|
|
|
RegexText.push_back('$');
|
|
|
|
return llvm::Regex(RegexText);
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobList::GlobList(StringRef Globs)
|
|
|
|
: Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
|
|
|
|
NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
|
|
|
|
|
|
|
|
bool GlobList::contains(StringRef S, bool Contains) {
|
|
|
|
if (Regex.match(S))
|
|
|
|
Contains = Positive;
|
|
|
|
|
|
|
|
if (NextGlob)
|
|
|
|
Contains = NextGlob->contains(S, Contains);
|
|
|
|
return Contains;
|
|
|
|
}
|
|
|
|
|
[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 ClangTidyContext::CachedGlobList {
|
|
|
|
public:
|
|
|
|
CachedGlobList(StringRef Globs) : Globs(Globs) {}
|
|
|
|
|
|
|
|
bool contains(StringRef S) {
|
|
|
|
switch (auto &Result = Cache[S]) {
|
|
|
|
case Yes: return true;
|
|
|
|
case No: return false;
|
|
|
|
case None:
|
|
|
|
Result = Globs.contains(S) ? Yes : No;
|
|
|
|
return Result == Yes;
|
|
|
|
}
|
2017-05-18 18:48:23 +08:00
|
|
|
llvm_unreachable("invalid enum");
|
[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
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
GlobList Globs;
|
|
|
|
enum Tristate { None, Yes, No };
|
|
|
|
llvm::StringMap<Tristate> Cache;
|
|
|
|
};
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
ClangTidyContext::ClangTidyContext(
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
|
|
|
bool AllowEnablingAnalyzerAlphaCheckers)
|
2017-05-09 22:56:28 +08:00
|
|
|
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
Profile(false),
|
|
|
|
AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers) {
|
2017-05-09 22:56:28 +08:00
|
|
|
// Before the first translation unit we can get errors related to command-line
|
|
|
|
// parsing, use empty string for the file name in this case.
|
|
|
|
setCurrentFile("");
|
|
|
|
}
|
|
|
|
|
[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::~ClangTidyContext() = default;
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
DiagnosticBuilder ClangTidyContext::diag(
|
|
|
|
StringRef CheckName, SourceLocation Loc, StringRef Description,
|
|
|
|
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
|
|
|
|
assert(Loc.isValid());
|
|
|
|
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
|
|
|
|
Level, (Description + " [" + CheckName + "]").str());
|
|
|
|
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
|
|
|
|
return DiagEngine->Report(Loc, ID);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
|
|
|
|
DiagEngine->setSourceManager(SourceMgr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangTidyContext::setCurrentFile(StringRef File) {
|
|
|
|
CurrentFile = File;
|
|
|
|
CurrentOptions = getOptionsForFile(CurrentFile);
|
[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
|
|
|
CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
|
|
|
|
WarningAsErrorFilter =
|
|
|
|
llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClangTidyContext::setASTContext(ASTContext *Context) {
|
|
|
|
DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
|
|
|
|
LangOpts = Context->getLangOpts();
|
|
|
|
}
|
|
|
|
|
|
|
|
const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
|
|
|
|
return OptionsProvider->getGlobalOptions();
|
|
|
|
}
|
|
|
|
|
|
|
|
const ClangTidyOptions &ClangTidyContext::getOptions() const {
|
|
|
|
return CurrentOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
|
|
|
|
// Merge options on top of getDefaults() as a safeguard against options with
|
|
|
|
// unset values.
|
|
|
|
return ClangTidyOptions::getDefaults().mergeWith(
|
|
|
|
OptionsProvider->getOptions(File));
|
|
|
|
}
|
|
|
|
|
[clang-tidy] Profile is a per-AST (per-TU) data.
Summary:
As discussed in D45931, currently, profiling output of clang-tidy is somewhat not great.
It outputs one profile at the end of the execution, and that profile contains the data
from the last TU that was processed. So if the tool run on multiple TU's, the data is
not accumulated, it is simply discarded.
It would be nice to improve this.
This differential is the first step - make this profiling info per-TU,
and output it after the tool has finished processing each TU.
In particular, when `ClangTidyASTConsumer` destructor runs.
Next step will be to add a CSV (JSON?) printer to store said profiles under user-specified directory prefix.
Reviewers: alexfh, sbenza
Reviewed By: alexfh
Subscribers: Eugene.Zelenko, mgorny, xazax.hun, mgrang, klimek, cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D46504
llvm-svn: 331763
2018-05-08 21:14:21 +08:00
|
|
|
void ClangTidyContext::setEnableProfiling(bool P) { Profile = P; }
|
2017-05-09 22:56:28 +08:00
|
|
|
|
[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
|
|
|
void ClangTidyContext::setProfileStoragePrefix(StringRef Prefix) {
|
|
|
|
ProfilePrefix = Prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Optional<ClangTidyProfiling::StorageParams>
|
|
|
|
ClangTidyContext::getProfileStorageParams() const {
|
|
|
|
if (ProfilePrefix.empty())
|
|
|
|
return llvm::None;
|
|
|
|
|
|
|
|
return ClangTidyProfiling::StorageParams(ProfilePrefix, CurrentFile);
|
|
|
|
}
|
|
|
|
|
2017-05-17 22:39:47 +08:00
|
|
|
bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
|
2017-05-09 22:56:28 +08:00
|
|
|
assert(CheckFilter != nullptr);
|
2017-05-17 22:39:47 +08:00
|
|
|
return CheckFilter->contains(CheckName);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
2017-05-17 22:39:47 +08:00
|
|
|
bool ClangTidyContext::treatAsError(StringRef CheckName) const {
|
2017-05-09 22:56:28 +08:00
|
|
|
assert(WarningAsErrorFilter != nullptr);
|
2017-05-17 22:39:47 +08:00
|
|
|
return WarningAsErrorFilter->contains(CheckName);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
2019-03-13 00:11:46 +08:00
|
|
|
std::string ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
|
|
|
|
std::string ClangWarningOption =
|
|
|
|
DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID);
|
|
|
|
if (!ClangWarningOption.empty())
|
|
|
|
return "clang-diagnostic-" + ClangWarningOption;
|
2017-05-09 22:56:28 +08:00
|
|
|
llvm::DenseMap<unsigned, std::string>::const_iterator I =
|
|
|
|
CheckNamesByDiagnosticID.find(DiagnosticID);
|
|
|
|
if (I != CheckNamesByDiagnosticID.end())
|
|
|
|
return I->second;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2017-05-09 23:10:26 +08:00
|
|
|
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
|
|
|
|
ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
|
|
|
|
: Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
|
|
|
|
LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
|
2018-11-09 01:42:16 +08:00
|
|
|
LastErrorWasIgnored(false) {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
|
|
|
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
|
|
|
if (!Errors.empty()) {
|
|
|
|
ClangTidyError &Error = Errors.back();
|
2017-05-17 22:39:47 +08:00
|
|
|
if (!Context.isCheckEnabled(Error.DiagnosticName) &&
|
2017-05-09 22:56:28 +08:00
|
|
|
Error.DiagLevel != ClangTidyError::Error) {
|
|
|
|
++Context.Stats.ErrorsIgnoredCheckFilter;
|
|
|
|
Errors.pop_back();
|
|
|
|
} else if (!LastErrorRelatesToUserCode) {
|
|
|
|
++Context.Stats.ErrorsIgnoredNonUserCode;
|
|
|
|
Errors.pop_back();
|
|
|
|
} else if (!LastErrorPassesLineFilter) {
|
|
|
|
++Context.Stats.ErrorsIgnoredLineFilter;
|
|
|
|
Errors.pop_back();
|
|
|
|
} else {
|
|
|
|
++Context.Stats.ErrorsDisplayed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LastErrorRelatesToUserCode = false;
|
|
|
|
LastErrorPassesLineFilter = false;
|
|
|
|
}
|
|
|
|
|
2017-12-15 00:13:57 +08:00
|
|
|
static bool IsNOLINTFound(StringRef NolintDirectiveText, StringRef Line,
|
|
|
|
unsigned DiagID, const ClangTidyContext &Context) {
|
|
|
|
const size_t NolintIndex = Line.find(NolintDirectiveText);
|
|
|
|
if (NolintIndex == StringRef::npos)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
size_t BracketIndex = NolintIndex + NolintDirectiveText.size();
|
|
|
|
// Check if the specific checks are specified in brackets.
|
|
|
|
if (BracketIndex < Line.size() && Line[BracketIndex] == '(') {
|
|
|
|
++BracketIndex;
|
|
|
|
const size_t BracketEndIndex = Line.find(')', BracketIndex);
|
|
|
|
if (BracketEndIndex != StringRef::npos) {
|
|
|
|
StringRef ChecksStr =
|
|
|
|
Line.substr(BracketIndex, BracketEndIndex - BracketIndex);
|
|
|
|
// Allow disabling all the checks with "*".
|
|
|
|
if (ChecksStr != "*") {
|
2019-03-13 00:11:46 +08:00
|
|
|
std::string CheckName = Context.getCheckName(DiagID);
|
2017-12-15 00:13:57 +08:00
|
|
|
// Allow specifying a few check names, delimited with comma.
|
|
|
|
SmallVector<StringRef, 1> Checks;
|
|
|
|
ChecksStr.split(Checks, ',', -1, false);
|
|
|
|
llvm::transform(Checks, Checks.begin(),
|
|
|
|
[](StringRef S) { return S.trim(); });
|
|
|
|
return llvm::find(Checks, CheckName) != Checks.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-12-19 21:25:08 +08:00
|
|
|
static bool LineIsMarkedWithNOLINT(const SourceManager &SM, SourceLocation Loc,
|
2017-12-15 00:13:57 +08:00
|
|
|
unsigned DiagID,
|
|
|
|
const ClangTidyContext &Context) {
|
2017-05-09 22:56:28 +08:00
|
|
|
bool Invalid;
|
|
|
|
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
|
|
|
|
if (Invalid)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check if there's a NOLINT on this line.
|
|
|
|
const char *P = CharacterData;
|
|
|
|
while (*P != '\0' && *P != '\r' && *P != '\n')
|
|
|
|
++P;
|
|
|
|
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
|
2017-12-15 00:13:57 +08:00
|
|
|
if (IsNOLINTFound("NOLINT", RestOfLine, DiagID, Context))
|
2017-05-09 22:56:28 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// Check if there's a NOLINTNEXTLINE on the previous line.
|
|
|
|
const char *BufBegin =
|
|
|
|
SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
|
|
|
|
if (Invalid || P == BufBegin)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Scan backwards over the current line.
|
|
|
|
P = CharacterData;
|
|
|
|
while (P != BufBegin && *P != '\n')
|
|
|
|
--P;
|
|
|
|
|
|
|
|
// If we reached the begin of the file there is no line before it.
|
|
|
|
if (P == BufBegin)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Skip over the newline.
|
|
|
|
--P;
|
|
|
|
const char *LineEnd = P;
|
|
|
|
|
|
|
|
// Now we're on the previous line. Skip to the beginning of it.
|
|
|
|
while (P != BufBegin && *P != '\n')
|
|
|
|
--P;
|
|
|
|
|
|
|
|
RestOfLine = StringRef(P, LineEnd - P + 1);
|
2017-12-15 00:13:57 +08:00
|
|
|
if (IsNOLINTFound("NOLINTNEXTLINE", RestOfLine, DiagID, Context))
|
2017-05-09 22:56:28 +08:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-19 21:25:08 +08:00
|
|
|
static bool LineIsMarkedWithNOLINTinMacro(const SourceManager &SM,
|
|
|
|
SourceLocation Loc, unsigned DiagID,
|
2017-12-15 00:13:57 +08:00
|
|
|
const ClangTidyContext &Context) {
|
2017-05-09 22:56:28 +08:00
|
|
|
while (true) {
|
2017-12-15 00:13:57 +08:00
|
|
|
if (LineIsMarkedWithNOLINT(SM, Loc, DiagID, Context))
|
2017-05-09 22:56:28 +08:00
|
|
|
return true;
|
|
|
|
if (!Loc.isMacroID())
|
|
|
|
return false;
|
2018-04-30 13:26:07 +08:00
|
|
|
Loc = SM.getImmediateExpansionRange(Loc).getBegin();
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
|
|
|
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
|
|
|
|
if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
|
|
|
|
DiagLevel != DiagnosticsEngine::Fatal &&
|
2018-11-09 01:42:16 +08:00
|
|
|
LineIsMarkedWithNOLINTinMacro(Info.getSourceManager(),
|
2017-12-15 00:13:57 +08:00
|
|
|
Info.getLocation(), Info.getID(),
|
|
|
|
Context)) {
|
2017-05-09 22:56:28 +08:00
|
|
|
++Context.Stats.ErrorsIgnoredNOLINT;
|
|
|
|
// Ignored a warning, should ignore related notes as well
|
|
|
|
LastErrorWasIgnored = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LastErrorWasIgnored = false;
|
|
|
|
// Count warnings/errors.
|
|
|
|
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
|
|
|
|
|
|
|
|
if (DiagLevel == DiagnosticsEngine::Note) {
|
|
|
|
assert(!Errors.empty() &&
|
|
|
|
"A diagnostic note can only be appended to a message.");
|
|
|
|
} else {
|
|
|
|
finalizeLastError();
|
2019-03-13 00:11:46 +08:00
|
|
|
std::string CheckName = Context.getCheckName(Info.getID());
|
2017-05-09 22:56:28 +08:00
|
|
|
if (CheckName.empty()) {
|
|
|
|
// This is a compiler diagnostic without a warning option. Assign check
|
|
|
|
// name based on its level.
|
|
|
|
switch (DiagLevel) {
|
|
|
|
case DiagnosticsEngine::Error:
|
|
|
|
case DiagnosticsEngine::Fatal:
|
|
|
|
CheckName = "clang-diagnostic-error";
|
|
|
|
break;
|
|
|
|
case DiagnosticsEngine::Warning:
|
|
|
|
CheckName = "clang-diagnostic-warning";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CheckName = "clang-diagnostic-unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClangTidyError::Level Level = ClangTidyError::Warning;
|
|
|
|
if (DiagLevel == DiagnosticsEngine::Error ||
|
|
|
|
DiagLevel == DiagnosticsEngine::Fatal) {
|
|
|
|
// Force reporting of Clang errors regardless of filters and non-user
|
|
|
|
// code.
|
|
|
|
Level = ClangTidyError::Error;
|
|
|
|
LastErrorRelatesToUserCode = true;
|
|
|
|
LastErrorPassesLineFilter = true;
|
|
|
|
}
|
2017-05-17 22:39:47 +08:00
|
|
|
bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
|
|
|
|
Context.treatAsError(CheckName);
|
2017-05-09 22:56:28 +08:00
|
|
|
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
|
|
|
|
IsWarningAsError);
|
|
|
|
}
|
|
|
|
|
|
|
|
ClangTidyDiagnosticRenderer Converter(
|
|
|
|
Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
|
|
|
|
Errors.back());
|
|
|
|
SmallString<100> Message;
|
|
|
|
Info.FormatDiagnostic(Message);
|
2018-11-09 01:42:16 +08:00
|
|
|
FullSourceLoc Loc;
|
|
|
|
if (Info.getLocation().isValid() && Info.hasSourceManager())
|
|
|
|
Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
|
2017-06-27 18:04:04 +08:00
|
|
|
Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
|
|
|
|
Info.getFixItHints());
|
2017-05-09 22:56:28 +08:00
|
|
|
|
2018-11-09 01:42:16 +08:00
|
|
|
if (Info.hasSourceManager())
|
|
|
|
checkFilters(Info.getLocation(), Info.getSourceManager());
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
|
|
|
|
unsigned LineNumber) const {
|
|
|
|
if (Context.getGlobalOptions().LineFilter.empty())
|
|
|
|
return true;
|
|
|
|
for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
|
|
|
|
if (FileName.endswith(Filter.Name)) {
|
|
|
|
if (Filter.LineRanges.empty())
|
|
|
|
return true;
|
|
|
|
for (const FileFilter::LineRange &Range : Filter.LineRanges) {
|
|
|
|
if (Range.first <= LineNumber && LineNumber <= Range.second)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-09 01:42:16 +08:00
|
|
|
void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location,
|
|
|
|
const SourceManager &Sources) {
|
2017-05-09 22:56:28 +08:00
|
|
|
// Invalid location may mean a diagnostic in a command line, don't skip these.
|
|
|
|
if (!Location.isValid()) {
|
|
|
|
LastErrorRelatesToUserCode = true;
|
|
|
|
LastErrorPassesLineFilter = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*Context.getOptions().SystemHeaders &&
|
|
|
|
Sources.isInSystemHeader(Location))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// FIXME: We start with a conservative approach here, but the actual type of
|
|
|
|
// location needed depends on the check (in particular, where this check wants
|
|
|
|
// to apply fixes).
|
|
|
|
FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
|
|
|
|
const FileEntry *File = Sources.getFileEntryForID(FID);
|
|
|
|
|
|
|
|
// -DMACRO definitions on the command line have locations in a virtual buffer
|
|
|
|
// that doesn't have a FileEntry. Don't skip these as well.
|
|
|
|
if (!File) {
|
|
|
|
LastErrorRelatesToUserCode = true;
|
|
|
|
LastErrorPassesLineFilter = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef FileName(File->getName());
|
|
|
|
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
|
|
|
|
Sources.isInMainFile(Location) ||
|
|
|
|
getHeaderFilter()->match(FileName);
|
|
|
|
|
|
|
|
unsigned LineNumber = Sources.getExpansionLineNumber(Location);
|
|
|
|
LastErrorPassesLineFilter =
|
|
|
|
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
|
|
|
|
if (!HeaderFilter)
|
[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
|
|
|
HeaderFilter =
|
|
|
|
llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
|
2017-05-09 22:56:28 +08:00
|
|
|
return HeaderFilter.get();
|
|
|
|
}
|
|
|
|
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
|
2017-05-09 22:56:28 +08:00
|
|
|
// Each error is modelled as the set of intervals in which it applies
|
|
|
|
// replacements. To detect overlapping replacements, we use a sweep line
|
|
|
|
// algorithm over these sets of intervals.
|
|
|
|
// An event here consists of the opening or closing of an interval. During the
|
|
|
|
// process, we maintain a counter with the amount of open intervals. If we
|
|
|
|
// find an endpoint of an interval and this counter is different from 0, it
|
|
|
|
// means that this interval overlaps with another one, so we set it as
|
|
|
|
// inapplicable.
|
|
|
|
struct Event {
|
|
|
|
// An event can be either the begin or the end of an interval.
|
|
|
|
enum EventType {
|
|
|
|
ET_Begin = 1,
|
|
|
|
ET_End = -1,
|
|
|
|
};
|
|
|
|
|
|
|
|
Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
|
|
|
|
unsigned ErrorSize)
|
|
|
|
: Type(Type), ErrorId(ErrorId) {
|
|
|
|
// The events are going to be sorted by their position. In case of draw:
|
|
|
|
//
|
|
|
|
// * If an interval ends at the same position at which other interval
|
|
|
|
// begins, this is not an overlapping, so we want to remove the ending
|
|
|
|
// interval before adding the starting one: end events have higher
|
|
|
|
// priority than begin events.
|
|
|
|
//
|
|
|
|
// * If we have several begin points at the same position, we will mark as
|
|
|
|
// inapplicable the ones that we process later, so the first one has to
|
|
|
|
// be the one with the latest end point, because this one will contain
|
|
|
|
// all the other intervals. For the same reason, if we have several end
|
|
|
|
// points in the same position, the last one has to be the one with the
|
|
|
|
// earliest begin point. In both cases, we sort non-increasingly by the
|
|
|
|
// position of the complementary.
|
|
|
|
//
|
|
|
|
// * In case of two equal intervals, the one whose error is bigger can
|
|
|
|
// potentially contain the other one, so we want to process its begin
|
|
|
|
// points before and its end points later.
|
|
|
|
//
|
|
|
|
// * Finally, if we have two equal intervals whose errors have the same
|
|
|
|
// size, none of them will be strictly contained inside the other.
|
|
|
|
// Sorting by ErrorId will guarantee that the begin point of the first
|
|
|
|
// one will be processed before, disallowing the second one, and the
|
|
|
|
// end point of the first one will also be processed before,
|
|
|
|
// disallowing the first one.
|
|
|
|
if (Type == ET_Begin)
|
|
|
|
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
|
|
|
|
else
|
|
|
|
Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator<(const Event &Other) const {
|
|
|
|
return Priority < Other.Priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determines if this event is the begin or the end of an interval.
|
|
|
|
EventType Type;
|
|
|
|
// The index of the error to which the interval that generated this event
|
|
|
|
// belongs.
|
|
|
|
unsigned ErrorId;
|
|
|
|
// The events will be sorted based on this field.
|
|
|
|
std::tuple<unsigned, EventType, int, int, unsigned> Priority;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Compute error sizes.
|
|
|
|
std::vector<int> Sizes;
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
std::vector<
|
|
|
|
std::pair<ClangTidyError *, llvm::StringMap<tooling::Replacements> *>>
|
|
|
|
ErrorFixes;
|
|
|
|
for (auto &Error : Errors) {
|
|
|
|
if (const auto *Fix = tooling::selectFirstFix(Error))
|
|
|
|
ErrorFixes.emplace_back(
|
|
|
|
&Error, const_cast<llvm::StringMap<tooling::Replacements> *>(Fix));
|
|
|
|
}
|
|
|
|
for (const auto &ErrorAndFix : ErrorFixes) {
|
2017-05-09 22:56:28 +08:00
|
|
|
int Size = 0;
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
for (const auto &FileAndReplaces : *ErrorAndFix.second) {
|
2017-05-09 22:56:28 +08:00
|
|
|
for (const auto &Replace : FileAndReplaces.second)
|
|
|
|
Size += Replace.getLength();
|
|
|
|
}
|
|
|
|
Sizes.push_back(Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build events from error intervals.
|
|
|
|
std::map<std::string, std::vector<Event>> FileEvents;
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
for (unsigned I = 0; I < ErrorFixes.size(); ++I) {
|
|
|
|
for (const auto &FileAndReplace : *ErrorFixes[I].second) {
|
2017-05-09 22:56:28 +08:00
|
|
|
for (const auto &Replace : FileAndReplace.second) {
|
|
|
|
unsigned Begin = Replace.getOffset();
|
|
|
|
unsigned End = Begin + Replace.getLength();
|
|
|
|
const std::string &FilePath = Replace.getFilePath();
|
|
|
|
// FIXME: Handle empty intervals, such as those from insertions.
|
|
|
|
if (Begin == End)
|
|
|
|
continue;
|
|
|
|
auto &Events = FileEvents[FilePath];
|
|
|
|
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
|
|
|
|
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
std::vector<bool> Apply(ErrorFixes.size(), true);
|
2017-05-09 22:56:28 +08:00
|
|
|
for (auto &FileAndEvents : FileEvents) {
|
|
|
|
std::vector<Event> &Events = FileAndEvents.second;
|
|
|
|
// Sweep.
|
|
|
|
std::sort(Events.begin(), Events.end());
|
|
|
|
int OpenIntervals = 0;
|
|
|
|
for (const auto &Event : Events) {
|
|
|
|
if (Event.Type == Event::ET_End)
|
|
|
|
--OpenIntervals;
|
|
|
|
// This has to be checked after removing the interval from the count if it
|
|
|
|
// is an end event, or before adding it if it is a begin event.
|
|
|
|
if (OpenIntervals != 0)
|
|
|
|
Apply[Event.ErrorId] = false;
|
|
|
|
if (Event.Type == Event::ET_Begin)
|
|
|
|
++OpenIntervals;
|
|
|
|
}
|
|
|
|
assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
|
|
|
|
}
|
|
|
|
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
for (unsigned I = 0; I < ErrorFixes.size(); ++I) {
|
2017-05-09 22:56:28 +08:00
|
|
|
if (!Apply[I]) {
|
[clang-tidy] Add fix descriptions to clang-tidy checks.
Summary:
Motivation/Context: in the code review system integrating with clang-tidy,
clang-tidy doesn't provide a human-readable description of the fix. Usually
developers have to preview a code diff (before vs after apply the fix) to
understand what the fix does before applying a fix.
This patch proposes that each clang-tidy check provides a short and
actional fix description that can be shown in the UI, so that users can know
what the fix does without previewing diff.
This patch extends clang-tidy framework to support fix descriptions (will add implementations for
existing checks in the future). Fix descriptions and fixes are emitted via diagnostic::Note (rather than
attaching the main warning diagnostic).
Before this patch:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning") << FixtItHint::CreateReplacement(...);
}
```
After:
```
void MyCheck::check(...) {
...
diag(loc, "my check warning"); // Emit a check warning
diag(loc, "fix description", DiagnosticIDs::Note) << FixtItHint::CreateReplacement(...); // Emit a diagnostic note and a fix
}
```
Reviewers: sammccall, alexfh
Reviewed By: alexfh
Subscribers: MyDeveloperDay, Eugene.Zelenko, aaron.ballman, JonasToth, xazax.hun, jdoerfert, cfe-commits
Tags: #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D59932
llvm-svn: 358576
2019-04-17 20:53:59 +08:00
|
|
|
ErrorFixes[I].second->clear();
|
|
|
|
ErrorFixes[I].first->Notes.emplace_back(
|
2017-05-09 22:56:28 +08:00
|
|
|
"this fix will not be applied because it overlaps with another fix");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct LessClangTidyError {
|
|
|
|
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
|
|
|
|
const tooling::DiagnosticMessage &M1 = LHS.Message;
|
|
|
|
const tooling::DiagnosticMessage &M2 = RHS.Message;
|
|
|
|
|
|
|
|
return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
|
|
|
|
std::tie(M2.FilePath, M2.FileOffset, M2.Message);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
struct EqualClangTidyError {
|
|
|
|
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
|
|
|
|
LessClangTidyError Less;
|
|
|
|
return !Less(LHS, RHS) && !Less(RHS, LHS);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
std::vector<ClangTidyError> ClangTidyDiagnosticConsumer::take() {
|
2017-05-09 22:56:28 +08:00
|
|
|
finalizeLastError();
|
|
|
|
|
|
|
|
std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
|
|
|
|
Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
|
|
|
|
Errors.end());
|
2017-05-09 23:10:26 +08:00
|
|
|
if (RemoveIncompatibleErrors)
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
removeIncompatibleErrors();
|
|
|
|
return std::move(Errors);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|