如何使用 Clang AST 匹配器匹配 C++ 模板函数调用?
How do you match a C++ template function call using Clang AST matchers?
我正在做一个小项目,我正在尝试使用 Clang 的 LibTooling 实现一个重构工具。作为该项目的一部分,我需要从特定库中查找对特定函数的调用。
我尝试使用 clang-query 来提供 AST 匹配器的尝试失败了。
但是,我发现了以下内容:
- 我可以使用我的 AST 匹配器成功找到对非模板函数的调用。
- 类似的 AST 匹配器查询无法找到对模板函数的调用。
我已经编写了以下测试程序,以便能够 post 这里作为一个自给自足的示例,而不依赖于我正在使用的库:
template <typename T>
int func1(T param) {
return 4;
}
int main() {
int value = 4;
int result = func1(value);
return 0;
}
为了观察Clang生成的AST,我编译了上面的程序,使用:
clang -Xclang -ast-dump -fsyntax-only test.cpp
生成的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
现在,根据我从 Introduction to the Clang AST and AST Matcher Reference 中找到的内容,我提出了以下两个 AST 匹配器并在 clang-query 中进行了测试:
clang-query> match callExpr(callee(functionTemplateDecl(hasName("func1"))))
0 matches.
clang-query> match callExpr(hasDeclaration(functionTemplateDecl(hasName("func1"))))
0 matches.
他们都 return 0 场比赛。如果我更改 func1 使其不是模板函数,并且如果我将 functionTemplateDecl 更改为 functionDecl,则相同的查询会起作用。
有什么想法吗?
如果您在匹配器中使用 functionDecl
,它也应该适用于您的模板代码。
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.
如果您想使用模板实例化,请添加一个 Narrowing 匹配器:
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.
我正在做一个小项目,我正在尝试使用 Clang 的 LibTooling 实现一个重构工具。作为该项目的一部分,我需要从特定库中查找对特定函数的调用。
我尝试使用 clang-query 来提供 AST 匹配器的尝试失败了。 但是,我发现了以下内容:
- 我可以使用我的 AST 匹配器成功找到对非模板函数的调用。
- 类似的 AST 匹配器查询无法找到对模板函数的调用。
我已经编写了以下测试程序,以便能够 post 这里作为一个自给自足的示例,而不依赖于我正在使用的库:
template <typename T>
int func1(T param) {
return 4;
}
int main() {
int value = 4;
int result = func1(value);
return 0;
}
为了观察Clang生成的AST,我编译了上面的程序,使用:
clang -Xclang -ast-dump -fsyntax-only test.cpp
生成的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
现在,根据我从 Introduction to the Clang AST and AST Matcher Reference 中找到的内容,我提出了以下两个 AST 匹配器并在 clang-query 中进行了测试:
clang-query> match callExpr(callee(functionTemplateDecl(hasName("func1"))))
0 matches.
clang-query> match callExpr(hasDeclaration(functionTemplateDecl(hasName("func1"))))
0 matches.
他们都 return 0 场比赛。如果我更改 func1 使其不是模板函数,并且如果我将 functionTemplateDecl 更改为 functionDecl,则相同的查询会起作用。
有什么想法吗?
如果您在匹配器中使用 functionDecl
,它也应该适用于您的模板代码。
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.
如果您想使用模板实例化,请添加一个 Narrowing 匹配器:
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.