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) {
|
[clang-tidy] Added virtual isLanguageVersionSupported to ClangTidyCheck
Summary:
Motivated by [[ https://bugs.llvm.org/show_bug.cgi?id=45045 | Tune inspections to a specific C++ standard. ]]
Moves the isLanguageVersionSupported virtual function from `MakeSmartPtrCheck` to the base `ClangTidyCheck` class.
This will disable registering matchers or pp callbacks on unsupported language versions for a check.
Having it as a standalone function is cleaner than manually disabling the check in the register function and should hopefully
encourage check developers to actually restrict the check based on language version.
As an added bonus this could enable automatic detection of what language version a check runs on for the purpose of documentation generation
Reviewers: aaron.ballman, gribozavr2, Eugene.Zelenko, JonasToth, alexfh, hokein
Reviewed By: gribozavr2
Subscribers: xazax.hun, jkorous, arphaman, kadircet, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D75289
2020-02-28 21:03:30 +08:00
|
|
|
if (!Check->isLanguageVersionSupported(Context.getLangOpts()))
|
|
|
|
continue;
|
2015-10-06 21:52:51 +08:00
|
|
|
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");
|
2020-01-29 03:23:46 +08:00
|
|
|
std::string extension(
|
|
|
|
std::string(llvm::sys::path::extension(Filename.str())));
|
2017-10-26 16:37:25 +08:00
|
|
|
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
|