Problem
The signature of the malloc function is defined as follows:
void* malloc (size_t size);
To assign a malloc return value to a pointer of any type other than void* , you must use it. Although C ++ requires you to explicitly specify, the C compiler does this implicitly for you. Therefore, even if you write
int *a = malloc(sizeof(*a));
the compiler will implicitly use the RHS expression. It will be equivalent
int *a = (int*) malloc(sizeof(*a));
hasParent constriction is used , which only matches direct parents, not ancestors . Therefore, your interlocutor will correspond to the appointments without any types.
And almost the same thing happens with your declRefExpr . The C standard says that functions automatically break up into function pointers. Clang implicitly drops malloc in void *(*)(size_t) and breaks your match hierarchy.
Possible solutions
Well, it depends on what you really want to do. First of all, you can basically fix the malloc select function with this snippet:
callExpr(callee(functionDecl(hasName("malloc"))))
The rest depends on what you want to choose. If you are only interested in matching direct jobs, as in the first example above, you can use ignoringImpCasts . For some reason, I was not able to insert it into the matches as you wrote it, so just invert the match. Looks like that:
binaryOperator( hasOperatorName("="), hasRHS(ignoringImpCasts( callExpr( callee(functionDecl(hasName("malloc"))) ).bind("functionCall") )) ).bind("assignment")
If you also want to enable explicit casts, as in the second example, use ignoringParenImpCasts instead:
binaryOperator( hasOperatorName("="), hasRHS(ignoringParenImpCasts( callExpr( callee(functionDecl(hasName("malloc"))) ).bind("functionCall") )) ).bind("assignment")
If you are interested in all assignments with arbitrary expressions containing malloc , use hasAncestor . It not only matches the direct parents, but also passes until it matches your node:
callExpr( callee(functionDecl(hasName("malloc"))), hasAncestor( binaryOperator(hasOperatorName("=")).bind("assignment") ) ).bind("functionCall")
One more thing. You are probably only interested in comparing everything that is defined in the source code and not including the header files. Just add unless(isExpansionInSystemHeader()) to your top-level match, and it will exclude all definitions from system headers.
Please note that this code has been tested with LLVM 3.7, and future changes may break it.
Debugging
Okay, so how the hell should we all know this? It turns out that Klang already provides you with everything you need :) In particular, there are two functions that may interest you.
When you call Clang with -Xclang ast-dump -fsyntax-only , it will display the beautiful and colorful AST of the translation unit. Do not be surprised if you find a huge preamble with all the declarations from the system headers that you have included, since you first need to run the preprocessor to generate the AST. Example:
$ 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 *' ...
And then there is clang-query, which is built along with clang if you compile it from sources. This is a great example of libTooling and absolutely amazing development assistance at the same time. You simply run it in an example source file and use it to check your helpers (note that it implicitly binds βrootβ to a complete match):
$ <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.
If you are interested in more detailed information on this topic, go to this excellent blog post by Eli Bendersky for a good overview and introduction. Full documentation for AST layouts is available here .