ast_matchers::MatchFinder to CallExpr that's a macro call

With code like:

ast_matchers::MatchFinder Finder;

CallRenamer gmblkCallCallback(&Tool.getReplacements(), “myfunc”, “xxmyfunc”);
Finder.addMatcher(
callExpr( callee(functionDecl(hasName(“myfunc”)))).bind(“myfunc”),
&gmblkCallCallback);

I’m able to match to a function call, such as:

int myfunc( void ** ppBlkPtr ) ;

void blah(void)
{
int * r ;

myfunc( (void **)&r ) ;
}

(I’ve also got a working version of the replacement callback that renames such a call).

However, the declaration of “myfunc” is actually of the form:

int myfuncEx( void ** ppBlkPtr, int line ) ;

#define myfunc( x ) myfuncEx( (x), LINE )

Such a call generates a different sort of AST structure:

`-CallExpr 0x14963a0 <line:1468:21, col:45> ‘int’

-ImplicitCastExpr 0x1496388 col:21 ‘int (*)(void **, int)’
-DeclRefExpr 0x1496338 <col:21> 'int (void **, int)' lvalue Function 0x1495b00 'myfuncEx' 'int (void **, int)' -ParenExpr 0x14962f8 <col:31, col:33> 'void **' -CStyleCastExpr 0x14962d0 <line:1478:12, col:22> ‘void **’
-UnaryOperator 0x1496298 <col:21, col:22> 'int **' prefix '&' -DeclRefExpr 0x1496248 col:22 ‘int *’ lvalue Var 0x1496180 ‘r’ ‘int *’
`-IntegerLiteral 0x1496318 <:2:1> ‘int’ 1478

How can I match to such a “macro call” AST?

My final goal is to have my replacement callback to remove that (void **) cast from all such parameters for this macro, so a better (but harder) question is probably:

How do I match to the (void **) cast operation of parameter N of a macro call named “myfunc”?

It’s not clear to me that the AST even has this info (see how the AST dump has lost the original macro origins, and has info only about the myfuncEx function).

With code like:

ast_matchers::MatchFinder Finder;
  CallRenamer gmblkCallCallback(&Tool.getReplacements(), "myfunc",
"xxmyfunc");
  Finder.addMatcher(
        callExpr( callee(functionDecl(hasName("myfunc")))).bind("myfunc"),
    &gmblkCallCallback);

I'm able to match to a function call, such as:

int myfunc( void ** ppBlkPtr ) ;
void blah(void)
{
   int * r ;

   myfunc( (void **)&r ) ;
}

(I've also got a working version of the replacement callback that renames
such a call).

However, the declaration of "myfunc" is actually of the form:

int myfuncEx( void ** ppBlkPtr, int line ) ;

#define myfunc( x ) myfuncEx( (x), __LINE__ )

Such a call generates a different sort of AST structure:

    `-CallExpr 0x14963a0 <line:1468:21, col:45> 'int'
      >-ImplicitCastExpr 0x1496388 <col:21> 'int (*)(void **, int)'
<FunctionToPointerDecay>
      > `-DeclRefExpr 0x1496338 <col:21> 'int (void **, int)' lvalue
Function 0x1495b00 'myfuncEx' 'int (void **, int)'
      >-ParenExpr 0x14962f8 <col:31, col:33> 'void **'
      > `-CStyleCastExpr 0x14962d0 <line:1478:12, col:22> 'void **'
<BitCast>
      > `-UnaryOperator 0x1496298 <col:21, col:22> 'int **' prefix '&'
      > `-DeclRefExpr 0x1496248 <col:22> 'int *' lvalue Var 0x1496180
'r' 'int *'
      `-IntegerLiteral 0x1496318 <<scratch space>:2:1> 'int' 1478

How can I match to such a "macro call" AST?

My final goal is to have my replacement callback to remove that (void **)
cast from all such parameters for this macro, so a better (but harder)
question is probably:

How do I match to the (void **) cast operation of parameter N of a macro
call named "myfunc"?

It's not clear to me that the AST even has this info (see how the AST dump
has lost the original macro origins, and has info only about the myfuncEx
function).

Note that the AST dump does not contain all the information there is (or it
would be completely unreadable).

With AST matchers, you can currently only match the "C++" names of things.
After matching the call, you can look at the expansion location of the
identifier that references the function (get it via the SourceManager) or
try to directly use the Lexer's makeFileCharRange.

Cheers,
/Manuel

To simplify the problem, I'll hack the code in this case to use a fake
prototype, one that looks like the macro interface. Then the problem
becomes manipulating the parameters.

I see in the match test code stuff like:

  const char Program =
      "struct T { };"
      "int f(int, T*, int, int);"
      "void g(int x) { T t; f(x, &t, 3, 4); }";
...
  EXPECT_TRUE(matches(Program,
      callExpr(allOf(callee(functionDecl(hasName("f"))),
                     hasArgument(0, declRefExpr(to(varDecl()))),
                     hasArgument(1, hasType(pointsTo(
                                        recordDecl(hasName("T"))))),
                     hasArgument(2, integerLiteral(equals(3))),
                     hasArgument(3, integerLiteral(equals(4)))))));

How would I bind the matcher to one of these parameters?

Note that the AST dump does not contain all the information there is (or
it would be completely unreadable).

Is there any way to do a very verbose dump rooted at a specific

expression?

Nope. Patches welcome, I guess :slight_smile:

With AST matchers, you can currently only match the "C++" names of
things. After matching the call, you can look at the expansion location of
the identifier that references the function (get it via the SourceManager)
or try to directly use the Lexer's makeFileCharRange.

To simplify the problem, I'll hack the code in this case to use a fake
prototype, one that looks like the macro interface. Then the problem
becomes manipulating the parameters.

I see in the match test code stuff like:

  const char Program =
      "struct T { };"
      "int f(int, T*, int, int);"
      "void g(int x) { T t; f(x, &t, 3, 4); }";
...
  EXPECT_TRUE(matches(Program,
      callExpr(allOf(callee(functionDecl(hasName("f"))),
                     hasArgument(0, declRefExpr(to(varDecl()))),
                     hasArgument(1, hasType(pointsTo(
                                        recordDecl(hasName("T"))))),
                     hasArgument(2, integerLiteral(equals(3))),
                     hasArgument(3, integerLiteral(equals(4)))))));

How would I bind the matcher to one of these parameters?

integerLiteral(equals(3)).bind("x"); same for declRefExpr. The only thing
you cannot bind here is "hasType", but I'm surprised this compiles at
all... stmt(hasType(pointsTo(...))).bind("x") should work...

Cheers,
/Manuel

I've tried:

  Finder.addMatcher(
        callExpr( allOf(
              callee(functionDecl(hasName("myfunc"))),
              hasArgument(1,
                 stmt(hasType(
                    pointsTo(
                       pointsTo( recordDecl(hasName("void")) )
                       )
                    ))
                    .bind("x")
                 )
           )),
    &gmblkCallBack);

This doesn't compile, and gives:

error: no member named 'getType' in 'clang::Stmt'
  return InnerMatcher.matches(Node.getType(), Finder, Builder);

Is there a different way (or better approach to getting at the parameter
data)?

integerLiteral(equals(3)).bind("x"); same for declRefExpr. The only thing
you cannot bind here is "hasType", but I'm surprised this compiles at
all... stmt(hasType(pointsTo(...))).bind("x") should work...

I've tried:

  Finder.addMatcher(
        callExpr( allOf(
              callee(functionDecl(hasName("myfunc"))),
              hasArgument(1,
                 stmt(hasType(
                    pointsTo(
                       pointsTo( recordDecl(hasName("void")) )
                       )
                    ))
                    .bind("x")
                 )
           )),
    &gmblkCallBack);

This doesn't compile, and gives:

error: no member named 'getType' in 'clang::Stmt'
  return InnerMatcher.matches(Node.getType(), Finder, Builder);

Is there a different way (or better approach to getting at the parameter
data)?

Sorry, of course expr(hasType(...)).bind(...) :slight_smile:

Okay thanks, that compiles now. However, it appears that my attempt to
match to the void ** itself is not working. Even if I take out the attempt
to bind to the parameter and just bind to the whole call statement, it
doesn't trigger. Example:

  Finder.addMatcher(
        callExpr( callee(functionDecl(hasName("myfunc"))) ).bind("x"),
    &gmblkCallBack);

triggers the callback:

match callback
<s>myfunc( &r )</s>
match callback
<s>myfunc( (void **)&r )</s>

where the source was:

int myfunc( void ** ppBlkPtr ) ;

void blah(void)
{
   //int * r ;
   void * r ;

   myfunc( &r ) ;
   myfunc( (void **)&r ) ;
}

But this matcher doesn't trigger:

  Finder.addMatcher(
        callExpr( allOf(
              callee(functionDecl(hasName("myfunc"))),
              hasArgument(0,
// expr(
                    hasType(
                    pointsTo(
                       pointsTo( recordDecl(hasName("void")) )
                       )
                    )
// ).bind("x")
                 )
           ))
                       .bind("x")

void cannot be matched with recordDecl - that's where your match fails. I
haven't played around with void pointers yet, but I'd assume you'll want
something with builtinType(asString("void")) or something similar...

Cheers,
/Manuel