You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:54:54