Index: bindings/python/clang/cppmodel.py =================================================================== --- /dev/null +++ bindings/python/clang/cppmodel.py @@ -0,0 +1,110 @@ +import os +import sys +import clang.cindex +from clang.cindex import AccessSpecifier, CursorKind, TypeKind + +def _get_annotations(node): + return [c.displayname for c in node.get_children() + if c.kind == CursorKind.ANNOTATE_ATTR] + +class Type: + def __init__(self, cindex_type): + self.kind = cindex_type.kind + self.name = cindex_type.spelling + self.is_pointer = self.kind == TypeKind.POINTER + self.is_reference = self.kind == TypeKind.LVALUEREFERENCE + self.is_const = cindex_type.is_const_qualified() + if self.is_pointer or self.is_reference: + self.pointee = Type(cindex_type.get_pointee()) + else: self.pointee = None + + def __repr__(self): + return self.name + + +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 = [Type(x) for x in cursor.type.argument_types()] + + self.return_type = Type(cursor.type.get_result()) + 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: + child_namespaces = list(namespaces) + child_namespaces.append(c.spelling) + self.add_child_nodes(c, child_namespaces) + Index: bindings/python/tests/cppmodel/test_classes.py =================================================================== --- /dev/null +++ bindings/python/tests/cppmodel/test_classes.py @@ -0,0 +1,129 @@ +from ..cindex.util import get_tu +from clang import cppmodel +from clang.cindex import TypeKind + +def test_class_name(): + source = 'class A{};' + tu = get_tu(source, 'cpp') + + model = cppmodel.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 = cppmodel.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 = cppmodel.Model(tu) + classes = model.classes + + assert classes[0].methods[0].return_type.kind == TypeKind.VOID + assert classes[0].methods[1].return_type.kind == TypeKind.INT + +def test_class_method_argument_types(): + source = """ + class A { + int foo(int i, const char* p); + };""" + tu = get_tu(source, 'cpp') + + model = cppmodel.Model(tu) + classes = model.classes + args = classes[0].methods[0].arguments + + assert args[0].type.kind == TypeKind.INT + assert args[0].name == "i" + assert args[1].type.kind == TypeKind.POINTER + 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 = cppmodel.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 = cppmodel.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 + class D{}; + } // end outer + class E{};""" + tu = get_tu(source, 'cpp') + + model = cppmodel.Model(tu) + classes = model.classes + + assert classes[0].namespace == "" + assert classes[1].namespace == "outer" + assert classes[2].namespace == "outer::inner" + assert classes[3].namespace == "outer" + assert classes[4].namespace == "" + +def test_access_specifiers(): + source = """ + class A { int foo(); }; + struct B { int foo(); }; + class C { public: int foo(); }; + """ + tu = get_tu(source, 'cpp') + + model = cppmodel.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 + Index: bindings/python/tests/cppmodel/test_free_functions.py =================================================================== --- /dev/null +++ bindings/python/tests/cppmodel/test_free_functions.py @@ -0,0 +1,55 @@ +from ..cindex.util import get_tu +from clang import cppmodel +from clang.cindex import TypeKind + +def test_function_name(): + source = """ + void foo(); + void bar(); + """ + tu = get_tu(source, 'cpp') + + model = cppmodel.Model(tu) + functions = model.functions + + assert len(functions) == 2 + assert functions[0].name == 'foo' + assert functions[1].name == 'bar' + + +def test_function_return_type(): + source = """ + int foo(); + double* bar(); + """ + + tu = get_tu(source, 'cpp') + + model = cppmodel.Model(tu) + functions = model.functions + + assert functions[0].return_type.kind == TypeKind.INT + assert functions[1].return_type.kind == TypeKind.POINTER + assert functions[1].return_type.is_pointer + assert functions[1].return_type.pointee.kind == TypeKind.DOUBLE + assert not functions[1].return_type.pointee.is_const + + +def test_function_arguments(): + source = """ + int foo(); + double bar(int x, char y); + """ + + tu = get_tu(source, 'cpp') + + model = cppmodel.Model(tu) + functions = model.functions + + assert len(functions[0].arguments) == 0 + assert len(functions[1].arguments) == 2 + assert functions[1].arguments[0].type.kind == TypeKind.INT + assert functions[1].arguments[0].name == 'x' + assert functions[1].arguments[1].type.kind == TypeKind.CHAR_S + assert functions[1].arguments[1].name == 'y' + Index: bindings/python/tests/cppmodel/test_types.py =================================================================== --- /dev/null +++ bindings/python/tests/cppmodel/test_types.py @@ -0,0 +1,81 @@ +from ..cindex.util import get_tu +from clang import cppmodel +from clang.cindex import TypeKind + +def test_pointer_type(): + source = "double* pd();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + f = model.functions[0] + + assert f.return_type.kind == TypeKind.POINTER + assert not f.return_type.is_const + assert f.return_type.pointee.kind == TypeKind.DOUBLE + assert not f.return_type.pointee.is_const + + +def test_const_pointer_to_double_type(): + source = "double* const cpd();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + f = model.functions[0] + + assert f.return_type.kind == TypeKind.POINTER + assert f.return_type.is_const + assert f.return_type.pointee.kind == TypeKind.DOUBLE + assert not f.return_type.pointee.is_const + + +def test_const_pointer_to_const_double_type(): + source = "const double* const cpcd();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + functions = model.functions + f = model.functions[0] + + assert f.return_type.kind == TypeKind.POINTER + assert f.return_type.is_const + assert f.return_type.pointee.kind == TypeKind.DOUBLE + assert f.return_type.pointee.is_const + + +def test_pointer_to_pointer_type(): + source = "double** ppd();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + f = model.functions[0] + + assert f.return_type.kind == TypeKind.POINTER + assert f.return_type.is_pointer + assert f.return_type.pointee.kind == TypeKind.POINTER + assert f.return_type.pointee.is_pointer + assert f.return_type.pointee.pointee.kind == TypeKind.DOUBLE + + +def test_pointer_to_record_type(): + source = "class A{}; A* pA();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + f = model.functions[0] + + assert f.return_type.kind == TypeKind.POINTER + assert f.return_type.is_pointer + assert f.return_type.pointee.kind == TypeKind.RECORD + +def test_reference_to_record_type(): + source = "class A{}; A& pA();" + + tu = get_tu(source, 'cpp') + model = cppmodel.Model(tu) + f = model.functions[0] + + assert f.return_type.kind == TypeKind.LVALUEREFERENCE + assert not f.return_type.is_pointer + assert f.return_type.is_reference + assert f.return_type.pointee.kind == TypeKind.RECORD +