Kian
Kian

Reputation: 23

How do you match a C++ template function call using Clang AST matchers?

I am working on a small project where I am trying to implement a refactoring tool using Clang's LibTooling. As part of this project, I need to find calls to a specific function from a specific library.

My attempts at using clang-query to come up with the AST matchers have failed. However, I have discovered the following:

I have written the following test program to be able to post it here as a self-sufficient example, without depending on the library that I am working with:

template <typename T>
int func1(T param) {
    return 4;
}

int main() {
    int value = 4;
    int result = func1(value);
    return 0;
}

In order to observe the generated AST by Clang, I compiled the above program by using:

clang -Xclang -ast-dump -fsyntax-only test.cpp

The following is the generated AST:

TranslationUnitDecl 0xad9088 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0xad9960 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0xad9620 '__int128'
|-TypedefDecl 0xad99d0 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0xad9640 'unsigned __int128'
|-TypedefDecl 0xad9d48 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'
| `-RecordType 0xad9ac0 '__NSConstantString_tag'
|   `-CXXRecord 0xad9a28 '__NSConstantString_tag'
|-TypedefDecl 0xad9de0 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0xad9da0 'char *'
|   `-BuiltinType 0xad9120 'char'
|-TypedefDecl 0xb16e98 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'
| `-ConstantArrayType 0xb16e40 '__va_list_tag [1]' 1 
|   `-RecordType 0xad9ed0 '__va_list_tag'
|     `-CXXRecord 0xad9e38 '__va_list_tag'
|-FunctionTemplateDecl 0xb17160 <test.cpp:1:1, line:4:1> line:2:5 func1
| |-TemplateTypeParmDecl 0xb16ef0 <line:1:11, col:20> col:20 referenced typename depth 0 index 0 T
| |-FunctionDecl 0xb170c0 <line:2:1, line:4:1> line:2:5 func1 'int (T)'
| | |-ParmVarDecl 0xb16fc8 <col:11, col:13> col:13 param 'T'
| | `-CompoundStmt 0xb17238 <col:20, line:4:1>
| |   `-ReturnStmt 0xb17228 <line:3:5, col:12>
| |     `-IntegerLiteral 0xb17208 <col:12> 'int' 4
| `-FunctionDecl 0xb17700 <line:2:1, line:4:1> line:2:5 used func1 'int (int)'
|   |-TemplateArgument type 'int'
|   |-ParmVarDecl 0xb17608 <col:11, col:13> col:13 param 'int':'int'
|   `-CompoundStmt 0xb17960 <col:20, line:4:1>
|     `-ReturnStmt 0xb17950 <line:3:5, col:12>
|       `-IntegerLiteral 0xb17208 <col:12> 'int' 4
`-FunctionDecl 0xb172a0 <line:6:1, line:10:1> line:6:5 main 'int ()'
  `-CompoundStmt 0xb17928 <col:12, line:10:1>
    |-DeclStmt 0xb17408 <line:7:5, col:18>
    | `-VarDecl 0xb17380 <col:5, col:17> col:9 used value 'int' cinit
    |   `-IntegerLiteral 0xb173e8 <col:17> 'int' 4
    |-DeclStmt 0xb178e0 <line:8:5, col:30>
    | `-VarDecl 0xb17438 <col:5, col:29> col:9 result 'int' cinit
    |   `-CallExpr 0xb178a0 <col:18, col:29> 'int'
    |     |-ImplicitCastExpr 0xb17888 <col:18> 'int (*)(int)' <FunctionToPointerDecay>
    |     | `-DeclRefExpr 0xb17800 <col:18> 'int (int)' lvalue Function 0xb17700 'func1' 'int (int)' (FunctionTemplate 0xb17160 'func1')
    |     `-ImplicitCastExpr 0xb178c8 <col:24> 'int' <LValueToRValue>
    |       `-DeclRefExpr 0xb174e8 <col:24> 'int' lvalue Var 0xb17380 'value' 'int'
    `-ReturnStmt 0xb17918 <line:9:5, col:12>
      `-IntegerLiteral 0xb178f8 <col:12> 'int' 0

Now, based on what I could find from Introduction to the Clang AST and AST Matcher Reference, I came up with the following two AST matchers and tested in clang-query:

clang-query> match callExpr(callee(functionTemplateDecl(hasName("func1"))))
0 matches.
clang-query> match callExpr(hasDeclaration(functionTemplateDecl(hasName("func1"))))
0 matches.

They both return 0 matches. The same queries work if I change func1 so that it is not a template function, and if I change functionTemplateDecl to functionDecl.

Any ideas what's going on?

Upvotes: 2

Views: 1316

Answers (1)

Booo
Booo

Reputation: 503

if you use functionDecl in your matcher, it should work on your template code as well.

1 template <typename T>                                                                                                                                               
2 int func1(T param) {                                                                                                                                                                                
3     return 4;                                                                                                                                                       
4 }                                                                                                                                                                   
5                                                                                                                                                                     
6 int func1(int param1, int param2) {                                                                                                                                                                 
7     return 5;                                                                                                                                                       
8 }                                                                                                                                                                   
9                                                                                                                                                                     
10 int main() {                                                                                                                                                        
11     int value = 4;                                                                                                                                                  
12     int result = func1(value);                                                                                                                                                                      
13     int result2 = func1(value, value);                                                                                                                                                              
14     return 0;                                                                                                                                                       
15 }   
clang-query> match callExpr(callee(functionDecl(hasName("func1"))))

Match #1:

/.../test.cpp:12:18: note: "root" binds here
    int result = func1(value);
                 ^~~~~~~~~~~~

Match #2:

/.../test.cpp:13:19: note: "root" binds here
    int result2 = func1(value, value);
                  ^~~~~~~~~~~~~~~~~~~
2 matches.

if you want to go for the template instantiations, add a Narrowing matcher:

clang-query>  match callExpr(callee(functionDecl(hasName("func1"), isTemplateInstantiation())))

Match #1:

/.../test.cpp:12:18: note: "root" binds here
    int result = func1(value);
                 ^~~~~~~~~~~~
1 match.

Upvotes: 1

Related Questions