使用 ASTMatcher 获取所有 "malloc" 个调用
Get all "malloc" calls using ASTMatcher
我试图在 clang 中使用 ASTMatcher
获取所有 malloc
调用。这是代码示例:
Finder.addMatcher(
callExpr(
hasParent(binaryOperator(
hasOperatorName("=")).bind("assignment")),
declRefExpr(to(functionDecl(hasName("malloc"))))).bind("functionCall"),
&HandlerForFunctionCall);
编译正常。但我仍然无法接到任何 malloc
电话。如何使用 clang ASTMatcher 获取所有 malloc
调用?
问题
malloc
函数的签名定义如下:
void* malloc (size_t size);
为了将 malloc
的 return 值分配给 void*
以外的任何类型的指针,您必须对其进行强制转换。
虽然 C++ 要求您显式转换,但 C 编译器会隐式为您执行此操作。所以即使你写
int *a = malloc(sizeof(*a));
编译器将隐式转换 RHS 表达式。那相当于
int *a = (int*) malloc(sizeof(*a));
您正在使用 hasParent
缩小匹配器,它只匹配 直接 parents 而不是任何祖先 .
因此,您的匹配器将只匹配没有任何类型转换的分配。
您的 declRefExpr
也发生了几乎相同的事情。
C 标准说函数会自动衰减到 pointers-to-functions。
Clang 隐式地将 malloc
转换为 void *(*)(size_t)
,这会破坏匹配器的层次结构。
可能的解决方案
好吧,这取决于您实际想要做什么。 首先,您通常可以使用以下代码段修复部分 selecting malloc 函数:
callExpr(callee(functionDecl(hasName("malloc"))))
剩下的就看你想要什么了select。
如果您只对匹配上面第一个示例中的直接赋值感兴趣,那么您可以使用 ignoringImpCasts
匹配器。
出于某种原因,我无法像您编写的那样将它插入匹配器中,所以只需反转匹配器即可。
看起来像这样:
binaryOperator(
hasOperatorName("="),
hasRHS(ignoringImpCasts(
callExpr(
callee(functionDecl(hasName("malloc")))
).bind("functionCall")
))
).bind("assignment")
如果您还想像第二个示例那样包含显式强制转换,请改用 ignoringParenImpCasts
:
binaryOperator(
hasOperatorName("="),
hasRHS(ignoringParenImpCasts(
callExpr(
callee(functionDecl(hasName("malloc")))
).bind("functionCall")
))
).bind("assignment")
如果您对包含 malloc
的任意表达式的所有赋值感兴趣,请改用 hasAncestor
。它不仅直接匹配parents,而且向上遍历直到匹配你的节点:
callExpr(
callee(functionDecl(hasName("malloc"))),
hasAncestor(
binaryOperator(hasOperatorName("=")).bind("assignment")
)
).bind("functionCall")
还有一件事。
您可能只对直接匹配源代码中定义的内容感兴趣,而不是包含 header 文件中的内容。
只需将 unless(isExpansionInSystemHeader())
添加到您的 top-level 匹配器,它将从系统 headers.
请注意,此代码已使用 LLVM 3.7 进行测试,未来的更改可能会破坏它。
如何调试
好吧,那我们到底是怎么知道的呢? 事实证明,Clang 已经为您提供了您所需要的一切:) 具体来说,有两个功能您可能会感兴趣。
当您使用 -Xclang ast-dump -fsyntax-only
调用 Clang 时,它将打印出翻译单元的漂亮多彩的 AST。不要惊讶地发现包含来自系统 header 的所有声明的巨大序言,因为它必须先 运行 预处理器才能生成 AST。示例:
$ clang -Xclang -ast-dump -fsyntax-only example.c
...
`-FunctionDecl 0x3f2fc28 <line:19:1, line:31:1> line:19:5 main 'int ()'
`-CompoundStmt 0x3f307b8 <line:20:1, line:31:1>
|-BinaryOperator 0x3f2ff38 <line:22:3, col:29> 'int *' '='
| |-DeclRefExpr 0x3f2fd40 <col:3> 'int *' lvalue Var 0x3f2f388 'a' 'int *'
| `-ImplicitCastExpr 0x3f2ff20 <col:7, col:29> 'int *' <BitCast>
| `-CallExpr 0x3f2fef0 <col:7, col:29> 'void *'
| |-ImplicitCastExpr 0x3f2fed8 <col:7> 'void *(*)(unsigned long)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x3f2fd68 <col:7> 'void *(unsigned long)' Function 0x3f1cdd0 'malloc' 'void *(unsigned long)'
| `-BinaryOperator 0x3f2fe88 <col:15, col:28> 'unsigned long' '*'
| |-ImplicitCastExpr 0x3f2fe70 <col:15> 'unsigned long' <IntegralCast>
| | `-ImplicitCastExpr 0x3f2fe58 <col:15> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x3f2fd90 <col:15> 'int' lvalue Var 0x3f2f488 'n' 'int'
| `-UnaryExprOrTypeTraitExpr 0x3f2fe38 <col:19, col:28> 'unsigned long' sizeof
| `-ParenExpr 0x3f2fe18 <col:25, col:28> 'int' lvalue
| `-UnaryOperator 0x3f2fdf8 <col:26, col:27> 'int' lvalue prefix '*'
| `-ImplicitCastExpr 0x3f2fde0 <col:27> 'int *' <LValueToRValue>
| `-DeclRefExpr 0x3f2fdb8 <col:27> 'int *' lvalue Var 0x3f2f388 'a' 'int *'
...
然后是 clang-query 如果您从源代码编译它,它会与 clang 一起构建。 它是 libTooling 的一个很好的例子,同时对开发有绝对惊人的帮助。 您只需在示例源文件上 运行 它并使用它来测试您的匹配器(请注意,它隐式地将 "root" 绑定到完整的匹配器):
$ <llvm>/bin/clang-query example.c --
clang-query> match callExpr(callee(functionDecl(hasName("malloc"))),hasAncestor(binaryOperator(hasOperatorName("=")).bind("assignment"))).bind("functionCall")
Match #1:
/vagrant/tests/true-valid-memsafety.c:22:3: note: "assignment" binds here
a = malloc (n * sizeof(*a));
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:22:7: note: "functionCall" binds here
a = malloc (n * sizeof(*a));
^~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:22:7: note: "root" binds here
a = malloc (n * sizeof(*a));
^~~~~~~~~~~~~~~~~~~~~~~
Match #2:
/vagrant/tests/true-valid-memsafety.c:23:3: note: "assignment" binds here
b = malloc (n * sizeof(*b));
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:23:7: note: "functionCall" binds here
b = malloc (n * sizeof(*b));
^~~~~~~~~~~~~~~~~~~~~~~
/vagrant/tests/true-valid-memsafety.c:23:7: note: "root" binds here
b = malloc (n * sizeof(*b));
^~~~~~~~~~~~~~~~~~~~~~~
2 matches.
如果您对有关该主题的更多信息感兴趣,请转到 this excellent blog post by Eli Bendersky for a good overview and introduction. The complete documentation for AST matchers can be found here。