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 有两种不同的位置:SourceLocationCharSourceLocation。通过 AST,第一个几乎随处可见。它指的是 tokens 的位置。这解释了为什么 beginend 位置可能有些违反直觉:

// 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 愉快!