Clang FunctionDecl匹配器重复匹配:如何仅匹配函数返回类型
问题描述
在Clang中使用FunctionDecl节点匹配器获取函数返回类型时,匹配器会在特定场景下同时匹配第一个参数。
代码环境
Instrumentizer类代码
#include <string> #include <vector> #include "clang/Tooling/Tooling.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "InstrumentizerMatchers.hpp" using namespace clang::ast_matchers; static llvm::cl::OptionCategory InstrumentizerAPICategory(""); class Instrumentizer { public: Instrumentizer(std::string source) { this->source_file = source; const char* argv[] = { "instrumentizer", source.c_str(), "--" }; int argc = 3; options = new CommonOptionsParser(argc, argv, InstrumentizerAPICategory); auto files = options->getSourcePathList(); tool = new RefactoringTool(options->getCompilations(), files); this->Callback = new InstrumentizerCallback(); } ~Instrumentizer(); // Config void addTypeToMatch(std::string regex){ // Base matcher TypeMatcher NaiveTypeMatcher = instrumentizer_matchers::asUnqualifiedType(asString(regex)); // Typeloc matcher auto NaiveTypeLocMatcher = typeLoc(loc(NaiveTypeMatcher)).bind("type"); // Checks if current node or its descendants have a float type (to detect arrays and pointers) // Also has a typeLoc variant TypeMatcher TypeMatcher = anyOf(hasDescendant(NaiveTypeMatcher), NaiveTypeMatcher); auto TypeLocMatcher = anyOf(hasDescendant(NaiveTypeLocMatcher), NaiveTypeLocMatcher); // Matches a function return type auto FunctionReturnMatcher = functionDecl(hasTypeLoc(TypeLocMatcher)).bind("floating_function"); Matcher.addMatcher(FunctionReturnMatcher, Callback); } // Get infos std::vector<variable_t>& getVariables() { // If it has not been done yet, run the tool with // callbacks that will obtain and save variable names/types std::cout << "in getVariables()" << std::endl; if(variables.size() == 0) { tool->run(newFrontendActionFactory(&Matcher).get()); } return variables; } private: // source file std::string source_file; // Store all the variables and types std::vector<variable_t> variables; clang::tooling::CommonOptionsParser *options; // Underlying tool used to analyse the code // It will also be used to apply the replacements for us clang::tooling::RefactoringTool *tool; // Matcher to be ran on the AST MatchFinder Matcher; InstrumentizerCallback *Callback; };
回调函数代码
#pragma once #include <string> #include <sstream> #include <cstring> #include <iostream> #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::tooling; using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; extern "C" { typedef struct { size_t type_idx; // index into the type list char* name; // name of the variable/function } variable_t; } // extern C namespace instrumentizer_matchers { // Matches if Name is contained in the string description of the qualType AST_MATCHER_P(clang::QualType, inString, std::string, Name) { return Node.getAsString().find(Name, Node.getAsString().length() - Name.length()) != -1; } // Matches if the type without Qualifiers matches AST_MATCHER_P(clang::QualType, asUnqualifiedType, Matcher<clang::QualType>, InnerMatcher) { return InnerMatcher.matches(Node.getLocalUnqualifiedType(), Finder, Builder); } } class InstrumentizerCallback : public MatchFinder::MatchCallback { public: InstrumentizerCallback(){} ~InstrumentizerCallback(){} virtual void run(const MatchFinder::MatchResult &Result) { auto SM = Result.SourceManager; // Handle function return types auto func_decl = Result.Nodes.getNodeAs<clang::FunctionDecl>("floating_function"); if (func_decl) { auto beginLoc = func_decl->getBeginLoc(); std::cout << "beginLoc: " << beginLoc.printToString(*SM) << std::endl; std::cout << "name: " << func_decl->getNameAsString() << std::endl; auto type = Result.Nodes.getNodeAs<clang::TypeLoc>("type"); std::cout << "position: " << type->getSourceRange().printToString(*SM) << std::endl; return; } } };
主函数代码
#include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" #include "clang/Tooling/Refactoring.h" #include "FloatingTypeFindingAction.h" #include "Instrumentizer_class.cpp" using namespace llvm; using namespace clang::tooling; int main(int argc, const char **argv) { Instrumentizer* inst = new Instrumentizer("test.cc"); inst->addTypeToMatch("double"); inst->addTypeToMatch("test"); std::vector<variable_t> var = inst->getVariables(); }
测试文件test.cc
typedef double test; double fun(test x){ return x; } int main(int argc, char **argv) { return 0; }
问题现象
运行程序后,回调会同时匹配函数返回类型double和参数类型test(test是double的typedef),输出如下:
beginLoc: /local/ferro/testestestestestestestestest/src/../api/test.cc:3:1 name: fun position: </local/ferro/testestestestestestestestest/src/../api/test.cc:3:1> beginLoc: /local/ferro/testestestestestestestestest/src/../api/test.cc:3:1 name: fun position: </local/ferro/testestestestestestestestest/src/../api/test.cc:3:12>
需要调整匹配器,使其仅匹配函数的返回类型,避免匹配参数类型。
解决方案
问题核心是hasTypeLoc会遍历FunctionDecl下所有关联的TypeLoc节点(包括返回值、参数、函数内部类型),再加上hasDescendant的逻辑,导致参数里的typedef类型被误匹配。要只匹配返回类型的TypeLoc,做以下修改:
1. 修改addTypeToMatch方法的匹配器逻辑
替换原有的FunctionReturnMatcher定义,使用hasReturnTypeLoc专门定位函数返回类型的位置:
void addTypeToMatch(std::string regex){ // Base matcher TypeMatcher NaiveTypeMatcher = instrumentizer_matchers::asUnqualifiedType(asString(regex)); // 支持匹配包含目标类型的复杂类型(数组、指针等) TypeMatcher TargetTypeMatcher = anyOf( NaiveTypeMatcher, hasDescendant(NaiveTypeMatcher) ); // 仅匹配函数返回类型的TypeLoc auto ReturnTypeLocMatcher = functionDecl( bind("floating_function"), hasReturnTypeLoc( typeLoc(loc(TargetTypeMatcher)).bind("type") ) ); Matcher.addMatcher(ReturnTypeLocMatcher, Callback); }
修改原理
hasReturnTypeLoc是Clang AST匹配器中专门用于匹配函数返回类型对应TypeLoc的接口,只会定位到函数签名最前方的返回类型位置,不会涉及参数或其他区域的TypeLoc。- 原有的
hasTypeLoc没有范围限制,会匹配FunctionDecl节点下所有符合条件的TypeLoc,这是参数被误匹配的根本原因。
验证结果
修改后运行程序,只会输出返回类型的匹配结果:
beginLoc: /local/ferro/testestestestestestestestest/src/../api/test.cc:3:1 name: fun position: </local/ferro/testestestestestestestestest/src/../api/test.cc:3:1>
内容的提问来源于stack exchange,提问作者qfe
相关产品推荐
相关产品推荐

