铛:如何获取用于恒定大小数组声明的大小的宏名

时间:2019-06-09 04:47:59

标签: clang clang++ llvm-clang clang-static-analyzer

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;
}

1 个答案:

答案 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进行快乐的黑客入侵!