diff options
Diffstat (limited to 'bindings/python/tests/cindex')
-rw-r--r-- | bindings/python/tests/cindex/INPUTS/compile_commands.json | 17 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_cdb.py | 89 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_cursor.py | 151 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_location.py | 9 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_token_kind.py | 43 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_tokens.py | 52 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_translation_unit.py | 187 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_type.py | 26 | ||||
-rw-r--r-- | bindings/python/tests/cindex/util.py | 42 |
9 files changed, 593 insertions, 23 deletions
diff --git a/bindings/python/tests/cindex/INPUTS/compile_commands.json b/bindings/python/tests/cindex/INPUTS/compile_commands.json new file mode 100644 index 0000000..944150b --- /dev/null +++ b/bindings/python/tests/cindex/INPUTS/compile_commands.json @@ -0,0 +1,17 @@ +[ +{ + "directory": "/home/john.doe/MyProject", + "command": "clang++ -o project.o -c /home/john.doe/MyProject/project.cpp", + "file": "/home/john.doe/MyProject/project.cpp" +}, +{ + "directory": "/home/john.doe/MyProjectA", + "command": "clang++ -o project2.o -c /home/john.doe/MyProject/project2.cpp", + "file": "/home/john.doe/MyProject/project2.cpp" +}, +{ + "directory": "/home/john.doe/MyProjectB", + "command": "clang++ -DFEATURE=1 -o project2-feature.o -c /home/john.doe/MyProject/project2.cpp", + "file": "/home/john.doe/MyProject/project2.cpp" +} +] diff --git a/bindings/python/tests/cindex/test_cdb.py b/bindings/python/tests/cindex/test_cdb.py new file mode 100644 index 0000000..d0f580e --- /dev/null +++ b/bindings/python/tests/cindex/test_cdb.py @@ -0,0 +1,89 @@ +from clang.cindex import CompilationDatabase +from clang.cindex import CompilationDatabaseError +from clang.cindex import CompileCommands +from clang.cindex import CompileCommand +import os +import gc + +kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') + +def test_create_fail(): + """Check we fail loading a database with an assertion""" + path = os.path.dirname(__file__) + try: + cdb = CompilationDatabase.fromDirectory(path) + except CompilationDatabaseError as e: + assert e.cdb_error == CompilationDatabaseError.ERROR_CANNOTLOADDATABASE + else: + assert False + +def test_create(): + """Check we can load a compilation database""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + +def test_lookup_fail(): + """Check file lookup failure""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + assert cdb.getCompileCommands('file_do_not_exist.cpp') == None + +def test_lookup_succeed(): + """Check we get some results if the file exists in the db""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') + assert len(cmds) != 0 + +def test_1_compilecommand(): + """Check file with single compile command""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') + assert len(cmds) == 1 + assert cmds[0].directory == '/home/john.doe/MyProject' + expected = [ 'clang++', '-o', 'project.o', '-c', + '/home/john.doe/MyProject/project.cpp'] + for arg, exp in zip(cmds[0].arguments, expected): + assert arg == exp + +def test_2_compilecommand(): + """Check file with 2 compile commands""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp') + assert len(cmds) == 2 + expected = [ + { 'wd': '/home/john.doe/MyProjectA', + 'line': ['clang++', '-o', 'project2.o', '-c', + '/home/john.doe/MyProject/project2.cpp']}, + { 'wd': '/home/john.doe/MyProjectB', + 'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c', + '/home/john.doe/MyProject/project2.cpp']} + ] + for i in range(len(cmds)): + assert cmds[i].directory == expected[i]['wd'] + for arg, exp in zip(cmds[i].arguments, expected[i]['line']): + assert arg == exp + +def test_compilecommand_iterator_stops(): + """Check that iterator stops after the correct number of elements""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + count = 0 + for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'): + count += 1 + assert count <= 2 + +def test_compilationDB_references(): + """Ensure CompilationsCommands are independent of the database""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') + del cdb + gc.collect() + workingdir = cmds[0].directory + +def test_compilationCommands_references(): + """Ensure CompilationsCommand keeps a reference to CompilationCommands""" + cdb = CompilationDatabase.fromDirectory(kInputsDir) + cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') + del cdb + cmd0 = cmds[0] + del cmds + gc.collect() + workingdir = cmd0.directory + diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py index 9f02bb2..51695e2 100644 --- a/bindings/python/tests/cindex/test_cursor.py +++ b/bindings/python/tests/cindex/test_cursor.py @@ -1,6 +1,10 @@ +import gc + from clang.cindex import CursorKind +from clang.cindex import TranslationUnit from clang.cindex import TypeKind from .util import get_cursor +from .util import get_cursors from .util import get_tu kInput = """\ @@ -37,6 +41,8 @@ def test_get_children(): tu_nodes = list(it) assert len(tu_nodes) == 3 + for cursor in tu_nodes: + assert cursor.translation_unit is not None assert tu_nodes[0] != tu_nodes[1] assert tu_nodes[0].kind == CursorKind.STRUCT_DECL @@ -46,6 +52,7 @@ def test_get_children(): assert tu_nodes[0].location.line == 4 assert tu_nodes[0].location.column == 8 assert tu_nodes[0].hash > 0 + assert tu_nodes[0].translation_unit is not None s0_nodes = list(tu_nodes[0].get_children()) assert len(s0_nodes) == 2 @@ -66,6 +73,51 @@ def test_get_children(): assert tu_nodes[2].displayname == 'f0(int, int)' assert tu_nodes[2].is_definition() == True +def test_references(): + """Ensure that references to TranslationUnit are kept.""" + tu = get_tu('int x;') + cursors = list(tu.cursor.get_children()) + assert len(cursors) > 0 + + cursor = cursors[0] + assert isinstance(cursor.translation_unit, TranslationUnit) + + # Delete reference to TU and perform a full GC. + del tu + gc.collect() + assert isinstance(cursor.translation_unit, TranslationUnit) + + # If the TU was destroyed, this should cause a segfault. + parent = cursor.semantic_parent + +def test_canonical(): + source = 'struct X; struct X; struct X { int member; };' + tu = get_tu(source) + + cursors = [] + for cursor in tu.cursor.get_children(): + if cursor.spelling == 'X': + cursors.append(cursor) + + assert len(cursors) == 3 + assert cursors[1].canonical == cursors[2].canonical + +def test_is_static_method(): + """Ensure Cursor.is_static_method works.""" + + source = 'class X { static void foo(); void bar(); };' + tu = get_tu(source, lang='cpp') + + cls = get_cursor(tu, 'X') + foo = get_cursor(tu, 'foo') + bar = get_cursor(tu, 'bar') + assert cls is not None + assert foo is not None + assert bar is not None + + assert foo.is_static_method() + assert not bar.is_static_method() + def test_underlying_type(): tu = get_tu('typedef int foo;') typedef = get_cursor(tu, 'foo') @@ -75,6 +127,30 @@ def test_underlying_type(): underlying = typedef.underlying_typedef_type assert underlying.kind == TypeKind.INT +kParentTest = """\ + class C { + void f(); + } + + void C::f() { } + """ +def test_semantic_parent(): + tu = get_tu(kParentTest, 'cpp') + curs = get_cursors(tu, 'f') + decl = get_cursor(tu, 'C') + assert(len(curs) == 2) + assert(curs[0].semantic_parent == curs[1].semantic_parent) + assert(curs[0].semantic_parent == decl) + +def test_lexical_parent(): + tu = get_tu(kParentTest, 'cpp') + curs = get_cursors(tu, 'f') + decl = get_cursor(tu, 'C') + assert(len(curs) == 2) + assert(curs[0].lexical_parent != curs[1].lexical_parent) + assert(curs[0].lexical_parent == decl) + assert(curs[1].lexical_parent == tu.cursor) + def test_enum_type(): tu = get_tu('enum TEST { FOO=1, BAR=2 };') enum = get_cursor(tu, 'TEST') @@ -84,9 +160,84 @@ def test_enum_type(): enum_type = enum.enum_type assert enum_type.kind == TypeKind.UINT +def test_enum_type_cpp(): + tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp") + enum = get_cursor(tu, 'TEST') + assert enum is not None + + assert enum.kind == CursorKind.ENUM_DECL + assert enum.enum_type.kind == TypeKind.LONGLONG + def test_objc_type_encoding(): tu = get_tu('int i;', lang='objc') i = get_cursor(tu, 'i') assert i is not None assert i.objc_type_encoding == 'i' + +def test_enum_values(): + tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};') + enum = get_cursor(tu, 'TEST') + assert enum is not None + + assert enum.kind == CursorKind.ENUM_DECL + + enum_constants = list(enum.get_children()) + assert len(enum_constants) == 3 + + spam, egg, ham = enum_constants + + assert spam.kind == CursorKind.ENUM_CONSTANT_DECL + assert spam.enum_value == 1 + assert egg.kind == CursorKind.ENUM_CONSTANT_DECL + assert egg.enum_value == 2 + assert ham.kind == CursorKind.ENUM_CONSTANT_DECL + assert ham.enum_value == 40 + +def test_enum_values_cpp(): + tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp") + enum = get_cursor(tu, 'TEST') + assert enum is not None + + assert enum.kind == CursorKind.ENUM_DECL + + enum_constants = list(enum.get_children()) + assert len(enum_constants) == 2 + + spam, ham = enum_constants + + assert spam.kind == CursorKind.ENUM_CONSTANT_DECL + assert spam.enum_value == -1 + assert ham.kind == CursorKind.ENUM_CONSTANT_DECL + assert ham.enum_value == 0x10000000000 + +def test_annotation_attribute(): + tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));') + + foo = get_cursor(tu, 'foo') + assert foo is not None + + for c in foo.get_children(): + if c.kind == CursorKind.ANNOTATE_ATTR: + assert c.displayname == "here be annotation attribute" + break + else: + assert False, "Couldn't find annotation" + +def test_result_type(): + tu = get_tu('int foo();') + foo = get_cursor(tu, 'foo') + + assert foo is not None + t = foo.result_type + assert t.kind == TypeKind.INT + +def test_get_tokens(): + """Ensure we can map cursors back to tokens.""" + tu = get_tu('int foo(int i);') + foo = get_cursor(tu, 'foo') + + tokens = list(foo.get_tokens()) + assert len(tokens) == 7 + assert tokens[0].spelling == 'int' + assert tokens[1].spelling == 'foo' diff --git a/bindings/python/tests/cindex/test_location.py b/bindings/python/tests/cindex/test_location.py index 528676e..9e9ef48 100644 --- a/bindings/python/tests/cindex/test_location.py +++ b/bindings/python/tests/cindex/test_location.py @@ -60,6 +60,15 @@ def test_location(): location3 = SourceLocation.from_position(tu, file, 1, 4) assert location2 != location3 + offset_location = SourceLocation.from_offset(tu, file, 5) + cursor = Cursor.from_location(tu, offset_location) + verified = False + for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']: + assert n == cursor + verified = True + + assert verified + def test_extent(): tu = get_tu(baseInput) one = get_cursor(tu, 'one') diff --git a/bindings/python/tests/cindex/test_token_kind.py b/bindings/python/tests/cindex/test_token_kind.py new file mode 100644 index 0000000..62ec63e --- /dev/null +++ b/bindings/python/tests/cindex/test_token_kind.py @@ -0,0 +1,43 @@ +from clang.cindex import TokenKind +from nose.tools import eq_ +from nose.tools import ok_ +from nose.tools import raises + +def test_constructor(): + """Ensure TokenKind constructor works as expected.""" + + t = TokenKind(5, 'foo') + + eq_(t.value, 5) + eq_(t.name, 'foo') + +@raises(ValueError) +def test_bad_register(): + """Ensure a duplicate value is rejected for registration.""" + + TokenKind.register(2, 'foo') + +@raises(ValueError) +def test_unknown_value(): + """Ensure trying to fetch an unknown value raises.""" + + TokenKind.from_value(-1) + +def test_registration(): + """Ensure that items registered appear as class attributes.""" + ok_(hasattr(TokenKind, 'LITERAL')) + literal = TokenKind.LITERAL + + ok_(isinstance(literal, TokenKind)) + +def test_from_value(): + """Ensure registered values can be obtained from from_value().""" + t = TokenKind.from_value(3) + ok_(isinstance(t, TokenKind)) + eq_(t, TokenKind.LITERAL) + +def test_repr(): + """Ensure repr() works.""" + + r = repr(TokenKind.LITERAL) + eq_(r, 'TokenKind.LITERAL') diff --git a/bindings/python/tests/cindex/test_tokens.py b/bindings/python/tests/cindex/test_tokens.py new file mode 100644 index 0000000..7074842 --- /dev/null +++ b/bindings/python/tests/cindex/test_tokens.py @@ -0,0 +1,52 @@ +from clang.cindex import CursorKind +from clang.cindex import Index +from clang.cindex import SourceLocation +from clang.cindex import SourceRange +from clang.cindex import TokenKind +from nose.tools import eq_ +from nose.tools import ok_ + +from .util import get_tu + +def test_token_to_cursor(): + """Ensure we can obtain a Cursor from a Token instance.""" + tu = get_tu('int i = 5;') + r = tu.get_extent('t.c', (0, 9)) + tokens = list(tu.get_tokens(extent=r)) + + assert len(tokens) == 5 + assert tokens[1].spelling == 'i' + assert tokens[1].kind == TokenKind.IDENTIFIER + + cursor = tokens[1].cursor + assert cursor.kind == CursorKind.VAR_DECL + assert tokens[1].cursor == tokens[2].cursor + +def test_token_location(): + """Ensure Token.location works.""" + + tu = get_tu('int foo = 10;') + r = tu.get_extent('t.c', (0, 11)) + + tokens = list(tu.get_tokens(extent=r)) + eq_(len(tokens), 4) + + loc = tokens[1].location + ok_(isinstance(loc, SourceLocation)) + eq_(loc.line, 1) + eq_(loc.column, 5) + eq_(loc.offset, 4) + +def test_token_extent(): + """Ensure Token.extent works.""" + tu = get_tu('int foo = 10;') + r = tu.get_extent('t.c', (0, 11)) + + tokens = list(tu.get_tokens(extent=r)) + eq_(len(tokens), 4) + + extent = tokens[1].extent + ok_(isinstance(extent, SourceRange)) + + eq_(extent.start.offset, 4) + eq_(extent.end.offset, 7) diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py index 2e65d95..c91f126 100644 --- a/bindings/python/tests/cindex/test_translation_unit.py +++ b/bindings/python/tests/cindex/test_translation_unit.py @@ -1,42 +1,48 @@ -from clang.cindex import * +import gc import os +from clang.cindex import CursorKind +from clang.cindex import Cursor +from clang.cindex import File +from clang.cindex import Index +from clang.cindex import SourceLocation +from clang.cindex import SourceRange +from clang.cindex import TranslationUnitSaveError +from clang.cindex import TranslationUnit +from .util import get_cursor +from .util import get_tu + kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') def test_spelling(): path = os.path.join(kInputsDir, 'hello.cpp') - index = Index.create() - tu = index.parse(path) + tu = TranslationUnit.from_source(path) assert tu.spelling == path def test_cursor(): path = os.path.join(kInputsDir, 'hello.cpp') - index = Index.create() - tu = index.parse(path) + tu = get_tu(path) c = tu.cursor assert isinstance(c, Cursor) assert c.kind is CursorKind.TRANSLATION_UNIT def test_parse_arguments(): path = os.path.join(kInputsDir, 'parse_arguments.c') - index = Index.create() - tu = index.parse(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) + tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) spellings = [c.spelling for c in tu.cursor.get_children()] assert spellings[-2] == 'hello' assert spellings[-1] == 'hi' def test_reparse_arguments(): path = os.path.join(kInputsDir, 'parse_arguments.c') - index = Index.create() - tu = index.parse(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) + tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) tu.reparse() spellings = [c.spelling for c in tu.cursor.get_children()] assert spellings[-2] == 'hello' assert spellings[-1] == 'hi' def test_unsaved_files(): - index = Index.create() - tu = index.parse('fake.c', ['-I./'], unsaved_files = [ + tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [ ('fake.c', """ #include "fake.h" int x; @@ -52,8 +58,7 @@ int SOME_DEFINE; def test_unsaved_files_2(): import StringIO - index = Index.create() - tu = index.parse('fake.c', unsaved_files = [ + tu = TranslationUnit.from_source('fake.c', unsaved_files = [ ('fake.c', StringIO.StringIO('int x;'))]) spellings = [c.spelling for c in tu.cursor.get_children()] assert spellings[-1] == 'x' @@ -78,7 +83,159 @@ def test_includes(): h3 = os.path.join(kInputsDir, "header3.h") inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)] - index = Index.create() - tu = index.parse(src) + tu = TranslationUnit.from_source(src) for i in zip(inc, tu.get_includes()): assert eq(i[0], i[1]) + +def save_tu(tu): + """Convenience API to save a TranslationUnit to a file. + + Returns the filename it was saved to. + """ + + # FIXME Generate a temp file path using system APIs. + base = 'TEMP_FOR_TRANSLATIONUNIT_SAVE.c' + path = os.path.join(kInputsDir, base) + + # Just in case. + if os.path.exists(path): + os.unlink(path) + + tu.save(path) + + return path + +def test_save(): + """Ensure TranslationUnit.save() works.""" + + tu = get_tu('int foo();') + + path = save_tu(tu) + assert os.path.exists(path) + assert os.path.getsize(path) > 0 + os.unlink(path) + +def test_save_translation_errors(): + """Ensure that saving to an invalid directory raises.""" + + tu = get_tu('int foo();') + + path = '/does/not/exist/llvm-test.ast' + assert not os.path.exists(os.path.dirname(path)) + + try: + tu.save(path) + assert False + except TranslationUnitSaveError as ex: + expected = TranslationUnitSaveError.ERROR_UNKNOWN + assert ex.save_error == expected + +def test_load(): + """Ensure TranslationUnits can be constructed from saved files.""" + + tu = get_tu('int foo();') + assert len(tu.diagnostics) == 0 + path = save_tu(tu) + + assert os.path.exists(path) + assert os.path.getsize(path) > 0 + + tu2 = TranslationUnit.from_ast_file(filename=path) + assert len(tu2.diagnostics) == 0 + + foo = get_cursor(tu2, 'foo') + assert foo is not None + + # Just in case there is an open file descriptor somewhere. + del tu2 + + os.unlink(path) + +def test_index_parse(): + path = os.path.join(kInputsDir, 'hello.cpp') + index = Index.create() + tu = index.parse(path) + assert isinstance(tu, TranslationUnit) + +def test_get_file(): + """Ensure tu.get_file() works appropriately.""" + + tu = get_tu('int foo();') + + f = tu.get_file('t.c') + assert isinstance(f, File) + assert f.name == 't.c' + + try: + f = tu.get_file('foobar.cpp') + except: + pass + else: + assert False + +def test_get_source_location(): + """Ensure tu.get_source_location() works.""" + + tu = get_tu('int foo();') + + location = tu.get_location('t.c', 2) + assert isinstance(location, SourceLocation) + assert location.offset == 2 + assert location.file.name == 't.c' + + location = tu.get_location('t.c', (1, 3)) + assert isinstance(location, SourceLocation) + assert location.line == 1 + assert location.column == 3 + assert location.file.name == 't.c' + +def test_get_source_range(): + """Ensure tu.get_source_range() works.""" + + tu = get_tu('int foo();') + + r = tu.get_extent('t.c', (1,4)) + assert isinstance(r, SourceRange) + assert r.start.offset == 1 + assert r.end.offset == 4 + assert r.start.file.name == 't.c' + assert r.end.file.name == 't.c' + + r = tu.get_extent('t.c', ((1,2), (1,3))) + assert isinstance(r, SourceRange) + assert r.start.line == 1 + assert r.start.column == 2 + assert r.end.line == 1 + assert r.end.column == 3 + assert r.start.file.name == 't.c' + assert r.end.file.name == 't.c' + + start = tu.get_location('t.c', 0) + end = tu.get_location('t.c', 5) + + r = tu.get_extent('t.c', (start, end)) + assert isinstance(r, SourceRange) + assert r.start.offset == 0 + assert r.end.offset == 5 + assert r.start.file.name == 't.c' + assert r.end.file.name == 't.c' + +def test_get_tokens_gc(): + """Ensures get_tokens() works properly with garbage collection.""" + + tu = get_tu('int foo();') + r = tu.get_extent('t.c', (0, 10)) + tokens = list(tu.get_tokens(extent=r)) + + assert tokens[0].spelling == 'int' + gc.collect() + assert tokens[0].spelling == 'int' + + del tokens[1] + gc.collect() + assert tokens[0].spelling == 'int' + + # May trigger segfault if we don't do our job properly. + del tokens + gc.collect() + gc.collect() # Just in case. diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py index 03621f3..28e4411 100644 --- a/bindings/python/tests/cindex/test_type.py +++ b/bindings/python/tests/cindex/test_type.py @@ -1,4 +1,7 @@ +import gc + from clang.cindex import CursorKind +from clang.cindex import TranslationUnit from clang.cindex import TypeKind from nose.tools import raises from .util import get_cursor @@ -28,6 +31,7 @@ def test_a_struct(): assert teststruct is not None, "Could not find teststruct." fields = list(teststruct.get_children()) assert all(x.kind == CursorKind.FIELD_DECL for x in fields) + assert all(x.translation_unit is not None for x in fields) assert fields[0].spelling == 'a' assert not fields[0].type.is_const_qualified() @@ -72,6 +76,26 @@ def test_a_struct(): assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT +def test_references(): + """Ensure that a Type maintains a reference to a TranslationUnit.""" + + tu = get_tu('int x;') + children = list(tu.cursor.get_children()) + assert len(children) > 0 + + cursor = children[0] + t = cursor.type + + assert isinstance(t.translation_unit, TranslationUnit) + + # Delete main TranslationUnit reference and force a GC. + del tu + gc.collect() + assert isinstance(t.translation_unit, TranslationUnit) + + # If the TU was destroyed, this should cause a segfault. + decl = t.get_declaration() + constarrayInput=""" struct teststruct { void *A[2]; @@ -263,7 +287,7 @@ def test_is_volatile_qualified(): def test_is_restrict_qualified(): """Ensure Type.is_restrict_qualified works.""" - tu = get_tu('struct s { void * restrict i; void * j };') + tu = get_tu('struct s { void * restrict i; void * j; };') i = get_cursor(tu, 'i') j = get_cursor(tu, 'j') diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py index 388b269..2323839 100644 --- a/bindings/python/tests/cindex/util.py +++ b/bindings/python/tests/cindex/util.py @@ -1,7 +1,7 @@ # This file provides common utility functions for the test suite. from clang.cindex import Cursor -from clang.cindex import Index +from clang.cindex import TranslationUnit def get_tu(source, lang='c', all_warnings=False): """Obtain a translation unit from source and language. @@ -15,21 +15,20 @@ def get_tu(source, lang='c', all_warnings=False): all_warnings is a convenience argument to enable all compiler warnings. """ name = 't.c' + args = [] if lang == 'cpp': name = 't.cpp' + args.append('-std=c++11') elif lang == 'objc': name = 't.m' elif lang != 'c': raise Exception('Unknown language: %s' % lang) - args = [] if all_warnings: - args = ['-Wall', '-Wextra'] + args += ['-Wall', '-Wextra'] - index = Index.create() - tu = index.parse(name, args=args, unsaved_files=[(name, source)]) - assert tu is not None - return tu + return TranslationUnit.from_source(name, args, unsaved_files=[(name, + source)]) def get_cursor(source, spelling): """Obtain a cursor from a source object. @@ -57,9 +56,38 @@ def get_cursor(source, spelling): return result return None + +def get_cursors(source, spelling): + """Obtain all cursors from a source object with a specific spelling. + + This provides a convenient search mechanism to find all cursors with specific + spelling within a source. The first argument can be either a + TranslationUnit or Cursor instance. + + If no cursors are found, an empty list is returned. + """ + cursors = [] + children = [] + if isinstance(source, Cursor): + children = source.get_children() + else: + # Assume TU + children = source.cursor.get_children() + + for cursor in children: + if cursor.spelling == spelling: + cursors.append(cursor) + + # Recurse into children. + cursors.extend(get_cursors(cursor, spelling)) + + return cursors + + __all__ = [ 'get_cursor', + 'get_cursors', 'get_tu', ] |