259 lines
7.1 KiB
Python
259 lines
7.1 KiB
Python
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 TranslationUnitLoadError
|
|
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')
|
|
tu = TranslationUnit.from_source(path)
|
|
assert tu.spelling == path
|
|
|
|
def test_cursor():
|
|
path = os.path.join(kInputsDir, 'hello.cpp')
|
|
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')
|
|
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')
|
|
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():
|
|
tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
|
|
('fake.c', """
|
|
#include "fake.h"
|
|
int x;
|
|
int SOME_DEFINE;
|
|
"""),
|
|
('./fake.h', """
|
|
#define SOME_DEFINE y
|
|
""")
|
|
])
|
|
spellings = [c.spelling for c in tu.cursor.get_children()]
|
|
assert spellings[-2] == 'x'
|
|
assert spellings[-1] == 'y'
|
|
|
|
def test_unsaved_files_2():
|
|
import StringIO
|
|
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'
|
|
|
|
def normpaths_equal(path1, path2):
|
|
""" Compares two paths for equality after normalizing them with
|
|
os.path.normpath
|
|
"""
|
|
return os.path.normpath(path1) == os.path.normpath(path2)
|
|
|
|
def test_includes():
|
|
def eq(expected, actual):
|
|
if not actual.is_input_file:
|
|
return normpaths_equal(expected[0], actual.source.name) and \
|
|
normpaths_equal(expected[1], actual.include.name)
|
|
else:
|
|
return normpaths_equal(expected[1], actual.include.name)
|
|
|
|
src = os.path.join(kInputsDir, 'include.cpp')
|
|
h1 = os.path.join(kInputsDir, "header1.h")
|
|
h2 = os.path.join(kInputsDir, "header2.h")
|
|
h3 = os.path.join(kInputsDir, "header3.h")
|
|
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
|
|
|
|
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.
|
|
|
|
def test_fail_from_source():
|
|
path = os.path.join(kInputsDir, 'non-existent.cpp')
|
|
try:
|
|
tu = TranslationUnit.from_source(path)
|
|
except TranslationUnitLoadError:
|
|
tu = None
|
|
assert tu == None
|
|
|
|
def test_fail_from_ast_file():
|
|
path = os.path.join(kInputsDir, 'non-existent.ast')
|
|
try:
|
|
tu = TranslationUnit.from_ast_file(path)
|
|
except TranslationUnitLoadError:
|
|
tu = None
|
|
assert tu == None
|