Reputation: 1883
Given the following C++ file:
class Foo
{
public:
Foo();
void bar(int input);
void another(int input, double & output);
};
void
Foo::bar(int input)
{
input += 1;
}
void
Foo::another(int input, double & output)
{
input += 1;
output = input * 1.2345;
}
How can I utilize clang python bindings to extract the definitions of the two methods. I am able to get the class declarations using the python script below, but I can't seem the figure out how to extract the complete methods. For example, I want this information:
void
Foo::another(int input, double & output)
{
input += 1;
output = input * 1.2345;
}
Python script:
#!/usr/bin/env python
import clang.cindex
clang.cindex.Config.set_library_path('/opt/moose/llvm-3.7.0/lib')
def getCursors(cursor, output, kind):
"""
Recursively extract all the cursors of the given kind.
"""
for c in cursor.get_children():
if c.kind == kind:
output.append(c)
getCursors(c, output, kind)
if __name__ == '__main__':
# Parse the test file
index = clang.cindex.Index.create()
tu = index.parse('method.C', ['-x', 'c++'])
# Extract the parsers
output = []
getCursors(tu.cursor, output, clang.cindex.CursorKind.CXX_METHOD)
# Print the method declarations (How to I get the definitions?)
for c in output:
defn = c.get_definition() # Gives nothing
print defn
print c.extent.start.file, c.extent.start.line, c.extent.end.line # Gives decleration
The following function was suggested as a solution, but it doesn't work for clang 3.7. I am unable to update to 3.9 until it is released and I need to support the latest two versions of clang (3.7 and 3.8). If you add a print statement the results indicate that a definition is not located.
def method_definitions(cursor):
for i in cursor.walk_preorder():
print i.kind, i.is_definition() # Added this
if i.kind != CursorKind.CXX_METHOD:
continue
if not i.is_definition():
continue
yield i
Running the method yields the following, anyone know what has changed between clang 3.7 and 3.9?
CursorKind.TRANSLATION_UNIT False
CursorKind.CLASS_DECL True
CursorKind.CXX_ACCESS_SPEC_DECL True
CursorKind.CONSTRUCTOR False
CursorKind.CXX_METHOD False
CursorKind.PARM_DECL True
CursorKind.CXX_METHOD False
CursorKind.PARM_DECL True
CursorKind.PARM_DECL True
Upvotes: 5
Views: 4151
Reputation: 42490
You're pretty close - the trick is to use the cursor offsets rather than line / columns, and that AFAIK, libclang doesn't expose the bytes of the source, so you need to read the text out yourself.
The following is tested on:
pip install 'clang==3.7'
) The only caveats are that for more complex cases with macros / headers YMMV.
import clang.cindex
from clang.cindex import *
def method_definitions(cursor):
for i in cursor.walk_preorder():
if i.kind != CursorKind.CXX_METHOD:
continue
if not i.is_definition():
continue
yield i
def extract_definition(cursor):
filename = cursor.location.file.name
with open(filename, 'r') as fh:
contents = fh.read()
return contents[cursor.extent.start.offset: cursor.extent.end.offset]
idx = Index.create()
tu = idx.parse('method.C', ['-x', 'c++'])
defns = method_definitions(tu.cursor)
for defn in defns:
print extract_definition(defn)
Which gives:
void
Foo::bar(int input)
{
input += 1;
}
void
Foo::another(int input, double & output)
{
input += 1;
output = input * 1.2345;
}
Upvotes: 1