TL; DR;
如何从callExpr-> arg_0-> DeclRefExpr中获取用于恒定大小数组声明的宏名称。
详细的问题说明:
最近,我开始应对一项挑战,该挑战需要使用源到源转换工具进行修改 带有附加参数的特定函数调用。对我可以达到的方法的研究介绍了我 这个惊人的工具集Clang。我一直在学习如何使用libtooling中提供的不同工具来 实现我的目标。但是现在我遇到了问题,请在这里寻求帮助。
考虑下面的程序(我的源代码的伪装),我的目标是重写所有对strcpy的调用 具有安全版本的strcpy_s的函数,并在新函数调用中添加其他参数 即-目标指针的最大大小。因此,对于以下程序,我的重构调用将是 strcpy_s(inStr,STR_MAX,argv [1]);
我编写了一个RecursiveVisitor类,并检查VisitCallExpr方法中的所有函数调用,以获取最大大小 我正在获取第一个聚合的VarDecl并试图获取大小(ConstArrayType)。以来 源文件已经过预处理,我看到大小为2049,但我需要的是STR_MAX中的宏 这个案例。我该怎么办? (使用此信息创建替换项,然后使用RefactoringTool替换它们)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STR_MAX 2049
int main(int argc, char **argv){
char inStr[STR_MAX];
if(argc>1){
//Clang tool required to transaform the below call into strncpy_s(inStr, STR_MAX, argv[1], strlen(argv[1]));
strcpy(inStr, argv[1]);
} else {
printf("\n not enough args");
return -1;
}
printf("got [%s]", inStr);
return 0;
}
答案 0 :(得分:0)
正如您正确注意到的那样,源代码已经过预处理,并且所有宏都已扩展。因此,AST将仅具有一个整数表达式作为数组的大小。
注意 :您可以跳过它,直接进入下面的解决方案
有关扩展宏的信息包含在AST节点的源位置中,通常可以使用 Lexer 进行检索(Clang的lexer和预处理器之间的联系非常紧密,甚至可以视为一个实体)。这只是最低要求,使用起来并不是很明显,但这就是事实。
当您正在寻找一种获取替代品的原始宏名称的方法时,您只需获取拼写(即其在原始源代码中的书写方式),而您无需不需要太多关于宏定义,函数式宏及其参数等的内容。
Clang具有两种类型的不同位置: SourceLocation 和 CharSourceLocation 。通过AST几乎可以在任何地方找到第一个。它是指根据令牌的头寸。这就解释了为什么 begin 和 end 位置可能有点违反直觉的原因:
// clang::DeclRefExpr
//
// ┌─ begin location
foo(VeryLongButDescriptiveVariableName);
// └─ end location
// clang::BinaryOperator
//
// ┌─ begin location
int Result = LHS + RHS;
// └─ end location
如您所见,这种类型的源位置指向相应令牌的开头。另一方面, CharSourceLocation 直接指向字符。
因此,为了获取表达式的原始文本,我们需要将 SourceLocation 转换为 CharSourceLocation 并从源中获取相应的文本。
我已经修改了您的示例以显示宏扩展的其他情况:
#define STR_MAX 2049
#define BAR(X) X
int main() {
char inStrDef[STR_MAX];
char inStrFunc[BAR(2049)];
char inStrFuncNested[BAR(BAR(STR_MAX))];
}
以下代码:
// clang::VarDecl *VD;
// clang::ASTContext *Context;
auto &SM = Context->getSourceManager();
auto &LO = Context->getLangOpts();
auto DeclarationType = VD->getTypeSourceInfo()->getTypeLoc();
if (auto ArrayType = DeclarationType.getAs<ConstantArrayTypeLoc>()) {
auto *Size = ArrayType.getSizeExpr();
auto CharRange = Lexer::getAsCharRange(Size->getSourceRange(), SM, LO);
// Lexer gets text for [start, end) and we want him to grab the end as well
CharRange.setEnd(CharRange.getEnd().getLocWithOffset(1));
auto StringRep = Lexer::getSourceText(CharRange, SM, LO);
llvm::errs() << StringRep << "\n";
}
生成此代码段的输出:
STR_MAX
BAR(2049)
BAR(BAR(STR_MAX))
我希望这些信息对您有所帮助。使用Clang进行快乐的黑客入侵!