如何使用 Clang AST 匹配器匹配 C++ 模板函数调用?

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

我正在做一个小项目,我正在尝试使用 Clang 的 LibTooling 实现一个重构工具。作为该项目的一部分,我需要从特定库中查找对特定函数的调用。

我尝试使用 clang-query 来提供 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.