libclang type resolution problem (python bindings)

Hi,

I’m new to libclang and looking to do some refactoring type work using it. After reading the “Thinking Beyond The Compiler” slides, and the very nice blog post at

http://eli.thegreenplace.net/2011/07/03/parsing-c-in-python-with-clang/

I’ve run into a difficulty that seems to be associated with templates.

I have a python script which walks the AST and finds a declaration node associated with a C++ method. Then, I walk the tree again and looks for references to the desired method (there are multiple classes which have this method name, which is why this is very difficult to do textually and why clang looks like the right tool.)

This works great when the class is not a templated class:

struct A { void foo() {} };

struct B { void foo() {} };

int main()
{
A a;
a.foo();

B b;
b.foo();
}

My python script produces the following expected output:

./reference-test.py test1.cxx
Parsing source into AST…
Finding target declaration…
Finding references…
found foo of kind CursorKind.CALL_EXPR
Is this foo a ref to the target declaration? YES
found foo of kind CursorKind.MEMBER_REF_EXPR
Is this foo a ref to the target declaration? YES
found foo of kind CursorKind.CALL_EXPR
Is this foo a ref to the target declaration? NO
found foo of kind CursorKind.MEMBER_REF_EXPR
Is this foo a ref to the target declaration? NO

That is all expected behavior. The target declaration is A::foo().

However, when class A is a templated class:

template
struct A { void foo() {} };

struct B { void foo() {} };

int main()
{
A a;
a.foo();

B b;
b.foo();
}

The behavior changes, and none of the references seem to point back to the declaration node this time. The output is now:

./reference-test.py test2.cxx
Parsing source into AST…
Finding target declaration…
Finding references…
found foo of kind CursorKind.CALL_EXPR
Is this foo a ref to the target declaration? NO
found foo of kind CursorKind.MEMBER_REF_EXPR
Is this foo a ref to the target declaration? NO
found foo of kind CursorKind.CALL_EXPR
Is this foo a ref to the target declaration? NO
found foo of kind CursorKind.MEMBER_REF_EXPR
Is this foo a ref to the target declaration? NO

When I print out the full AST as text, I cannot find any other node in the AST which corresponds to the declaration of A::foo(). There is only one, and it is the one I am finding in my first pass through the tree.

I’m stumped as to why I seem to be unable to resolve to the declaration of A::foo() when A is a templated class.

Help?

My python script is as follows (apologies for most likely awful python, I don’t use it much at all):

#!/usr/bin/env python
“”" Usage: call with
“”"

import sys
import clang.cindex

target_decl_node = None

def find_target_method(node):
global target_decl_node

When we get here, we know we’re in the target declaration, so

look for the desired method

if node.spelling == “foo”:
target_decl_node = node

for child in node.get_children():
find_target_method(child)

def find_target_decl(node):

Find class declaration subtree

if node.kind.is_declaration():
if node.spelling == “A”:
find_target_method(node)

for child in node.get_children():
find_target_decl(child)

def find_target_refs(node):
if node.kind.is_expression():
if node.displayname == “foo”:
print “found foo of kind %s” % (node.kind)
refnode = clang.cindex.Cursor_ref(node)
print “Is this foo a ref to the target declaration?”,
if refnode == target_decl_node:
print “YES”
else:
print “NO”

for child in node.get_children():
find_target_refs(child)

cxx_args =
cxx_args.append(“-I/usr/local/include”)

print “Parsing source into AST…”
index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1], cxx_args)

print “Finding target declaration…”
find_target_decl(tu.cursor)

print “Finding references…”
find_target_refs(tu.cursor)