如何使用 libtooling 找到函数参数之间逗号的 SourceLocation?

How do I find the SourceLocation of the commas between function arguments using libtooling?

我的主要目标是尝试在函数参数之前获取宏(甚至只是文本)。例如:

void Foo(_In_ void* p, _Out_ int* x, _Out_cap_(2) int* y);

我需要优雅地处理声明参数的宏之类的事情(通过忽略它们)。

#define Example _In_ int x void Foo(Example);

我查看了预处理器记录对象并使用 Lexer::getSourceText 获取宏名称 InOut 等,但我没有看到将它们映射回函数参数的干净方法。

我目前的解决方案是记录文件中的所有宏展开,然后将它们的SourceLocation 与ParamVarDecl SourceLocation 进行比较。这主要是有效的,除了我不知道如何跳过参数后的东西。

void Foo(_In_ void* p _Other_, _In_ int y);

获取逗号的 SourceLocation 会起作用,但我无法在任何地方找到它。

问题的标题要求 libclang,但当您使用 Lexer::getSourceText 时,我假设它是 libTooling。我的其余答案仅在 libTooling.

方面可行

解决方案 1

词法分析器在标记级别上工作。 逗号 也是一个标记,因此您可以获取参数的结束位置并使用 Lexer::findNextToken.

获取下一个标记

这里是ParmVarDecl(对于函数参数)和CallExpr(对于函数参数)访问函数,展示了如何使用它:

template <class T> void printNextTokenLocation(T *Node) {
  auto NodeEndLocation = Node->getSourceRange().getEnd();

  auto &SM = Context->getSourceManager();
  auto &LO = Context->getLangOpts();

  auto NextToken = Lexer::findNextToken(NodeEndLocation, SM, LO);
  if (!NextToken) {
    return;
  }
  auto NextTokenLocation = NextToken->getLocation();

  llvm::errs() << NextTokenLocation.printToString(SM) << "\n";
}

bool VisitParmVarDecl(ParmVarDecl *Param) {
  printNextTokenLocation(Param);
  return true;
}

bool VisitCallExpr(CallExpr *Call) {
  for (auto *Arg : Call->arguments()) {
    printNextTokenLocation(Arg);
  }
  return true;
}

对于以下代码片段:

#define FOO(x) int x
#define BAR float d
#define MINUS -
#define BLANK

void foo(int a, double b     ,
         FOO(c)   , BAR) {}

int main() {
  foo(   42     ,
      36.6 ,   MINUS 10    ,   BLANK 0.0  );
  return 0;
}

它产生以下输出(逗号的六个位置和括号的两个位置):

test.cpp:6:15
test.cpp:6:30
test.cpp:7:19
test.cpp:7:24
test.cpp:10:17
test.cpp:11:12
test.cpp:11:28
test.cpp:11:43

不过,这是一种相当 low-level 和 error-prone 的方法。但是,您可以更改解决原始问题的方法。

解决方案 2

Clang 在其源位置存储有关扩展宏的信息。您可以在 SourceManager 中找到相关方法(例如 isMacroArgExpansion or isMacroBodyExpansion)。因此,您可以访问 ParmVarDecl 个节点并检查它们的位置以进行宏扩展。

我强烈建议朝第二个方向前进。

希望这些信息对您有所帮助。祝您使用 Clang 愉快!

UPD 说到属性,不幸的是,您没有太多选择。 Clang 会忽略 任何 未知属性,并且此行为 不可调整 。如果您不想修补 Clang 本身并将您的属性添加到 Attrs.td,那么您确实仅限于令牌和第一种方法。