Clang AST Matchers:如何从函数声明中找到函数体?
Clang AST Matchers: how to find function body from a function declaration?
我试图编写一个简单的 clang-tidy 检查器,它将检查多次调用 fopen()
的构造函数。我的目的是找到潜在的内存泄漏,以防在第二次 fopen()
调用中发生任何异常。
class Dummy_file
{
FILE *f1_;
FILE *f2_;
public:
Dummy_file(const char* f1_name, const char* f2_name, const char * mode){
f1_ = fopen(f1_name, mode);
f2_ = fopen(f2_name, mode);
}
~Dummy_file(){
fclose(f1_);
fclose(f2_);
}
};
使用这个
callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencalls")
能够找到所有 fopen()
调用。
但是我找不到cxxConstructorDecl
使用这个。
cxxConstructorDecl(has(callExpr(callee(functionDecl(hasName("fopen")))))).bind("ctr")
我很怀疑,因为我正在使用 cxxConstructorDecl
我的过滤器没有应用于构造函数主体。
那么如何从函数声明中找到函数体呢?
简短说明
您应该使用 hasDescendant
匹配器而不是 has
匹配器。虽然 has
只检查被测试节点的 直接 子节点是否匹配,但 hasDescendant
匹配 any 后代。
在这里你可以看到你的例子:
|-CXXConstructorDecl <line:8:3, line:11:3> line:8:3 Dummy_file 'void (const char *, const char *, const char *)'
| |-ParmVarDecl <col:14, col:26> col:26 used f1_name 'const char *'
| |-ParmVarDecl <col:35, col:47> col:47 used f2_name 'const char *'
| |-ParmVarDecl <col:56, col:68> col:68 used mode 'const char *'
| `-CompoundStmt <col:74, line:11:3>
| |-BinaryOperator <line:9:5, col:30> 'FILE *' lvalue '='
| | |-MemberExpr <col:5> 'FILE *' lvalue ->f1_ 0x55d36491a230
| | | `-CXXThisExpr <col:5> 'Dummy_file *' this
| | `-CallExpr <col:11, col:30> 'FILE *'
| | |-ImplicitCastExpr <col:11> 'FILE *(*)(const char *__restrict, const char *__restrict)' <FunctionToPointerDecay>
| | | `-DeclRefExpr <col:11> 'FILE *(const char *__restrict, const char *__restrict)' lvalue Function 0x55d3648fa220 'fopen' 'FILE *(const char *__restrict, const char *__restrict)'
| | |-ImplicitCastExpr <col:17> 'const char *' <LValueToRValue>
| | | `-DeclRefExpr <col:17> 'const char *' lvalue ParmVar 0x55d36491a310 'f1_name' 'const char *'
| | `-ImplicitCastExpr <col:26> 'const char *' <LValueToRValue>
| | `-DeclRefExpr <col:26> 'const char *' lvalue ParmVar 0x55d36491a400 'mode' 'const char *'
CallExpr
不是 CXXConstructorDecl
的子代,而是 BinaryOperator
.
的子代
解决方案
下面我完成了你的匹配器并在 clang-query
中检查了它。
clang-query> match cxxConstructorDecl(hasDescendant(callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencall"))).bind("ctr")
Match #1:
$TEST_DIR/test.cpp:8:3: note: "ctr" binds here
Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:9:11: note: "fopencall" binds here
f1_ = fopen(f1_name, mode);
^~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:8:3: note: "root" binds here
Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.
我希望这能回答您的问题!
我试图编写一个简单的 clang-tidy 检查器,它将检查多次调用 fopen()
的构造函数。我的目的是找到潜在的内存泄漏,以防在第二次 fopen()
调用中发生任何异常。
class Dummy_file
{
FILE *f1_;
FILE *f2_;
public:
Dummy_file(const char* f1_name, const char* f2_name, const char * mode){
f1_ = fopen(f1_name, mode);
f2_ = fopen(f2_name, mode);
}
~Dummy_file(){
fclose(f1_);
fclose(f2_);
}
};
使用这个
callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencalls")
能够找到所有 fopen()
调用。
但是我找不到cxxConstructorDecl
使用这个。
cxxConstructorDecl(has(callExpr(callee(functionDecl(hasName("fopen")))))).bind("ctr")
我很怀疑,因为我正在使用 cxxConstructorDecl
我的过滤器没有应用于构造函数主体。
那么如何从函数声明中找到函数体呢?
简短说明
您应该使用 hasDescendant
匹配器而不是 has
匹配器。虽然 has
只检查被测试节点的 直接 子节点是否匹配,但 hasDescendant
匹配 any 后代。
在这里你可以看到你的例子:
|-CXXConstructorDecl <line:8:3, line:11:3> line:8:3 Dummy_file 'void (const char *, const char *, const char *)'
| |-ParmVarDecl <col:14, col:26> col:26 used f1_name 'const char *'
| |-ParmVarDecl <col:35, col:47> col:47 used f2_name 'const char *'
| |-ParmVarDecl <col:56, col:68> col:68 used mode 'const char *'
| `-CompoundStmt <col:74, line:11:3>
| |-BinaryOperator <line:9:5, col:30> 'FILE *' lvalue '='
| | |-MemberExpr <col:5> 'FILE *' lvalue ->f1_ 0x55d36491a230
| | | `-CXXThisExpr <col:5> 'Dummy_file *' this
| | `-CallExpr <col:11, col:30> 'FILE *'
| | |-ImplicitCastExpr <col:11> 'FILE *(*)(const char *__restrict, const char *__restrict)' <FunctionToPointerDecay>
| | | `-DeclRefExpr <col:11> 'FILE *(const char *__restrict, const char *__restrict)' lvalue Function 0x55d3648fa220 'fopen' 'FILE *(const char *__restrict, const char *__restrict)'
| | |-ImplicitCastExpr <col:17> 'const char *' <LValueToRValue>
| | | `-DeclRefExpr <col:17> 'const char *' lvalue ParmVar 0x55d36491a310 'f1_name' 'const char *'
| | `-ImplicitCastExpr <col:26> 'const char *' <LValueToRValue>
| | `-DeclRefExpr <col:26> 'const char *' lvalue ParmVar 0x55d36491a400 'mode' 'const char *'
CallExpr
不是 CXXConstructorDecl
的子代,而是 BinaryOperator
.
解决方案
下面我完成了你的匹配器并在 clang-query
中检查了它。
clang-query> match cxxConstructorDecl(hasDescendant(callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencall"))).bind("ctr")
Match #1:
$TEST_DIR/test.cpp:8:3: note: "ctr" binds here
Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:9:11: note: "fopencall" binds here
f1_ = fopen(f1_name, mode);
^~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:8:3: note: "root" binds here
Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.
我希望这能回答您的问题!