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

时间:2019-06-20 18:35:38

标签: clang llvm abstract-syntax-tree

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

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

我需要妥善处理声明参数的宏之类的东西(忽略它们)。

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

我查看了预处理器记录对象,并使用Lexer :: getSourceText获取了宏名称​​ In Out 等,但是看不到干净的宏。将它们映射回功能参数的方法。

我当前的解决方案是将所有宏扩展记录在文件中,然后将其SourceLocation与ParamVarDecl SourceLocation进行比较。除我不知道如何跳过参数后的内容外,这通常可以工作。

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

获取逗号的SourceLocation可以,但是我在任何地方都找不到。

1 个答案:

答案 0 :(得分:1)

问题的标题要求输入libclang,但是当您使用Lexer::getSourceText时,我假设它是libTooling。我剩下的答案只有在libTooling上才可行。

解决方案1 ​​

Lexer在令牌级别上工作。 逗号也是一个令牌,因此您可以使用参数的结束位置并使用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

这是一种非常低级且容易出错的方法。但是,您可以更改解决原始问题的方式。

解决方案2

Clang在其源位置存储有关扩展宏的信息。您可以在SourceManager中找到相关方法(例如isMacroArgExpansionisMacroBodyExpansion)。结果,您可以访问ParmVarDecl节点并检查其位置以进行宏扩展。

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

我希望这些信息会有所帮助。使用Clang进行快乐的黑客入侵!

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