1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
/*
SPDX-FileCopyrightText: 2025 Alexander Lohnau <alexander.lohnau@gmx.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "AccessSpecifierManager.h"
#include "ClazyContext.h"
#include "ClazyVisitHelper.h"
#include "TypeUtils.h"
#include "Utils.h"
#include "checkbase.h"
#include "checkmanager.h"
#include "clang-tidy/ClangTidyCheck.h"
#include "clang-tidy/ClangTidyModule.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include <utility>
#if LLVM_VERSION_MAJOR < 22
#include "clang-tidy/ClangTidyModuleRegistry.h"
#endif
using namespace clang::ast_matchers;
using namespace clang::tidy;
using namespace clang;
class FullASTVisitor : public RecursiveASTVisitor<FullASTVisitor>
{
public:
explicit FullASTVisitor(ClazyContext *context, ClangTidyCheck &Check, const clazy::VisitHelper::Visitors &visitors)
: m_context(context)
, m_visitors(visitors)
{
}
bool VisitStmt(Stmt *stmt)
{
return clazy::VisitHelper::VisitStmt(stmt, m_context, m_visitors);
}
bool VisitDecl(Decl *decl)
{
return clazy::VisitHelper::VisitDecl(decl, m_context, m_visitors);
}
private:
ClazyContext *const m_context;
const clazy::VisitHelper::Visitors &m_visitors;
};
std::vector<std::string> s_enabledChecks;
class ClazyCheck : public ClangTidyCheck
{
public:
ClazyCheck(StringRef CheckName, ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context)
, m_shouldRunClazyChecks(s_enabledChecks.empty())
, clangTidyContext(Context)
, clazyContext(nullptr)
{
// so that we later know which check was registered
s_enabledChecks.emplace_back(CheckName);
}
~ClazyCheck() override
{
for (auto &checkPair : m_allChecks) {
delete checkPair.first;
}
m_allChecks.clear();
delete clazyContext;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override
{
if (!m_shouldRunClazyChecks) {
return;
}
Finder->addMatcher(translationUnitDecl().bind("tu"), this);
const auto checks = CheckManager::instance()->availableChecks(ManualCheckLevel);
for (const auto &availCheck : checks) {
const std::string checkName = "clazy-" + availCheck.name;
if (std::ranges::find(s_enabledChecks, checkName) == s_enabledChecks.end()) {
continue;
}
auto *check = availCheck.factory();
m_visitors.addCheck(availCheck.options, check);
check->registerASTMatchers(*Finder);
m_allChecks.emplace_back(check, availCheck.options);
}
}
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
{
if (!m_shouldRunClazyChecks) {
return;
}
clazyContext->astContext = Result.Context;
FullASTVisitor visitor(clazyContext, *this, m_visitors);
auto translationUnit = const_cast<TranslationUnitDecl *>(Result.Nodes.getNodeAs<TranslationUnitDecl>("tu"));
visitor.TraverseDecl(translationUnit);
}
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
{
if (!m_shouldRunClazyChecks) {
return;
}
clazyContext = new ClazyContext(nullptr, PP->getSourceManager(), getLangOpts(), PP->getPreprocessorOpts(), "", "", "", {}, {}, emitDiagnostic, true);
clazyContext->registerPreprocessorCallbacks(*PP);
for (const auto &[check, options] : m_allChecks) {
check->m_context = clazyContext;
if (options & RegisteredCheck::Option_PreprocessorCallbacks) {
check->enablePreProcessorCallbacks(*PP);
}
}
}
const bool m_shouldRunClazyChecks;
ClangTidyContext *clangTidyContext;
ClazyContext *clazyContext = nullptr;
std::vector<std::pair<CheckBase *, RegisteredCheck::Options>> m_allChecks;
clazy::VisitHelper::Visitors m_visitors;
// setting the engine fixes a weird crash, but we still run in a codepath where we do not know the check name in the end
const ClazyContext::WarningReporter emitDiagnostic = //
[this](const std::string &checkName,
const clang::SourceLocation &loc,
clang::DiagnosticIDs::Level level,
std::string error,
const std::vector<clang::FixItHint> &fixits) {
clangTidyContext->diag("clazy-" + checkName, loc, error, level) << fixits;
};
};
class ClazyModule : public ClangTidyModule
{
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override
{
for (const auto &check : CheckManager::instance()->availableChecks(CheckLevel::ManualCheckLevel)) {
CheckFactories.registerCheck<ClazyCheck>("clazy-" + check.name);
}
}
};
namespace clang::tidy
{
static ClangTidyModuleRegistry::Add<ClazyModule> X("clazy-module", "Adds all Clazy checks to clang-tidy.");
volatile int ClazyModuleAnchorSource = 0;
}
|