diff --git a/src/plugins/c2py/ast_consumer.cpp b/src/plugins/c2py/ast_consumer.cpp index 6fad174..a7ba71f 100644 --- a/src/plugins/c2py/ast_consumer.cpp +++ b/src/plugins/c2py/ast_consumer.cpp @@ -33,7 +33,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match some concepts in c2py::concepts { - matchers::concept_t ma{worker}; + matcher ma{worker}; MatchFinder mf; mf.addMatcher(namespaceDecl(hasName("c2py"), forEach(namespaceDecl(hasName("concepts"), forEach(namedDecl().bind("conceptDecl"))))), &ma); // a few concepts that may be present in know library @@ -54,7 +54,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match the config variables in c2py_module in main file // e.g. auto ns = "N"; etc... { - matchers::module_vars_t vars{worker}; + matcher vars{worker}; MatchFinder mf; mf.addMatcher(namespaceDecl(isExpansionInMainFile(), hasName("c2py_module"), // forEach(varDecl().bind("decl"))), @@ -66,7 +66,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match automatically detected classes. { - matchers::cls_t ma{worker}; + matcher ma{worker}; MatchFinder mf; if (auto const &s = worker->module_info.match_names; not s.empty()) mf.addMatcher(cxxRecordDecl(matchesName(s), unless(isExpansionInSystemHeader())).bind("class"), &ma); @@ -80,7 +80,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match automatically detected functions // except friend function, or method (will be done by inspecting the relevant classes) { - matchers::matcher ma{worker}; + matcher ma{worker}; // NB the std header clause will remove everything included with -isystem (e.g. other libs) MatchFinder mf; if (auto const &s = worker->module_info.match_names; not s.empty()) @@ -96,7 +96,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match the enums { - matchers::enum_t ma{worker}; + matcher ma{worker}; MatchFinder mf; if (auto const &s = worker->module_info.match_names; not s.empty()) mf.addMatcher(enumDecl(matchesName(s), unless(isExpansionInSystemHeader())).bind("en"), &ma); @@ -108,7 +108,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match the c2py::dispatch declarations in c2py_module::functions { - matchers::module_fun_dispatch_t ma{worker}; + matcher ma{worker}; MatchFinder mf; // match the dispatch directly under add, not those of the nested struct mf.addMatcher(namespaceDecl(isExpansionInMainFile(), hasName("c2py_module"), // @@ -121,7 +121,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match the classes declared in module by using { - matchers::module_cls_wrap_t ma{worker}; + matcher ma{worker}; MatchFinder mf; mf.addMatcher(namespaceDecl(isExpansionInMainFile(), hasName("c2py_module"), // forEach(typeAliasDecl().bind("decl"))), @@ -132,7 +132,7 @@ void ast_consumer::HandleTranslationUnit(clang::ASTContext &ctx) { // ------- Match the classes in c2py_module::add: in main file { - matchers::module_cls_info_t ma{worker}; + matcher ma{worker}; MatchFinder mf; mf.addMatcher(namespaceDecl(hasName("c2py_module"), forEach(classTemplateDecl().bind("decl"))), &ma); mf.matchAST(ctx); diff --git a/src/plugins/c2py/matchers.cpp b/src/plugins/c2py/matchers.cpp index 089d564..a0aecd5 100644 --- a/src/plugins/c2py/matchers.cpp +++ b/src/plugins/c2py/matchers.cpp @@ -9,266 +9,263 @@ static const struct { util::logger note = util::logger{&std::cout, "-- ", "\033[1;32mNote: \033[0m"}; util::logger error = util::logger{&std::cout, "-- ", "\033[1;33mError: \033[0m"}; } logs; -namespace matchers { - - // ****************************************************** - // extract_literal functions. - // Take a string/int/bool, etc from a VarDecl with literal value - // ****************************************************** - - // Gets the initializer of the VarDecl, removing the ImplicitCastExpr - const clang::Expr *get_vardecl_initlalizer(clang::VarDecl const *decl) { - auto *value = decl->getAnyInitializer(); - auto *iexpr = llvm::dyn_cast_or_null(value); - while (iexpr) { - value = iexpr->getSubExpr(); - iexpr = llvm::dyn_cast_or_null(value); - } - return value; - } - - // Literal string - void extract_literal(clang::VarDecl const *d, str_t &x) { - auto value = get_vardecl_initlalizer(d); - if (auto *s = llvm::dyn_cast_or_null(value)) - x = s->getString().str(); - else { clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal string."); } - } - - // Regex : a string into a regex with control - void extract_literal(clang::VarDecl const *d, std::optional &x) { - str_t s; - extract_literal(d, s); - try { - if (not s.empty()) x = std::regex{s}; - } catch (std::regex_error const &e) { - logs.error(fmt::format("Regular Expression is invalid: \n {}", e.what())); - clu::emit_error(d, "c2py: invalid C++ regular expression. Cf std::regex documentation for information."); - } - } - // Bool - void extract_literal(clang::VarDecl const *d, bool &x) { - auto value = get_vardecl_initlalizer(d); - if (auto *b = llvm::dyn_cast_or_null(value)) - x = b->getValue(); - else - clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal bool."); - } - - // long - void extract_literal(clang::VarDecl const *d, long &x) { - auto value = get_vardecl_initlalizer(d); - auto const &ctx = d->getASTContext(); - if (auto *i = llvm::dyn_cast_or_null(value)) - x = long{i->EvaluateKnownConstInt(ctx).getExtValue()}; - else - clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal integer."); +// ****************************************************** +// extract_literal functions. +// Take a string/int/bool, etc from a VarDecl with literal value +// ****************************************************** + +// Gets the initializer of the VarDecl, removing the ImplicitCastExpr +const clang::Expr *get_vardecl_initlalizer(clang::VarDecl const *decl) { + auto *value = decl->getAnyInitializer(); + auto *iexpr = llvm::dyn_cast_or_null(value); + while (iexpr) { + value = iexpr->getSubExpr(); + iexpr = llvm::dyn_cast_or_null(value); } - - // Check the module_init function exists - void check_module_init(clang::VarDecl const *d, bool &x) { - auto value = get_vardecl_initlalizer(d); - if (auto *l = llvm::dyn_cast_or_null(value)) { - if (l->getCallOperator()->getNumParams() != 0) - clu::emit_error(d, "The init function must take 0 parameters."); - else - x = true; - } else - clu::emit_error(d, "c2py: Variable of incorrect type. Expected a lambda."); - } - - // ----------------------------------------------------- - - void concept_t::run(const MatchResult &Result) { - - // warning : we must use the if here, as result may not be a concept - // since there is no explicit ConceptDecl Matcher in AST yet. - if (const auto *cpt = Result.Nodes.getNodeAs("conceptDecl")) { - auto cname = cpt->getName().str(); - - if (cname == "IsConvertiblePy2C") - worker->IsConvertiblePy2C = cpt; - else if (cname == "IsConvertibleC2Py") - worker->IsConvertibleC2Py = cpt; - else if (cname == "force_instantiation_add_methods") - worker->force_instantiation_add_methods = cpt; - else if (cname == "HasSerializeLikeBoost") - worker->HasSerializeLikeBoost = cpt; - else if (cname == "Storable") - worker->HasHdf5 = cpt; - // else ignore the others concepts - } + return value; +} + +// Literal string +void extract_literal(clang::VarDecl const *d, str_t &x) { + auto value = get_vardecl_initlalizer(d); + if (auto *s = llvm::dyn_cast_or_null(value)) + x = s->getString().str(); + else { clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal string."); } +} + +// Regex : a string into a regex with control +void extract_literal(clang::VarDecl const *d, std::optional &x) { + str_t s; + extract_literal(d, s); + try { + if (not s.empty()) x = std::regex{s}; + } catch (std::regex_error const &e) { + logs.error(fmt::format("Regular Expression is invalid: \n {}", e.what())); + clu::emit_error(d, "c2py: invalid C++ regular expression. Cf std::regex documentation for information."); } - - // ------------------------------------------------- - - void module_vars_t::run(const MatchResult &Result) { - auto const *decl = Result.Nodes.getNodeAs("decl"); - assert(decl); - - static auto vars = std::map>{ - {"module_name", [](auto *d, auto &M) { extract_literal(d, M.module_name); }}, - {"package_name", [](auto *d, auto &M) { extract_literal(d, M.package_name); }}, - {"documentation", [](auto *d, auto &M) { extract_literal(d, M.documentation); }}, - {"module_init", [](auto *d, auto &M) { check_module_init(d, M.has_module_init); }}, - {"get_set_as_properties", [](auto *d, auto &M) { extract_literal(d, M.get_set_as_properties); }}, - {"match_names", [](auto *d, auto &M) { extract_literal(d, M.match_names); }}, - {"reject_names", [](auto *d, auto &M) { extract_literal(d, M.reject_names); }}, - {"match_files", [](auto *d, auto &M) { extract_literal(d, M.match_files); }}, - }; - - if (auto it = vars.find(decl->getName().str()); it != vars.end()) - it->second(decl, worker->module_info); +} + +// Bool +void extract_literal(clang::VarDecl const *d, bool &x) { + auto value = get_vardecl_initlalizer(d); + if (auto *b = llvm::dyn_cast_or_null(value)) + x = b->getValue(); + else + clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal bool."); +} + +// long +void extract_literal(clang::VarDecl const *d, long &x) { + auto value = get_vardecl_initlalizer(d); + auto const &ctx = d->getASTContext(); + if (auto *i = llvm::dyn_cast_or_null(value)) + x = long{i->EvaluateKnownConstInt(ctx).getExtValue()}; + else + clu::emit_error(d, "c2py: Variable of incorrect type. Expected a literal integer."); +} + +// Check the module_init function exists +void check_module_init(clang::VarDecl const *d, bool &x) { + auto value = get_vardecl_initlalizer(d); + if (auto *l = llvm::dyn_cast_or_null(value)) { + if (l->getCallOperator()->getNumParams() != 0) + clu::emit_error(d, "The init function must take 0 parameters."); else - clu::emit_error(decl, "c2py: module variable declaration is not recognized."); + x = true; + } else + clu::emit_error(d, "c2py: Variable of incorrect type. Expected a lambda."); +} + +// ----------------------------------------------------- + +template <> void matcher::run(const MatchResult &Result) { + + // warning : we must use the if here, as result may not be a concept + // since there is no explicit ConceptDecl Matcher in AST yet. + if (const auto *cpt = Result.Nodes.getNodeAs("conceptDecl")) { + auto cname = cpt->getName().str(); + + if (cname == "IsConvertiblePy2C") + worker->IsConvertiblePy2C = cpt; + else if (cname == "IsConvertibleC2Py") + worker->IsConvertibleC2Py = cpt; + else if (cname == "force_instantiation_add_methods") + worker->force_instantiation_add_methods = cpt; + else if (cname == "HasSerializeLikeBoost") + worker->HasSerializeLikeBoost = cpt; + else if (cname == "Storable") + worker->HasHdf5 = cpt; + // else ignore the others concepts } - - // ------------------------------------------------- - - void module_fun_dispatch_t::run(const MatchResult &Result) { - auto const *decl = Result.Nodes.getNodeAs("decl"); - assert(decl); - analyse_dispatch(worker->module_info.functions, decl); - } - - // -------------------------------------------------------------------------------- - - void module_cls_wrap_t::run(const MatchResult &Result) { - auto *d = Result.Nodes.getNodeAs("decl"); - assert(d); - if (auto *cls = d->getUnderlyingType()->getAsCXXRecordDecl()) { - if (auto *cls_i = llvm::dyn_cast(cls)) { - // probably useless ? - if (not cls->hasDefinition()) { clu::emit_error(d, "c2py: The class template should be explicitly instantiated"); } - } - worker->module_info.classes.emplace_back(d->getName().str(), cls_info_t{cls}); - //if (not inserted) clu::emit_error(d, "[c2py] Class declaration duplication"); +} + +// ------------------------------------------------- + +template <> void matcher::run(const MatchResult &Result) { + auto const *decl = Result.Nodes.getNodeAs("decl"); + assert(decl); + + static auto vars = std::map>{ + {"module_name", [](auto *d, auto &M) { extract_literal(d, M.module_name); }}, + {"package_name", [](auto *d, auto &M) { extract_literal(d, M.package_name); }}, + {"documentation", [](auto *d, auto &M) { extract_literal(d, M.documentation); }}, + {"module_init", [](auto *d, auto &M) { check_module_init(d, M.has_module_init); }}, + {"get_set_as_properties", [](auto *d, auto &M) { extract_literal(d, M.get_set_as_properties); }}, + {"match_names", [](auto *d, auto &M) { extract_literal(d, M.match_names); }}, + {"reject_names", [](auto *d, auto &M) { extract_literal(d, M.reject_names); }}, + {"match_files", [](auto *d, auto &M) { extract_literal(d, M.match_files); }}, + }; + + if (auto it = vars.find(decl->getName().str()); it != vars.end()) + it->second(decl, worker->module_info); + else + clu::emit_error(decl, "c2py: module variable declaration is not recognized."); +} + +// ------------------------------------------------- + +template <> void matcher::run(const MatchResult &Result) { + auto const *decl = Result.Nodes.getNodeAs("decl"); + assert(decl); + analyse_dispatch(worker->module_info.functions, decl); +} + +// -------------------------------------------------------------------------------- + +template <> void matcher::run(const MatchResult &Result) { + auto *d = Result.Nodes.getNodeAs("decl"); + assert(d); + if (auto *cls = d->getUnderlyingType()->getAsCXXRecordDecl()) { + if (auto *cls_i = llvm::dyn_cast(cls)) { + // probably useless ? + if (not cls->hasDefinition()) { clu::emit_error(d, "c2py: The class template should be explicitly instantiated"); } } + worker->module_info.classes.emplace_back(d->getName().str(), cls_info_t{cls}); + //if (not inserted) clu::emit_error(d, "[c2py] Class declaration duplication"); } +} - // ------------------------------------------------- +// ------------------------------------------------- - void module_cls_info_t::run(const MatchResult &Result) { - auto const *decl = Result.Nodes.getNodeAs("decl"); - assert(decl); +template <> void matcher::run(const MatchResult &Result) { + auto const *decl = Result.Nodes.getNodeAs("decl"); + assert(decl); - auto varname = decl->getName().str(); - if (varname == "add_methods_to") - worker->add_methods_to = decl; - else - clu::emit_error(decl, "c2py: unknown class declaration"); - } + auto varname = decl->getName().str(); + if (varname == "add_methods_to") + worker->add_methods_to = decl; + else + clu::emit_error(decl, "c2py: unknown class declaration"); +} - // ------------------------------------------------- +// ------------------------------------------------- - void matchers::cls_t::run(const clang::ast_matchers::MatchFinder::MatchResult &Result) { - const auto *cls = Result.Nodes.getNodeAs("class"); +template <> void matcher::run(const clang::ast_matchers::MatchFinder::MatchResult &Result) { + const auto *cls = Result.Nodes.getNodeAs("class"); - if (!cls) return; // just in case - if (cls->getASTContext().getDiagnostics().hasErrorOccurred()) return; + if (!cls) return; // just in case + if (cls->getASTContext().getDiagnostics().hasErrorOccurred()) return; - // Filter some automatic instantiation from the compiler, and alike - if (!cls->getSourceRange().isValid()) return; - if (!cls->isCompleteDefinition()) return; // skip forward declaration. - if (cls->isLambda()) return; // no lambda + // Filter some automatic instantiation from the compiler, and alike + if (!cls->getSourceRange().isValid()) return; + if (!cls->isCompleteDefinition()) return; // skip forward declaration. + if (cls->isLambda()) return; // no lambda #if LLVM_VERSION_MAJOR < 18 - if (not((cls->getTagKind() == clang::TTK_Struct) or (cls->getTagKind() == clang::TTK_Class))) return; // just struct and class + if (not((cls->getTagKind() == clang::TTK_Struct) or (cls->getTagKind() == clang::TTK_Class))) return; // just struct and class #else - if (not((cls->getTagKind() == clang::TagTypeKind::Struct) or (cls->getTagKind() == clang::TagTypeKind::Class))) return; // just struct and class + if (not((cls->getTagKind() == clang::TagTypeKind::Struct) or (cls->getTagKind() == clang::TagTypeKind::Class))) return; // just struct and class #endif - if (auto *s = llvm::dyn_cast_or_null(cls)) { - if (!s->isExplicitInstantiationOrSpecialization()) return; - } + if (auto *s = llvm::dyn_cast_or_null(cls)) { + if (!s->isExplicitInstantiationOrSpecialization()) return; + } - // Reject the declaration of the template itself. - if (cls->getDescribedClassTemplate()) return; + // Reject the declaration of the template itself. + if (cls->getDescribedClassTemplate()) return; - // FIXME : check ? why ? - // Reject template specialization - if (llvm::dyn_cast_or_null(cls)) return; + // FIXME : check ? why ? + // Reject template specialization + if (llvm::dyn_cast_or_null(cls)) return; - // Apply the filters - auto qname = cls->getQualifiedNameAsString(); - auto &M = worker->module_info; + // Apply the filters + auto qname = cls->getQualifiedNameAsString(); + auto &M = worker->module_info; - // apply c2py_ignore and reject_names - if (is_rejected(cls, M.reject_names, &logs.rejected)) return; + // apply c2py_ignore and reject_names + if (is_rejected(cls, M.reject_names, &logs.rejected)) return; - // Reject classes defined in c2py_module - if (qname.starts_with("c2py_module::")) return; + // Reject classes defined in c2py_module + if (qname.starts_with("c2py_module::")) return; - // Insert in the module class list - str_t py_name = util::camel_case(cls->getNameAsString()); - M.classes.emplace_back(py_name, cls_info_t{cls}); // + // Insert in the module class list + str_t py_name = util::camel_case(cls->getNameAsString()); + M.classes.emplace_back(py_name, cls_info_t{cls}); // - //if (not inserted) clu::emit_error(cls, "Class rejected. Should have another class with the same Python name ??"); - } + //if (not inserted) clu::emit_error(cls, "Class rejected. Should have another class with the same Python name ??"); +} - // ------------------------------------------------- - template <> void matcher::run(const MatchResult &Result) { +// ------------------------------------------------- +template <> void matcher::run(const MatchResult &Result) { - auto *f = Result.Nodes.getNodeAs("func"); - if (!f) return; + auto *f = Result.Nodes.getNodeAs("func"); + if (!f) return; - // ............. Discard some automatic instantiation from the compiler, and alike + // ............. Discard some automatic instantiation from the compiler, and alike - // f in e.g. operator new, internal function, not defined in the sources - // function defined in std headers are already filtered by the AST Matching - if (!f->getBeginLoc().isValid()) return; + // f in e.g. operator new, internal function, not defined in the sources + // function defined in std headers are already filtered by the AST Matching + if (!f->getBeginLoc().isValid()) return; - // Skip automatic template instantiation by the compiler - if (f->isFunctionTemplateSpecialization()) return; + // Skip automatic template instantiation by the compiler + if (f->isFunctionTemplateSpecialization()) return; - // Skip deleted function - if (f->isDeleted()) return; + // Skip deleted function + if (f->isDeleted()) return; - // Skip some internal functions generated by coroutines - if (f->getNameAsString().starts_with("__builtin_coro_")) return; + // Skip some internal functions generated by coroutines + if (f->getNameAsString().starts_with("__builtin_coro_")) return; - // skip the deduction guides (CTAD) - if (llvm::dyn_cast_or_null(f)) return; + // skip the deduction guides (CTAD) + if (llvm::dyn_cast_or_null(f)) return; - // Reject the declaration of the template itself. - if (f->getDescribedFunctionTemplate()) return; + // Reject the declaration of the template itself. + if (f->getDescribedFunctionTemplate()) return; - // reject method - // FIXME : in matcher ? - if (llvm::dyn_cast_or_null(f)) return; + // reject method + // FIXME : in matcher ? + if (llvm::dyn_cast_or_null(f)) return; - // Discard some special function - if (f->getNameAsString().starts_with("operator")) return; + // Discard some special function + if (f->getNameAsString().starts_with("operator")) return; - // apply c2py_ignore and the reject_name regex - auto &M = worker->module_info; - if (is_rejected(f, M.reject_names, &logs.rejected)) return; + // apply c2py_ignore and the reject_name regex + auto &M = worker->module_info; + if (is_rejected(f, M.reject_names, &logs.rejected)) return; - // Reject functions defined in c2py_module - auto fqname = f->getQualifiedNameAsString(); - if (fqname.starts_with("c2py_module::")) return; + // Reject functions defined in c2py_module + auto fqname = f->getQualifiedNameAsString(); + if (fqname.starts_with("c2py_module::")) return; - // Insert in the module function list. Unicity will be taken care of later by worker. - str_t py_name = f->getNameAsString(); - M.functions[py_name].push_back(fnt_info_t{f}); - } - - // ------------------------------------------------- + // Insert in the module function list. Unicity will be taken care of later by worker. + str_t py_name = f->getNameAsString(); + M.functions[py_name].push_back(fnt_info_t{f}); +} - void enum_t::run(const MatchResult &Result) { - auto *enu = Result.Nodes.getNodeAs("en"); - if (!enu) return; +// ------------------------------------------------- - auto &M = worker->module_info; - auto qname = enu->getQualifiedNameAsString(); +template <> void matcher::run(const MatchResult &Result) { + auto *enu = Result.Nodes.getNodeAs("en"); + if (!enu) return; - if (M.reject_names and std::regex_match(qname, M.reject_names.value())) { - logs.rejected(fmt::format(R"RAW({0} [{1}])RAW", qname, "reject_names")); - return; - } + auto &M = worker->module_info; + auto qname = enu->getQualifiedNameAsString(); - M.enums.push_back(enu); + if (M.reject_names and std::regex_match(qname, M.reject_names.value())) { + logs.rejected(fmt::format(R"RAW({0} [{1}])RAW", qname, "reject_names")); + return; } -} // namespace matchers + M.enums.push_back(enu); +} diff --git a/src/plugins/c2py/matchers.hpp b/src/plugins/c2py/matchers.hpp index 9a7928c..73ccb83 100644 --- a/src/plugins/c2py/matchers.hpp +++ b/src/plugins/c2py/matchers.hpp @@ -2,42 +2,17 @@ #include "llvm/ADT/APFloat.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "worker.hpp" -namespace matchers { - using MatchCallback = clang::ast_matchers::MatchFinder::MatchCallback; - using MatchResult = clang::ast_matchers::MatchFinder::MatchResult; +using MatchCallback = clang::ast_matchers::MatchFinder::MatchCallback; +using MatchResult = clang::ast_matchers::MatchFinder::MatchResult; - // FIXME : choose MACRO or template below ... +enum class mtch { Concept, ModuleVars, ModuleFntDispatch, ModuleClsWrap, ModuleClsInfo, Cls, Fnt, Enum }; - enum MatcherList { Concept, ModuleVars, ModuleFntDispatch, ModuleClsWrap, ModuleClsInfo, Cls, Fnt, Enum }; +// cpp file implements for all M in mtch values... +template class matcher : public MatchCallback { + worker_t *worker; - template class matcher : public MatchCallback { - worker_t *worker; - - public: - matcher(worker_t *worker) : worker{worker} {} - void run(const MatchResult &Result) override; - }; - -// NOLINTNEXTLINE -#define CLAIR_DECLARE_MATCHER(CLS) \ - class CLS : public MatchCallback { \ - worker_t *worker; \ - \ - public: \ - CLS(worker_t *worker) : worker{worker} {} \ - void run(const MatchResult &Result) override; \ - }; - - CLAIR_DECLARE_MATCHER(concept_t); - CLAIR_DECLARE_MATCHER(module_vars_t); - CLAIR_DECLARE_MATCHER(module_fun_dispatch_t); - CLAIR_DECLARE_MATCHER(module_cls_wrap_t); - CLAIR_DECLARE_MATCHER(module_cls_info_t); - CLAIR_DECLARE_MATCHER(cls_t); - //CLAIR_DECLARE_MATCHER(fnt_t); - CLAIR_DECLARE_MATCHER(enum_t); - -#undef CLAIR_DECLARE_MATCHER - -} // namespace matchers + public: + matcher(worker_t *worker) : worker{worker} {} + void run(const MatchResult &Result) override; +};