Clang 工具,用于提取给定 lambda 类型的 lambda 主体

Clang Tool that extracts the lambda body given the lambda type

本文关键字:lambda 类型 主体 工具 用于 提取 Clang      更新时间:2023-10-16

我目前正在尝试实现一个基于RecursiveASTVisitors(基于本教程(的Clang工具,该工具基于给定函数的lambda应用代码转换。

例如,根据作为foo参数给出的lambda生成一些东西:

foo([](){});

这很容易实现。查找名称为foo的所有callExpr,然后查找作为此callExpr后代的所有lambdaExprs:

struct LambdaExprVisitor : public RecursiveASTVisitor<LambdaExprVisitor> {
bool VisitLambdaExpr(LambdaExpr * lambdaExpr) {
//Do stuff
lambdaExpr->getCallOperator()->getBody()->dump();
return true;
}
};
struct CallVisitor : public RecursiveASTVisitor<CallVisitor> {
//Find call expressions based on the given name
bool VisitCallExpr(CallExpr * expr) {
auto * callee = expr->getDirectCallee(); 
if(callee != nullptr && callee->getName() == "foo") {
visitor.TraverseCallExpr(expr);
}
return true;
}
LambdaExprVisitor visitor;
};

我现在遇到的问题是,有多种方法可以将lambda函数传递给这个原始函数foo,例如:

auto bar() { return [](){}; }
int main() {
foo(bar());
}

而早期获得身体的方法在这里不起作用。

现在我认为 lambda 的主体在编译时是已知的,因此给定参数的Expr值,lambda 主体必须以某种方式推断:

struct CallVisitor : public RecursiveASTVisitor<CallVisitor> {
bool VisitCallExpr(CallExpr * expr) {
auto * callee = expr->getDirectCallee(); 
if(callee != nullptr && callee->getName() == "foo") {
//Get the first argument which must be the lambda
auto * arg = expr->getArg(0);
//do something with the the first argument
//?
};
return true;
}
};

此时有没有办法获得 lambda 正文? 如果没有,有没有办法以不同的方式推断 lambda 体,而不必诉诸实现所有可能的方法将 lambda 体传递给foo

注意:基于匹配器的解决方案也适合我。

解决方案是首先遍历翻译单元,并将所有lambdaExpr收集到一个类型为键的地图中。然后,在翻译单元的第二次遍历中,可以通过类型推断 lambda 的主体。 以下是修改后的访问者,它们现在存储对此地图的引用(该类型编码为其字符串表示形式(:

struct LambdaExprVisitor : public RecursiveASTVisitor<LambdaExprVisitor> {
LambdaExprVisitor(std::map<std::string, LambdaExpr *>& id2Expr) : RecursiveASTVisitor<LambdaExprVisitor> {}, id2Expr { id2Expr } {} 
std::map<std::string, LambdaExpr *>& id2Expr; 
bool VisitLambdaExpr(LambdaExpr * lambdaExpr) {
id2Expr.emplace(
lambdaExpr->getType().getAsString(),
lambdaExpr
);
return true;
}
};
struct CallVisitor : public RecursiveASTVisitor<CallVisitor> {
CallVisitor(std::map<std::string, LambdaExpr *>& id2Expr) : RecursiveASTVisitor<CallVisitor> {}, id2Expr { id2Expr } {} 
std::map<std::string, LambdaExpr *>& id2Expr;
bool VisitCallExpr(CallExpr * expr) {
auto * callee = expr->getDirectCallee(); 
if(callee != nullptr && callee->getName() == "foo") {
//Get the expr from the map
auto arg = expr->getArg(0)->getType().getAsString();
if(auto iter = id2Expr.find(arg); iter != id2Expr.end()) {
//Do stuff with the lambdaExpr
auto * lambdaExpr = iter->second;
lambdaExpr->dump();
}
};
return true;
}
};

处理此问题ASTConsumer只是存储此地图并执行两个访问者:

struct Consumer : public ASTConsumer {
public: 
virtual void HandleTranslationUnit(clang::ASTContext &Context) override {
visitor.TraverseDecl(Context.getTranslationUnitDecl());
visitor2.TraverseDecl(Context.getTranslationUnitDecl());
}
std::map<std::string, LambdaExpr *> id2Expr; 
LambdaExprVisitor visitor { id2Expr };
CallVisitor visitor2 { id2Expr };
};