获取所有" malloc"使用ASTMatcher打电话

时间:2015-04-01 00:38:06

标签: clang abstract-syntax-tree

我试图在clang中使用malloc拨打所有ASTMatcher来电。这是代码示例:

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

编译好。但我仍然无法接听malloc个电话。如何使用clang ASTMatcher获取所有malloc个电话?

1 个答案:

答案 0 :(得分:13)

问题

malloc函数的签名定义如下:

void* malloc (size_t size);

为了将malloc的返回值分配给除void*以外的任何类型的指针,您必须强制转换它。 虽然C ++要求您显式转换,但C编译器会隐式执行此操作。所以即使你写了

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

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

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的this excellent blog post,以获得良好的概述和介绍。可以找到AST匹配器的完整文档here