Clang IfStmt with shortcut binary operator in cond

2019-08-17 02:50发布

I am trying to detect if there is a function call inside an if statement as part of condition; like following:

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

But the problem is condition could have shortcuts like &&, ||; like following:

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

Now this condition has binaryoperator && and ||; also have a call expression as part of it. Now how I could detect that there is a call expression inside this if statement? Definitely I don't know how many binary operator as shortcuts will be there, so I am looking for a generalize solution for this, possibly using clange AST matcher.

1条回答
劳资没心,怎么记你
2楼-- · 2019-08-17 03:06

In the first case, if(cmp(a,b)), the CallExpr node is a direct child of the IfStmt. In the second case, it is a descendant of the IfStmt, but not a child. Instead, it is nested beneath two BinaryOperator nodes. (I found this out by looking at the AST with clang-check -ast-dump test.cpp --.) Adding a hasDescendant traversal matcher will find the more deeply nested CallExpr. Unfortunately, that alone will not find the first case. So we could use anyOf to combine it with the original matcher:

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

If I take test.cpp to have the following code:

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

then I can test this with 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.
查看更多
登录 后发表回答