Clang:如何获取用于常量大小数组声明大小的宏名称
Clang: How to get the macro name used for size of a constant size array declaration
TL;DR;
如何从 callExpr -> arg_0 -> DeclRefExpr 获取用于常量数组声明大小的宏名称。
详细问题陈述:
最近我开始应对一项挑战,需要源到源转换工具进行修改
带有附加参数的特定函数调用。研究我可以实现的方式介绍了我
到这个惊人的工具集 Clang。我一直在学习如何使用 libtooling 中提供的不同工具来
实现我的目标。但是现在我遇到了一个问题,在这里寻求你的帮助。
考虑下面的程序(我的来源的虚拟),我的目标是重写对 strcpy 的所有调用
使用安全版本 strcpy_s 的函数,并在新函数调用中添加一个附加参数
即 - 目标指针最大大小。所以,对于下面的程序,我重构的调用就像
strcpy_s(inStr, STR_MAX, argv[1]);
我写了一个 RecursiveVisitor class 并检查了 VisitCallExpr 方法中的所有函数调用,以获得最大大小
在 dest arg 中,我正在获取第一个 agrument 的 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;
}
如您所见,源代码已经过预处理,并且扩展了所有宏。因此,AST 将简单地将整数表达式作为数组的大小。
有关源位置的一些信息
注意:您可以跳过它并直接进行下面的解决方案
关于扩展宏的信息包含在AST节点的源位置,通常可以使用Lexer检索(Clang的词法分析器和预处理器联系非常紧密,甚至可以认为是一个实体)。它是最低限度的,使用起来不是很明显,但它就是这样。
当您正在寻找一种方法来获取替换的原始宏名称时,您只需要获取拼写(即它在原始源代码中的编写方式代码),你不需要携带太多关于宏定义、function-style 宏及其参数等的信息。
Clang 有两种不同的位置:SourceLocation 和 CharSourceLocation。通过 AST,第一个几乎随处可见。它指的是 tokens 的位置。这解释了为什么 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 愉快!
TL;DR;
如何从 callExpr -> arg_0 -> DeclRefExpr 获取用于常量数组声明大小的宏名称。
详细问题陈述:
最近我开始应对一项挑战,需要源到源转换工具进行修改 带有附加参数的特定函数调用。研究我可以实现的方式介绍了我 到这个惊人的工具集 Clang。我一直在学习如何使用 libtooling 中提供的不同工具来 实现我的目标。但是现在我遇到了一个问题,在这里寻求你的帮助。
考虑下面的程序(我的来源的虚拟),我的目标是重写对 strcpy 的所有调用 使用安全版本 strcpy_s 的函数,并在新函数调用中添加一个附加参数 即 - 目标指针最大大小。所以,对于下面的程序,我重构的调用就像 strcpy_s(inStr, STR_MAX, argv[1]);
我写了一个 RecursiveVisitor class 并检查了 VisitCallExpr 方法中的所有函数调用,以获得最大大小 在 dest arg 中,我正在获取第一个 agrument 的 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;
}
如您所见,源代码已经过预处理,并且扩展了所有宏。因此,AST 将简单地将整数表达式作为数组的大小。
有关源位置的一些信息
注意:您可以跳过它并直接进行下面的解决方案
关于扩展宏的信息包含在AST节点的源位置,通常可以使用Lexer检索(Clang的词法分析器和预处理器联系非常紧密,甚至可以认为是一个实体)。它是最低限度的,使用起来不是很明显,但它就是这样。
当您正在寻找一种方法来获取替换的原始宏名称时,您只需要获取拼写(即它在原始源代码中的编写方式代码),你不需要携带太多关于宏定义、function-style 宏及其参数等的信息。
Clang 有两种不同的位置:SourceLocation 和 CharSourceLocation。通过 AST,第一个几乎随处可见。它指的是 tokens 的位置。这解释了为什么 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 愉快!