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"
|
2019-08-26 23:44:32 +08:00
|
|
|
#include "GlobList.h"
|
2022-01-26 20:02:35 +08:00
|
|
|
#include "NoLintDirectiveHandler.h"
|
2020-06-29 23:05:51 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2017-05-09 22:56:28 +08:00
|
|
|
#include "clang/AST/ASTDiagnostic.h"
|
2019-11-16 09:31:55 +08:00
|
|
|
#include "clang/AST/Attr.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"
|
2021-09-29 19:59:04 +08:00
|
|
|
#include "clang/Basic/FileManager.h"
|
2020-06-29 23:05:51 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2017-05-09 22:56:28 +08:00
|
|
|
#include "clang/Frontend/DiagnosticRenderer.h"
|
2021-03-16 00:06:03 +08:00
|
|
|
#include "clang/Lex/Lexer.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"
|
2020-06-29 23:05:51 +08:00
|
|
|
#include "clang/Tooling/Core/Replacement.h"
|
2022-01-19 00:58:57 +08:00
|
|
|
#include "llvm/ADT/BitVector.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"
|
2020-06-29 23:05:51 +08:00
|
|
|
#include "llvm/ADT/StringMap.h"
|
2020-06-20 03:40:03 +08:00
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
2020-06-29 23:05:51 +08:00
|
|
|
#include "llvm/Support/Regex.h"
|
2017-05-09 22:56:28 +08:00
|
|
|
#include <tuple>
|
2021-10-06 14:17:13 +08:00
|
|
|
#include <utility>
|
2017-05-09 22:56:28 +08:00
|
|
|
#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);
|
2021-03-16 00:06:03 +08:00
|
|
|
|
2021-11-02 15:14:25 +08:00
|
|
|
// Make sure that if a TokenRange is received from the check it is unfurled
|
2021-03-16 00:06:03 +08:00
|
|
|
// into a real CharRange for the diagnostic printer later.
|
|
|
|
// Whatever we store here gets decoupled from the current SourceManager, so
|
|
|
|
// we **have to** know the exact position and length of the highlight.
|
2021-04-11 00:48:22 +08:00
|
|
|
auto ToCharRange = [this, &Loc](const CharSourceRange &SourceRange) {
|
2021-03-16 00:06:03 +08:00
|
|
|
if (SourceRange.isCharRange())
|
|
|
|
return SourceRange;
|
2021-04-11 00:48:22 +08:00
|
|
|
assert(SourceRange.isTokenRange());
|
2021-03-16 00:06:03 +08:00
|
|
|
SourceLocation End = Lexer::getLocForEndOfToken(
|
2021-04-11 00:48:22 +08:00
|
|
|
SourceRange.getEnd(), 0, Loc.getManager(), LangOpts);
|
2021-03-16 00:06:03 +08:00
|
|
|
return CharSourceRange::getCharRange(SourceRange.getBegin(), End);
|
|
|
|
};
|
|
|
|
|
2021-11-29 16:56:43 +08:00
|
|
|
// We are only interested in valid ranges.
|
|
|
|
auto ValidRanges =
|
|
|
|
llvm::make_filter_range(Ranges, [](const CharSourceRange &R) {
|
|
|
|
return R.getAsRange().isValid();
|
|
|
|
});
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
if (Level == DiagnosticsEngine::Note) {
|
|
|
|
Error.Notes.push_back(TidyMessage);
|
2021-11-29 16:56:43 +08:00
|
|
|
for (const CharSourceRange &SourceRange : ValidRanges)
|
2021-03-16 00:06:03 +08:00
|
|
|
Error.Notes.back().Ranges.emplace_back(Loc.getManager(),
|
|
|
|
ToCharRange(SourceRange));
|
2017-05-09 22:56:28 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
|
|
|
|
Error.Message = TidyMessage;
|
2021-11-29 16:56:43 +08:00
|
|
|
for (const CharSourceRange &SourceRange : ValidRanges)
|
2021-03-16 00:06:03 +08:00
|
|
|
Error.Message.Ranges.emplace_back(Loc.getManager(),
|
|
|
|
ToCharRange(SourceRange));
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
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) {}
|
|
|
|
|
|
|
|
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),
|
[clang-tidy] Add a Standalone diagnostics mode to clang-tidy
Adds a flag to `ClangTidyContext` that is used to indicate to checks that fixes will only be applied one at a time.
This is to indicate to checks that each fix emitted should not depend on any other fixes emitted across the translation unit.
I've currently implemented the `IncludeInserter`, `LoopConvertCheck` and `PreferMemberInitializerCheck` to use these support these modes.
Reasoning behind this is in use cases like `clangd` it's only possible to apply one fix at a time.
For include inserter checks, the include is only added once for the first diagnostic that requires it, this will result in subsequent fixes not having the included needed.
A similar issue is seen in the `PreferMemberInitializerCheck` where the `:` will only be added for the first member that needs fixing.
Fixes emitted in `StandaloneDiagsMode` will likely result in malformed code if they are applied all together, conversely fixes currently emitted may result in malformed code if they are applied one at a time.
For this reason invoking `clang-tidy` from the binary will always with `StandaloneDiagsMode` disabled, However using it as a library its possible to select the mode you wish to use, `clangd` always selects `StandaloneDiagsMode`.
This is an example of the current behaviour failing
```lang=c++
struct Foo {
int A, B;
Foo(int D, int E) {
A = D;
B = E; // Fix Here
}
};
```
Incorrectly transformed to:
```lang=c++
struct Foo {
int A, B;
Foo(int D, int E), B(E) {
A = D;
// Fix Here
}
};
```
In `StandaloneDiagsMode`, it gets transformed to:
```lang=c++
struct Foo {
int A, B;
Foo(int D, int E) : B(E) {
A = D;
// Fix Here
}
};
```
Reviewed By: sammccall
Differential Revision: https://reviews.llvm.org/D97121
2022-04-16 16:53:32 +08:00
|
|
|
AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers),
|
|
|
|
SelfContainedDiags(false) {
|
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);
|
|
|
|
}
|
|
|
|
|
2020-12-09 04:28:42 +08:00
|
|
|
DiagnosticBuilder ClangTidyContext::diag(
|
|
|
|
StringRef CheckName, StringRef Description,
|
|
|
|
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
|
|
|
|
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
|
|
|
|
Level, (Description + " [" + CheckName + "]").str());
|
|
|
|
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
|
|
|
|
return DiagEngine->Report(ID);
|
|
|
|
}
|
|
|
|
|
2022-01-26 20:02:35 +08:00
|
|
|
DiagnosticBuilder ClangTidyContext::diag(const tooling::Diagnostic &Error) {
|
2021-09-29 19:59:04 +08:00
|
|
|
SourceManager &SM = DiagEngine->getSourceManager();
|
|
|
|
llvm::ErrorOr<const FileEntry *> File =
|
|
|
|
SM.getFileManager().getFile(Error.Message.FilePath);
|
|
|
|
FileID ID = SM.getOrCreateFileID(*File, SrcMgr::C_User);
|
|
|
|
SourceLocation FileStartLoc = SM.getLocForStartOfFile(ID);
|
2021-12-23 04:31:45 +08:00
|
|
|
SourceLocation Loc = FileStartLoc.getLocWithOffset(
|
|
|
|
static_cast<SourceLocation::IntTy>(Error.Message.FileOffset));
|
2021-09-29 19:59:04 +08:00
|
|
|
return diag(Error.DiagnosticName, Loc, Error.Message.Message,
|
|
|
|
static_cast<DiagnosticIDs::Level>(Error.DiagLevel));
|
|
|
|
}
|
|
|
|
|
2020-12-09 04:28:42 +08:00
|
|
|
DiagnosticBuilder ClangTidyContext::configurationDiag(
|
|
|
|
StringRef Message,
|
|
|
|
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
|
|
|
|
return diag("clang-tidy-config", Message, Level);
|
|
|
|
}
|
|
|
|
|
2022-01-26 20:02:35 +08:00
|
|
|
bool ClangTidyContext::shouldSuppressDiagnostic(
|
|
|
|
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info,
|
|
|
|
SmallVectorImpl<tooling::Diagnostic> &NoLintErrors, bool AllowIO,
|
|
|
|
bool EnableNoLintBlocks) {
|
|
|
|
std::string CheckName = getCheckName(Info.getID());
|
|
|
|
return NoLintHandler.shouldSuppress(DiagLevel, Info, CheckName, NoLintErrors,
|
|
|
|
AllowIO, EnableNoLintBlocks);
|
|
|
|
}
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
|
|
|
|
DiagEngine->setSourceManager(SourceMgr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangTidyContext::setCurrentFile(StringRef File) {
|
2020-01-29 03:23:46 +08:00
|
|
|
CurrentFile = std::string(File);
|
2017-05-09 22:56:28 +08:00
|
|
|
CurrentOptions = getOptionsForFile(CurrentFile);
|
2019-08-15 07:52:23 +08:00
|
|
|
CheckFilter = std::make_unique<CachedGlobList>(*getOptions().Checks);
|
[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
|
|
|
WarningAsErrorFilter =
|
2019-08-15 07:52:23 +08:00
|
|
|
std::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.
|
2020-11-13 02:19:11 +08:00
|
|
|
return ClangTidyOptions::getDefaults().merge(
|
2020-04-01 17:08:53 +08:00
|
|
|
OptionsProvider->getOptions(File), 0);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
[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) {
|
2020-01-29 03:23:46 +08:00
|
|
|
ProfilePrefix = std::string(Prefix);
|
[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
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-01-29 03:23:46 +08:00
|
|
|
std::string ClangWarningOption = std::string(
|
|
|
|
DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(DiagnosticID));
|
2019-03-13 00:11:46 +08:00
|
|
|
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(
|
2019-06-06 21:13:27 +08:00
|
|
|
ClangTidyContext &Ctx, DiagnosticsEngine *ExternalDiagEngine,
|
2021-12-03 03:52:47 +08:00
|
|
|
bool RemoveIncompatibleErrors, bool GetFixesFromNotes,
|
|
|
|
bool EnableNolintBlocks)
|
2019-06-06 21:13:27 +08:00
|
|
|
: Context(Ctx), ExternalDiagEngine(ExternalDiagEngine),
|
|
|
|
RemoveIncompatibleErrors(RemoveIncompatibleErrors),
|
2021-12-03 03:52:47 +08:00
|
|
|
GetFixesFromNotes(GetFixesFromNotes),
|
|
|
|
EnableNolintBlocks(EnableNolintBlocks), LastErrorRelatesToUserCode(false),
|
2021-03-02 06:07:09 +08:00
|
|
|
LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {}
|
2017-05-09 22:56:28 +08:00
|
|
|
|
|
|
|
void ClangTidyDiagnosticConsumer::finalizeLastError() {
|
|
|
|
if (!Errors.empty()) {
|
|
|
|
ClangTidyError &Error = Errors.back();
|
2020-12-09 04:28:42 +08:00
|
|
|
if (Error.DiagnosticName == "clang-tidy-config") {
|
|
|
|
// Never ignore these.
|
|
|
|
} else if (!Context.isCheckEnabled(Error.DiagnosticName) &&
|
|
|
|
Error.DiagLevel != ClangTidyError::Error) {
|
2017-05-09 22:56:28 +08:00
|
|
|
++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;
|
|
|
|
}
|
|
|
|
|
2019-05-19 12:06:52 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace tidy {
|
|
|
|
|
2021-03-02 06:07:09 +08:00
|
|
|
const llvm::StringMap<tooling::Replacements> *
|
|
|
|
getFixIt(const tooling::Diagnostic &Diagnostic, bool GetFixFromNotes) {
|
|
|
|
if (!Diagnostic.Message.Fix.empty())
|
|
|
|
return &Diagnostic.Message.Fix;
|
|
|
|
if (!GetFixFromNotes)
|
|
|
|
return nullptr;
|
|
|
|
const llvm::StringMap<tooling::Replacements> *Result = nullptr;
|
|
|
|
for (const auto &Note : Diagnostic.Notes) {
|
|
|
|
if (!Note.Fix.empty()) {
|
|
|
|
if (Result)
|
|
|
|
// We have 2 different fixes in notes, bail out.
|
|
|
|
return nullptr;
|
|
|
|
Result = &Note.Fix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2019-05-19 12:06:52 +08:00
|
|
|
} // namespace tidy
|
|
|
|
} // namespace clang
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
|
|
|
|
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
|
|
|
|
if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
|
|
|
|
return;
|
|
|
|
|
2022-01-26 20:02:35 +08:00
|
|
|
SmallVector<tooling::Diagnostic, 1> SuppressionErrors;
|
|
|
|
if (Context.shouldSuppressDiagnostic(DiagLevel, Info, SuppressionErrors,
|
|
|
|
EnableNolintBlocks)) {
|
2017-05-09 22:56:28 +08:00
|
|
|
++Context.Stats.ErrorsIgnoredNOLINT;
|
|
|
|
// Ignored a warning, should ignore related notes as well
|
|
|
|
LastErrorWasIgnored = true;
|
2022-01-26 20:02:35 +08:00
|
|
|
Context.DiagEngine->Clear();
|
|
|
|
for (const auto &Error : SuppressionErrors)
|
|
|
|
Context.diag(Error);
|
2017-05-09 22:56:28 +08:00
|
|
|
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;
|
2021-05-24 23:21:44 +08:00
|
|
|
case DiagnosticsEngine::Remark:
|
|
|
|
CheckName = "clang-diagnostic-remark";
|
|
|
|
break;
|
2017-05-09 22:56:28 +08:00
|
|
|
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;
|
2021-05-24 23:21:44 +08:00
|
|
|
} else if (DiagLevel == DiagnosticsEngine::Remark) {
|
|
|
|
Level = ClangTidyError::Remark;
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
2021-05-24 23:21:44 +08:00
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-06-06 21:13:27 +08:00
|
|
|
if (ExternalDiagEngine) {
|
|
|
|
// If there is an external diagnostics engine, like in the
|
|
|
|
// ClangTidyPluginAction case, forward the diagnostics to it.
|
|
|
|
forwardDiagnostic(Info);
|
|
|
|
} else {
|
|
|
|
ClangTidyDiagnosticRenderer Converter(
|
|
|
|
Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
|
|
|
|
Errors.back());
|
|
|
|
SmallString<100> Message;
|
|
|
|
Info.FormatDiagnostic(Message);
|
|
|
|
FullSourceLoc Loc;
|
|
|
|
if (Info.getLocation().isValid() && Info.hasSourceManager())
|
|
|
|
Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
|
|
|
|
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());
|
2021-09-29 19:59:04 +08:00
|
|
|
|
|
|
|
Context.DiagEngine->Clear();
|
|
|
|
for (const auto &Error : SuppressionErrors)
|
|
|
|
Context.diag(Error);
|
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;
|
|
|
|
}
|
|
|
|
|
2019-06-06 21:13:27 +08:00
|
|
|
void ClangTidyDiagnosticConsumer::forwardDiagnostic(const Diagnostic &Info) {
|
|
|
|
// Acquire a diagnostic ID also in the external diagnostics engine.
|
|
|
|
auto DiagLevelAndFormatString =
|
|
|
|
Context.getDiagLevelAndFormatString(Info.getID(), Info.getLocation());
|
|
|
|
unsigned ExternalID = ExternalDiagEngine->getDiagnosticIDs()->getCustomDiagID(
|
|
|
|
DiagLevelAndFormatString.first, DiagLevelAndFormatString.second);
|
|
|
|
|
|
|
|
// Forward the details.
|
2019-12-06 22:38:39 +08:00
|
|
|
auto Builder = ExternalDiagEngine->Report(Info.getLocation(), ExternalID);
|
2019-06-06 21:13:27 +08:00
|
|
|
for (auto Hint : Info.getFixItHints())
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Hint;
|
2019-06-06 21:13:27 +08:00
|
|
|
for (auto Range : Info.getRanges())
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Range;
|
2019-06-06 21:13:27 +08:00
|
|
|
for (unsigned Index = 0; Index < Info.getNumArgs(); ++Index) {
|
2019-12-06 22:38:39 +08:00
|
|
|
DiagnosticsEngine::ArgumentKind Kind = Info.getArgKind(Index);
|
|
|
|
switch (Kind) {
|
2019-06-06 21:13:27 +08:00
|
|
|
case clang::DiagnosticsEngine::ak_std_string:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Info.getArgStdStr(Index);
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_c_string:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Info.getArgCStr(Index);
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_sint:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Info.getArgSInt(Index);
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_uint:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Info.getArgUInt(Index);
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_tokenkind:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << static_cast<tok::TokenKind>(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_identifierinfo:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Info.getArgIdentifier(Index);
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_qual:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << Qualifiers::fromOpaqueValue(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_qualtype:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << QualType::getFromOpaquePtr((void *)Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_declarationname:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << DeclarationName::getFromOpaqueInteger(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_nameddecl:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << reinterpret_cast<const NamedDecl *>(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_nestednamespec:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << reinterpret_cast<NestedNameSpecifier *>(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_declcontext:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << reinterpret_cast<DeclContext *>(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_qualtype_pair:
|
|
|
|
assert(false); // This one is not passed around.
|
|
|
|
break;
|
|
|
|
case clang::DiagnosticsEngine::ak_attr:
|
2019-12-06 22:38:39 +08:00
|
|
|
Builder << reinterpret_cast<Attr *>(Info.getRawArg(Index));
|
2019-06-06 21:13:27 +08:00
|
|
|
break;
|
2019-12-14 04:54:13 +08:00
|
|
|
case clang::DiagnosticsEngine::ak_addrspace:
|
|
|
|
Builder << static_cast<LangAS>(Info.getRawArg(Index));
|
|
|
|
break;
|
2019-06-06 21:13:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 &&
|
2021-12-29 22:42:30 +08:00
|
|
|
(Sources.isInSystemHeader(Location) || Sources.isInSystemMacro(Location)))
|
2017-05-09 22:56:28 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-09-23 21:06:25 +08:00
|
|
|
StringRef FileName(File->getName());
|
2017-05-09 22:56:28 +08:00
|
|
|
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 =
|
2019-08-15 07:52:23 +08:00
|
|
|
std::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,
|
2020-07-29 23:34:06 +08:00
|
|
|
ET_Insert = 0,
|
2017-05-09 22:56:28 +08:00
|
|
|
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.
|
2020-07-29 23:34:06 +08:00
|
|
|
switch (Type) {
|
|
|
|
case ET_Begin:
|
2017-05-09 22:56:28 +08:00
|
|
|
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
|
2020-07-29 23:34:06 +08:00
|
|
|
break;
|
|
|
|
case ET_Insert:
|
|
|
|
Priority = std::make_tuple(Begin, Type, -End, ErrorSize, ErrorId);
|
|
|
|
break;
|
|
|
|
case ET_End:
|
2017-05-09 22:56:28 +08:00
|
|
|
Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
|
2020-07-29 23:34:06 +08:00
|
|
|
break;
|
|
|
|
}
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2020-06-20 03:40:03 +08:00
|
|
|
removeDuplicatedDiagnosticsOfAliasCheckers();
|
|
|
|
|
2017-05-09 22:56:28 +08:00
|
|
|
// 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) {
|
2021-03-02 06:07:09 +08:00
|
|
|
if (const auto *Fix = getFixIt(Error, GetFixesFromNotes))
|
[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.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.
|
2020-07-29 23:34:06 +08:00
|
|
|
llvm::StringMap<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();
|
2020-07-29 23:34:06 +08:00
|
|
|
auto &Events = FileEvents[Replace.getFilePath()];
|
|
|
|
if (Begin == End) {
|
|
|
|
Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
|
|
|
|
} else {
|
|
|
|
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
|
|
|
|
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
|
|
|
|
}
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-19 00:58:57 +08:00
|
|
|
llvm::BitVector Apply(ErrorFixes.size(), true);
|
2017-05-09 22:56:28 +08:00
|
|
|
for (auto &FileAndEvents : FileEvents) {
|
|
|
|
std::vector<Event> &Events = FileAndEvents.second;
|
|
|
|
// Sweep.
|
2020-03-29 02:19:55 +08:00
|
|
|
llvm::sort(Events);
|
2017-05-09 22:56:28 +08:00
|
|
|
int OpenIntervals = 0;
|
|
|
|
for (const auto &Event : Events) {
|
2020-07-29 23:34:06 +08:00
|
|
|
switch (Event.Type) {
|
|
|
|
case Event::ET_Begin:
|
|
|
|
if (OpenIntervals++ != 0)
|
|
|
|
Apply[Event.ErrorId] = false;
|
|
|
|
break;
|
|
|
|
case Event::ET_Insert:
|
|
|
|
if (OpenIntervals != 0)
|
|
|
|
Apply[Event.ErrorId] = false;
|
|
|
|
break;
|
|
|
|
case Event::ET_End:
|
|
|
|
if (--OpenIntervals != 0)
|
|
|
|
Apply[Event.ErrorId] = false;
|
|
|
|
break;
|
|
|
|
}
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2020-03-30 03:12:58 +08:00
|
|
|
return std::tie(M1.FilePath, M1.FileOffset, LHS.DiagnosticName,
|
|
|
|
M1.Message) <
|
|
|
|
std::tie(M2.FilePath, M2.FileOffset, RHS.DiagnosticName, M2.Message);
|
2017-05-09 22:56:28 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
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();
|
|
|
|
|
2020-08-12 23:51:52 +08:00
|
|
|
llvm::stable_sort(Errors, LessClangTidyError());
|
2017-05-09 22:56:28 +08:00
|
|
|
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
|
|
|
}
|
2020-06-20 03:40:03 +08:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct LessClangTidyErrorWithoutDiagnosticName {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
void ClangTidyDiagnosticConsumer::removeDuplicatedDiagnosticsOfAliasCheckers() {
|
|
|
|
using UniqueErrorSet =
|
|
|
|
std::set<ClangTidyError *, LessClangTidyErrorWithoutDiagnosticName>;
|
|
|
|
UniqueErrorSet UniqueErrors;
|
|
|
|
|
|
|
|
auto IT = Errors.begin();
|
|
|
|
while (IT != Errors.end()) {
|
|
|
|
ClangTidyError &Error = *IT;
|
|
|
|
std::pair<UniqueErrorSet::iterator, bool> Inserted =
|
|
|
|
UniqueErrors.insert(&Error);
|
|
|
|
|
|
|
|
// Unique error, we keep it and move along.
|
|
|
|
if (Inserted.second) {
|
|
|
|
++IT;
|
|
|
|
} else {
|
|
|
|
ClangTidyError &ExistingError = **Inserted.first;
|
|
|
|
const llvm::StringMap<tooling::Replacements> &CandidateFix =
|
|
|
|
Error.Message.Fix;
|
|
|
|
const llvm::StringMap<tooling::Replacements> &ExistingFix =
|
|
|
|
(*Inserted.first)->Message.Fix;
|
|
|
|
|
|
|
|
if (CandidateFix != ExistingFix) {
|
|
|
|
|
|
|
|
// In case of a conflict, don't suggest any fix-it.
|
|
|
|
ExistingError.Message.Fix.clear();
|
|
|
|
ExistingError.Notes.emplace_back(
|
|
|
|
llvm::formatv("cannot apply fix-it because an alias checker has "
|
|
|
|
"suggested a different fix-it; please remove one of "
|
|
|
|
"the checkers ('{0}', '{1}') or "
|
|
|
|
"ensure they are both configured the same",
|
|
|
|
ExistingError.DiagnosticName, Error.DiagnosticName)
|
|
|
|
.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Error.IsWarningAsError)
|
|
|
|
ExistingError.IsWarningAsError = true;
|
|
|
|
|
|
|
|
// Since it is the same error, we should take it as alias and remove it.
|
|
|
|
ExistingError.EnabledDiagnosticAliases.emplace_back(Error.DiagnosticName);
|
|
|
|
IT = Errors.erase(IT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|