在条件

时间:2017-06-29 00:29:36

标签: clang llvm abstract-syntax-tree llvm-clang

我试图检测if语句中是否存在函数调用作为条件的一部分;如下:

if (cmp(a, b)){
  \\do something
}
I have found I could do this with AST matcher in following manner:
Matcher.addMatcher(ifStmt(hasCondition(callExpr().bind("call_expr")))
                           .bind("call_if_stmt"),&handleMatch);

但问题是条件可能有像&&,||这样的快捷方式;如下:

if(a != b && cmp(a,b) || c == 10){
\\ do something
}

现在这个条件有二元操作符&&和||;也有一个调用表达式作为它的一部分。现在我怎么能检测到这个if语句中有一个调用表达式?当然,我不知道有多少二元运算符作为快捷方式,所以我正在寻找一个通用的解决方案,可能使用clange AST匹配器。

1 个答案:

答案 0 :(得分:1)

在第一种情况下,if(cmp(a,b)),CallExpr节点是IfStmt的直接子节点。在第二种情况下,它是IfStmt的后代,但不是子级。相反,它嵌套在两个BinaryOperator节点下。 (我通过使用clang-check -ast-dump test.cpp --查看AST发现了这一点。)添加hasDescendant遍历匹配器将找到更深层嵌套的CallExpr。不幸的是,仅凭这一点就找不到第一例。因此,我们可以使用anyOf将其与原始匹配器结合使用:

ifStmt( 
  hasCondition( 
    anyOf(
      callExpr().bind("top_level_call_expr"),
      hasDescendant(
        callExpr().bind("nested_call_expr")
      )
    )
  )
).bind("call_if_stmt")

如果我使用test.cpp来获得以下代码:

bool cmp(int a, int b){return a < b;}

int f(int a, int c){
  int b = 42;
  if( a != b && cmp(a,b) || c == 10){
    return 2;
  }
  return c;
}

int g(int a, int c){
  int b = 42;
  if( cmp(a,b)) {
    return 2;
  }
  return c;
}

然后我可以使用clang-query test.cpp --测试:

clang-query> let m2 ifStmt( hasCondition( anyOf(callExpr().bind("top_level_call_expr"),hasDescendant(callExpr().bind("nested_call_expr"))))).bind("call_if_stmt")
clang-query> m m2

Match #1:

/path/to/test.xpp:5:7: note: "call_if_stmt" binds here
      if( a != b && cmp(a,b) || c == 10){
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/path/to/test.cpp:5:21: note: "nested_call_expr" binds here
      if( a != b && cmp(a,b) || c == 10){
                    ^~~~~~~~
/path/to/test.cpp:5:7: note: "root" binds here
      if( a != b && cmp(a,b) || c == 10){
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Match #2:

/path/to/test.cpp:13:7: note: "call_if_stmt" binds here
      if( cmp(a,b)) {
      ^~~~~~~~~~~~~~~
/path/to/test.cpp:13:7: note: "root" binds here
      if( cmp(a,b)) {
      ^~~~~~~~~~~~~~~
/path/to/test.cpp:13:11: note: "top_level_call_expr" binds here
      if( cmp(a,b)) {
          ^~~~~~~~
2 matches.