summaryrefslogtreecommitdiffstats
path: root/bindings/python/tests
diff options
context:
space:
mode:
Diffstat (limited to 'bindings/python/tests')
-rw-r--r--bindings/python/tests/cindex/INPUTS/compile_commands.json17
-rw-r--r--bindings/python/tests/cindex/test_cdb.py89
-rw-r--r--bindings/python/tests/cindex/test_cursor.py151
-rw-r--r--bindings/python/tests/cindex/test_location.py9
-rw-r--r--bindings/python/tests/cindex/test_token_kind.py43
-rw-r--r--bindings/python/tests/cindex/test_tokens.py52
-rw-r--r--bindings/python/tests/cindex/test_translation_unit.py187
-rw-r--r--bindings/python/tests/cindex/test_type.py26
-rw-r--r--bindings/python/tests/cindex/util.py42
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',
]
OpenPOWER on IntegriCloud