使用ASTMatcher获取所有的"malloc"调用

12

我正在尝试使用clang中的ASTMatcher获取所有malloc调用。这是代码示例:

Finder.addMatcher(
      callExpr(
        hasParent(binaryOperator(
          hasOperatorName("=")).bind("assignment")),
          declRefExpr(to(functionDecl(hasName("malloc"))))).bind("functionCall"),
      &HandlerForFunctionCall);

编译没有问题。但我仍然无法获得任何malloc调用。如何使用clang ASTMatcher获取所有的malloc调用?

1个回答

16

问题

malloc函数的签名定义如下:

void* malloc (size_t size);

为了将malloc 的返回值分配给类型为void*以外的任何指针,你需要进行强制类型转换。虽然C++要求显式转换,但C编译器会自动进行转换。所以,即使您写了以下内容:
int *a = malloc(sizeof(*a));

编译器将隐式转换右侧表达式。这相当于

int *a = (int*) malloc(sizeof(*a));

您正在使用的是hasParent缩小匹配器,它仅匹配直接父级而不匹配任何祖先。因此,您的匹配器仅能匹配没有任何类型转换的赋值语句。
并且您的declRefExpr也会发生类似的情况。C标准规定函数会自动衰减为指向函数的指针。Clang会隐式地将malloc转换为void *(*)(size_t),这破坏了您匹配器的层次结构。

可能的解决方案

这取决于您实际想要做什么。首先,您通常可以使用以下代码片段修复选择malloc函数的部分:
callExpr(callee(functionDecl(hasName("malloc"))))

其余的取决于您要选择什么。 如果您只对像上面第一个示例中那样的直接匹配感兴趣,则可以使用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。它不仅匹配直接父节点,而且会向上遍历,直到匹配到您的节点为止。
callExpr(
  callee(functionDecl(hasName("malloc"))),
  hasAncestor(
    binaryOperator(hasOperatorName("=")).bind("assignment")
  )
).bind("functionCall")

还有一件事。 你可能只对直接定义在源代码中的内容感兴趣,而不是包含在头文件中的内容。 只需在顶层匹配器中添加unless(isExpansionInSystemHeader()),它将排除所有来自系统标头的定义。

请注意,此代码已经经过LLVM 3.7的测试,未来更改可能会破坏它。

如何调试

好吧,我们怎么知道所有这些呢? 事实证明,Clang已经为您提供了所需的全部内容 :) 具体而言,有两个功能可能会对您感兴趣。

当您使用-Xclang ast-dump -fsyntax-only调用Clang时,它将打印出一个漂亮而丰富多彩的翻译单元AST。不要惊讶地发现一个巨大的前导,其中包含您包括的所有系统标头的声明,因为它必须先运行预处理器以生成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.

如果您对该主题有更多兴趣,可以前往 Eli Bendersky 的这篇优秀博客文章进行概览和介绍。AST matchers 的完整文档可以在这里找到。


非常好的解释,Jan。非常感谢你。 - G Gill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接