Reputation: 23
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
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