2013-07-29 16:19:24 +08:00
|
|
|
//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===//
|
|
|
|
//
|
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
|
2013-07-29 16:19:24 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-03-10 00:52:33 +08:00
|
|
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
|
|
|
|
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
|
2013-07-29 16:19:24 +08:00
|
|
|
|
|
|
|
#include "ClangTidy.h"
|
|
|
|
#include "ClangTidyDiagnosticConsumer.h"
|
|
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
|
|
#include "clang/Frontend/FrontendActions.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"
|
|
|
|
#include "clang/Tooling/Core/Replacement.h"
|
2013-07-29 16:19:24 +08:00
|
|
|
#include "clang/Tooling/Refactoring.h"
|
|
|
|
#include "clang/Tooling/Tooling.h"
|
2016-07-11 21:53:21 +08:00
|
|
|
#include "llvm/ADT/Optional.h"
|
2017-10-26 16:37:25 +08:00
|
|
|
#include "llvm/Support/Path.h"
|
2015-08-11 19:37:48 +08:00
|
|
|
#include <map>
|
2015-10-06 21:52:51 +08:00
|
|
|
#include <memory>
|
2013-07-29 16:19:24 +08:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace tidy {
|
2014-02-27 22:28:02 +08:00
|
|
|
namespace test {
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2019-06-26 23:04:33 +08:00
|
|
|
template <typename Check, typename... Checks> struct CheckFactory {
|
|
|
|
static void
|
|
|
|
createChecks(ClangTidyContext *Context,
|
|
|
|
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
|
|
|
|
CheckFactory<Check>::createChecks(Context, Result);
|
|
|
|
CheckFactory<Checks...>::createChecks(Context, Result);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Check> struct CheckFactory<Check> {
|
|
|
|
static void
|
|
|
|
createChecks(ClangTidyContext *Context,
|
|
|
|
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
|
2019-08-15 07:52:23 +08:00
|
|
|
Result.emplace_back(std::make_unique<Check>(
|
2019-06-26 23:04:33 +08:00
|
|
|
"test-check-" + std::to_string(Result.size()), Context));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename... CheckTypes>
|
2014-10-07 23:49:36 +08:00
|
|
|
class TestClangTidyAction : public ASTFrontendAction {
|
2014-02-27 22:28:02 +08:00
|
|
|
public:
|
2015-10-06 21:52:51 +08:00
|
|
|
TestClangTidyAction(SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks,
|
|
|
|
ast_matchers::MatchFinder &Finder,
|
2014-10-07 23:49:36 +08:00
|
|
|
ClangTidyContext &Context)
|
2015-10-06 21:52:51 +08:00
|
|
|
: Checks(Checks), Finder(Finder), Context(Context) {}
|
2013-07-29 16:19:24 +08:00
|
|
|
|
|
|
|
private:
|
2014-10-07 23:49:36 +08:00
|
|
|
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
|
|
|
StringRef File) override {
|
|
|
|
Context.setSourceManager(&Compiler.getSourceManager());
|
2015-09-03 00:04:15 +08:00
|
|
|
Context.setCurrentFile(File);
|
|
|
|
Context.setASTContext(&Compiler.getASTContext());
|
|
|
|
|
2019-03-23 02:58:12 +08:00
|
|
|
Preprocessor *PP = &Compiler.getPreprocessor();
|
2019-06-26 23:04:33 +08:00
|
|
|
|
|
|
|
// Checks must be created here, _after_ `Context` has been initialized, so
|
|
|
|
// that check constructors can access the context (for example, through
|
|
|
|
// `getLangOpts()`).
|
|
|
|
CheckFactory<CheckTypes...>::createChecks(&Context, Checks);
|
2015-10-06 21:52:51 +08:00
|
|
|
for (auto &Check : Checks) {
|
|
|
|
Check->registerMatchers(&Finder);
|
2019-03-23 02:58:12 +08:00
|
|
|
Check->registerPPCallbacks(Compiler.getSourceManager(), PP, PP);
|
2015-10-06 21:52:51 +08:00
|
|
|
}
|
2014-10-07 23:49:36 +08:00
|
|
|
return Finder.newASTConsumer();
|
2014-02-27 22:28:02 +08:00
|
|
|
}
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2015-10-06 21:52:51 +08:00
|
|
|
SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks;
|
2014-10-07 23:49:36 +08:00
|
|
|
ast_matchers::MatchFinder &Finder;
|
|
|
|
ClangTidyContext &Context;
|
2014-02-27 22:28:02 +08:00
|
|
|
};
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2019-06-26 23:04:33 +08:00
|
|
|
template <typename... CheckTypes>
|
2015-03-31 21:53:03 +08:00
|
|
|
std::string
|
|
|
|
runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
|
|
|
|
const Twine &Filename = "input.cc",
|
|
|
|
ArrayRef<std::string> ExtraArgs = None,
|
2015-08-11 19:37:48 +08:00
|
|
|
const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),
|
2015-08-11 20:13:15 +08:00
|
|
|
std::map<StringRef, StringRef> PathsToContent =
|
|
|
|
std::map<StringRef, StringRef>()) {
|
2015-03-31 21:53:03 +08:00
|
|
|
ClangTidyOptions Options = ExtraOptions;
|
2014-09-04 22:23:36 +08:00
|
|
|
Options.Checks = "*";
|
2019-08-15 07:52:23 +08:00
|
|
|
ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
|
2014-09-04 22:23:36 +08:00
|
|
|
ClangTidyGlobalOptions(), Options));
|
2014-02-27 22:28:02 +08:00
|
|
|
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
2018-11-09 01:42:16 +08:00
|
|
|
DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions,
|
|
|
|
&DiagConsumer, false);
|
|
|
|
Context.setDiagnosticsEngine(&DE);
|
2014-10-07 23:49:36 +08:00
|
|
|
|
2017-10-26 16:37:25 +08:00
|
|
|
std::vector<std::string> Args(1, "clang-tidy");
|
|
|
|
Args.push_back("-fsyntax-only");
|
2020-01-04 05:02:11 +08:00
|
|
|
Args.push_back("-fno-delayed-template-parsing");
|
2017-10-26 16:37:25 +08:00
|
|
|
std::string extension(llvm::sys::path::extension(Filename.str()));
|
|
|
|
if (extension == ".m" || extension == ".mm") {
|
|
|
|
Args.push_back("-fobjc-abi-version=2");
|
|
|
|
Args.push_back("-fobjc-arc");
|
|
|
|
}
|
|
|
|
if (extension == ".cc" || extension == ".cpp" || extension == ".mm") {
|
|
|
|
Args.push_back("-std=c++11");
|
|
|
|
}
|
|
|
|
Args.push_back("-Iinclude");
|
|
|
|
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
|
|
|
|
Args.push_back(Filename.str());
|
2015-09-03 00:04:15 +08:00
|
|
|
|
|
|
|
ast_matchers::MatchFinder Finder;
|
2018-10-10 21:27:25 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
|
|
|
new llvm::vfs::InMemoryFileSystem);
|
2014-10-09 21:22:46 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<FileManager> Files(
|
2015-10-07 16:35:23 +08:00
|
|
|
new FileManager(FileSystemOptions(), InMemoryFileSystem));
|
2015-10-06 21:52:51 +08:00
|
|
|
|
|
|
|
SmallVector<std::unique_ptr<ClangTidyCheck>, 1> Checks;
|
2014-10-07 23:49:36 +08:00
|
|
|
tooling::ToolInvocation Invocation(
|
2019-08-30 17:29:34 +08:00
|
|
|
Args,
|
|
|
|
std::make_unique<TestClangTidyAction<CheckTypes...>>(Checks, Finder,
|
|
|
|
Context),
|
2019-06-26 23:04:33 +08:00
|
|
|
Files.get());
|
2015-10-07 16:35:23 +08:00
|
|
|
InMemoryFileSystem->addFile(Filename, 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(Code));
|
2015-08-11 20:13:15 +08:00
|
|
|
for (const auto &FileContent : PathsToContent) {
|
2015-10-07 16:35:23 +08:00
|
|
|
InMemoryFileSystem->addFile(
|
|
|
|
Twine("include/") + FileContent.first, 0,
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(FileContent.second));
|
2015-08-11 19:37:48 +08:00
|
|
|
}
|
2014-10-07 23:49:36 +08:00
|
|
|
Invocation.setDiagnosticConsumer(&DiagConsumer);
|
2015-08-12 15:57:16 +08:00
|
|
|
if (!Invocation.run()) {
|
|
|
|
std::string ErrorText;
|
[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
|
|
|
for (const auto &Error : DiagConsumer.take()) {
|
2015-08-12 15:57:16 +08:00
|
|
|
ErrorText += Error.Message.Message + "\n";
|
|
|
|
}
|
|
|
|
llvm::report_fatal_error(ErrorText);
|
|
|
|
}
|
2014-10-07 23:49:36 +08:00
|
|
|
|
2014-02-27 22:28:02 +08:00
|
|
|
tooling::Replacements Fixes;
|
[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> Diags = DiagConsumer.take();
|
|
|
|
for (const ClangTidyError &Error : Diags) {
|
[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
|
|
|
if (const auto *ChosenFix = tooling::selectFirstFix(Error))
|
|
|
|
for (const auto &FileAndFixes : *ChosenFix) {
|
|
|
|
for (const auto &Fix : FileAndFixes.second) {
|
|
|
|
auto Err = Fixes.add(Fix);
|
|
|
|
// FIXME: better error handling. Keep the behavior for now.
|
|
|
|
if (Err) {
|
|
|
|
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
|
|
|
|
return "";
|
|
|
|
}
|
2016-08-09 15:54:49 +08:00
|
|
|
}
|
2016-08-01 18:16:39 +08:00
|
|
|
}
|
2016-08-09 15:54:49 +08:00
|
|
|
}
|
2014-05-09 20:24:09 +08:00
|
|
|
if (Errors)
|
[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
|
|
|
*Errors = std::move(Diags);
|
2016-07-11 21:53:21 +08:00
|
|
|
auto Result = tooling::applyAllReplacements(Code, Fixes);
|
|
|
|
if (!Result) {
|
|
|
|
// FIXME: propogate the error.
|
|
|
|
llvm::consumeError(Result.takeError());
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return *Result;
|
2014-02-27 22:28:02 +08:00
|
|
|
}
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2015-03-02 19:55:04 +08:00
|
|
|
#define EXPECT_NO_CHANGES(Check, Code) \
|
|
|
|
EXPECT_EQ(Code, runCheckOnCode<Check>(Code))
|
|
|
|
|
2014-02-27 22:28:02 +08:00
|
|
|
} // namespace test
|
2013-07-29 16:19:24 +08:00
|
|
|
} // namespace tidy
|
|
|
|
} // namespace clang
|
|
|
|
|
2015-03-10 00:52:33 +08:00
|
|
|
#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
|