Index: bindings/python/clang/cppmodel.py =================================================================== --- /dev/null +++ bindings/python/clang/cppmodel.py @@ -0,0 +1,97 @@ +import os +import sys +import clang.cindex +from clang.cindex import AccessSpecifier +from clang.cindex import CursorKind + +def _get_annotations(node): + return [c.displayname for c in node.get_children() + if c.kind == CursorKind.ANNOTATE_ATTR] + + +class FunctionArgument: + def __repr__(self): + return str(self.type)+":\""+str(self.name)+"\"" + + def __init__(self, type, name): + self.type = type + self.name = name + + +class _Function(object): + def __repr__(self): + return str(self.name) + + def __init__(self, cursor): + self.name = cursor.spelling + arguments = [x.spelling for x in cursor.get_arguments()] + argument_types = [x.spelling for x in cursor.type.argument_types()] + + self.type = cursor.type.spelling + self.return_type = cursor.type.get_result().spelling + self.arguments = [] + self.annotations = _get_annotations(cursor) + + for t,n in zip(argument_types,arguments): + self.arguments.append(FunctionArgument(t,n)) + + +class Function(_Function): + + def __init__(self, cursor, namespaces=[]): + _Function.__init__(self, cursor) + self.namespace = '::'.join(namespaces) + + +class Method(Function): + + def __init__(self, cursor): + _Function.__init__(self, cursor) + self.is_const = cursor.is_const_method() + self.is_virtual = cursor.is_virtual_method() + self.is_pure_virtual = cursor.is_pure_virtual_method() + self.is_public = (cursor.access_specifier == AccessSpecifier.PUBLIC) + + +class Class(object): + def __repr__(self): + return "Class:%s"%str(self.name) + + def __init__(self, cursor, namespaces): + self.name = cursor.spelling + self.namespace = '::'.join(namespaces) + self.constructors = [] + self.methods = [] + self.fields = [] + self.annotations = _get_annotations(cursor) + self.base_classes = [] + + for c in cursor.get_children(): + if (c.kind == CursorKind.CXX_METHOD): + f = Method(c) + self.methods.append(f) + elif (c.kind == CursorKind.CONSTRUCTOR): + f = Method(c) + self.constructors.append(f) + elif (c.kind == CursorKind.CXX_BASE_SPECIFIER): + self.base_classes.append(c.type.spelling) + +class Model(object): + def __repr__(self): + return "Classes:[{}]".format(",".join(self.classes)) + + def __init__(self, translation_unit): + self.functions = [] + self.classes = [] + self.add_child_nodes(translation_unit.cursor, []) + + def add_child_nodes(self, cursor, namespaces=[]): + for c in cursor.get_children(): + if c.kind == CursorKind.CLASS_DECL or c.kind == CursorKind.STRUCT_DECL: + self.classes.append(Class(c,namespaces)) + if c.kind == CursorKind.FUNCTION_DECL: + self.functions.append(Function(c,namespaces)) + elif c.kind == CursorKind.NAMESPACE: + namespaces.append(c.spelling) + self.add_child_nodes(c, namespaces) + Index: bindings/python/tests/cppmodel/test_classes.py =================================================================== --- /dev/null +++ bindings/python/tests/cppmodel/test_classes.py @@ -0,0 +1,125 @@ +from clang.cppmodel import * +from ..cindex.util import get_tu + +def test_class_name(): + source = 'class A{};' + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + + assert len(classes)==1 + assert classes[0].name == 'A' + +def test_class_methods(): + source = """ + class A{}; + class B{ + void foo(); + int bar(); + };""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + + assert len(classes[0].methods) == 0 + assert len(classes[1].methods) == 2 + +def test_class_method_return_types(): + source = """ + class B{ + void foo(); + int bar(); + };""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + + assert classes[0].methods[0].return_type == "void" + assert classes[0].methods[1].return_type == "int" + +def test_class_method_argument_types(): + source = """ + class A { + int foo(int i, const char* p); + };""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + args = classes[0].methods[0].arguments + + assert args[0].type == "int" + assert args[0].name == "i" + assert args[1].type == "const char *" + # note the inserted whitespace ^ + assert args[1].name == "p" + +def test_class_method_const_qualifiers(): + source = """ + class A { + int foo() const; + int bar(); + };""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + methods = classes[0].methods + + assert methods[0].is_const + assert not methods[1].is_const + +def test_class_methods_are_virtual(): + source = """ + class A { + virtual int foo(); + int bar(); + virtual int foobar() = 0; + };""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + methods = classes[0].methods + + assert methods[0].is_virtual + assert not methods[0].is_pure_virtual + assert not methods[1].is_virtual + assert methods[2].is_pure_virtual + +def test_namespaces(): + source = """ + class A{}; + namespace outer { + class B{}; + namespace inner { + class C{}; + } // end inner + } // end outer""" + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + + assert classes[0].namespace == "" + assert classes[1].namespace == "outer" + assert classes[2].namespace == "outer::inner" + +def test_access_specifiers(): + source = """ + class A { int foo(); }; + struct B { int foo(); }; + class C { public: int foo(); }; + """ + tu = get_tu(source, 'cpp') + + model = Model(tu) + classes = model.classes + + assert not classes[0].methods[0].is_public + assert classes[1].methods[0].is_public + assert classes[2].methods[0].is_public +