aboutsummaryrefslogtreecommitdiff
path: root/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'bindings')
-rw-r--r--bindings/python/README.txt4
-rw-r--r--bindings/python/clang/cindex.py100
-rw-r--r--bindings/python/examples/cindex/cindex-dump.py2
-rw-r--r--bindings/python/tests/CMakeLists.txt46
-rw-r--r--bindings/python/tests/cindex/test_access_specifiers.py4
-rw-r--r--bindings/python/tests/cindex/test_cdb.py19
-rw-r--r--bindings/python/tests/cindex/test_code_completion.py41
-rw-r--r--bindings/python/tests/cindex/test_comment.py5
-rw-r--r--bindings/python/tests/cindex/test_cursor.py5
-rw-r--r--bindings/python/tests/cindex/test_cursor_kind.py5
-rw-r--r--bindings/python/tests/cindex/test_diagnostics.py9
-rw-r--r--bindings/python/tests/cindex/test_exception_specification_kind.py5
-rw-r--r--bindings/python/tests/cindex/test_file.py5
-rw-r--r--bindings/python/tests/cindex/test_index.py5
-rw-r--r--bindings/python/tests/cindex/test_linkage.py5
-rw-r--r--bindings/python/tests/cindex/test_location.py5
-rw-r--r--bindings/python/tests/cindex/test_tls_kind.py5
-rw-r--r--bindings/python/tests/cindex/test_token_kind.py5
-rw-r--r--bindings/python/tests/cindex/test_tokens.py5
-rw-r--r--bindings/python/tests/cindex/test_translation_unit.py80
-rw-r--r--bindings/python/tests/cindex/test_type.py30
-rw-r--r--bindings/python/tests/cindex/util.py15
22 files changed, 355 insertions, 50 deletions
diff --git a/bindings/python/README.txt b/bindings/python/README.txt
index 8a0bf99b30e3..b0f0142a56f1 100644
--- a/bindings/python/README.txt
+++ b/bindings/python/README.txt
@@ -4,12 +4,12 @@
This directory implements Python bindings for Clang.
-You may need to alter LD_LIBRARY_PATH so that the Clang library can be
+You may need to set CLANG_LIBRARY_PATH so that the Clang library can be
found. The unit tests are designed to be run with any standard test
runner. For example:
--
$ env PYTHONPATH=$(echo ~/llvm/tools/clang/bindings/python/) \
- LD_LIBRARY_PATH=$(llvm-config --libdir) \
+ CLANG_LIBRARY_PATH=$(llvm-config --libdir) \
python -m unittest discover -v
tests.cindex.test_index.test_create ... ok
...
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 56fcc78763e3..8b23ae90b982 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -44,6 +44,7 @@ The major indexing objects are:
Most object information is exposed using properties, when the underlying API
call is efficient.
"""
+from __future__ import absolute_import, division, print_function
# TODO
# ====
@@ -63,10 +64,10 @@ call is efficient.
# o implement additional SourceLocation, SourceRange, and File methods.
from ctypes import *
-import collections
import clang.enumerations
+import os
import sys
if sys.version_info[0] == 3:
# Python 3 strings are unicode, translate them to/from utf8 for C-interop.
@@ -108,8 +109,6 @@ if sys.version_info[0] == 3:
return x
return x.encode('utf8')
- xrange = range
-
elif sys.version_info[0] == 2:
# Python 2 strings are utf8 byte strings, no translation is needed for
# C-interop.
@@ -123,6 +122,22 @@ elif sys.version_info[0] == 2:
def b(x):
return x
+# Importing ABC-s directly from collections is deprecated since Python 3.7,
+# will stop working in Python 3.8.
+# See: https://docs.python.org/dev/whatsnew/3.7.html#id3
+if sys.version_info[:2] >= (3, 7):
+ from collections import abc as collections_abc
+else:
+ import collections as collections_abc
+
+# We only support PathLike objects on Python version with os.fspath present
+# to be consistent with the Python standard library. On older Python versions
+# we only support strings and we have dummy fspath to just pass them through.
+try:
+ fspath = os.fspath
+except AttributeError:
+ def fspath(x):
+ return x
# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper
# object. This is a problem, because it means that from_parameter will see an
@@ -391,7 +406,7 @@ class Diagnostic(object):
@property
def ranges(self):
- class RangeIterator:
+ class RangeIterator(object):
def __init__(self, diag):
self.diag = diag
@@ -407,7 +422,7 @@ class Diagnostic(object):
@property
def fixits(self):
- class FixItIterator:
+ class FixItIterator(object):
def __init__(self, diag):
self.diag = diag
@@ -427,7 +442,7 @@ class Diagnostic(object):
@property
def children(self):
- class ChildDiagnosticsIterator:
+ class ChildDiagnosticsIterator(object):
def __init__(self, diag):
self.diag_set = conf.lib.clang_getChildDiagnostics(diag)
@@ -547,7 +562,7 @@ class TokenGroup(object):
token_group = TokenGroup(tu, tokens_memory, tokens_count)
- for i in xrange(0, count):
+ for i in range(0, count):
token = Token()
token.int_data = tokens_array[i].int_data
token.ptr_data = tokens_array[i].ptr_data
@@ -2173,7 +2188,7 @@ class Type(Structure):
The returned object is iterable and indexable. Each item in the
container is a Type instance.
"""
- class ArgumentsIterator(collections.Sequence):
+ class ArgumentsIterator(collections_abc.Sequence):
def __init__(self, parent):
self.parent = parent
self.length = None
@@ -2254,6 +2269,12 @@ class Type(Structure):
return res
+ def get_num_template_arguments(self):
+ return conf.lib.clang_Type_getNumTemplateArguments(self)
+
+ def get_template_argument_type(self, num):
+ return conf.lib.clang_Type_getTemplateArgumentAsType(self, num)
+
def get_canonical(self):
"""
Return the canonical type for a Type.
@@ -2460,8 +2481,8 @@ SpellingCache = {
# 20: CompletionChunk.Kind("VerticalSpace")
}
-class CompletionChunk:
- class Kind:
+class CompletionChunk(object):
+ class Kind(object):
def __init__(self, name):
self.name = name
@@ -2548,7 +2569,7 @@ completionChunkKindMap = {
20: CompletionChunk.Kind("VerticalSpace")}
class CompletionString(ClangObject):
- class Availability:
+ class Availability(object):
def __init__(self, name):
self.name = name
@@ -2641,7 +2662,7 @@ class CodeCompletionResults(ClangObject):
@property
def diagnostics(self):
- class DiagnosticsItr:
+ class DiagnosticsItr(object):
def __init__(self, ccr):
self.ccr= ccr
@@ -2746,11 +2767,11 @@ class TranslationUnit(ClangObject):
etc. e.g. ["-Wall", "-I/path/to/include"].
In-memory file content can be provided via unsaved_files. This is an
- iterable of 2-tuples. The first element is the str filename. The
- second element defines the content. Content can be provided as str
- source code or as file objects (anything with a read() method). If
- a file object is being used, content will be read until EOF and the
- read cursor will not be reset to its original position.
+ iterable of 2-tuples. The first element is the filename (str or
+ PathLike). The second element defines the content. Content can be
+ provided as str source code or as file objects (anything with a read()
+ method). If a file object is being used, content will be read until EOF
+ and the read cursor will not be reset to its original position.
options is a bitwise or of TranslationUnit.PARSE_XXX flags which will
control parsing behavior.
@@ -2795,11 +2816,13 @@ class TranslationUnit(ClangObject):
if hasattr(contents, "read"):
contents = contents.read()
- unsaved_array[i].name = b(name)
+ unsaved_array[i].name = b(fspath(name))
unsaved_array[i].contents = b(contents)
unsaved_array[i].length = len(contents)
- ptr = conf.lib.clang_parseTranslationUnit(index, filename, args_array,
+ ptr = conf.lib.clang_parseTranslationUnit(index,
+ fspath(filename) if filename is not None else None,
+ args_array,
len(args), unsaved_array,
len(unsaved_files), options)
@@ -2820,11 +2843,13 @@ class TranslationUnit(ClangObject):
index is optional and is the Index instance to use. If not provided,
a default Index will be created.
+
+ filename can be str or PathLike.
"""
if index is None:
index = Index.create()
- ptr = conf.lib.clang_createTranslationUnit(index, filename)
+ ptr = conf.lib.clang_createTranslationUnit(index, fspath(filename))
if not ptr:
raise TranslationUnitLoadError(filename)
@@ -2939,7 +2964,7 @@ class TranslationUnit(ClangObject):
"""
Return an iterable (and indexable) object containing the diagnostics.
"""
- class DiagIterator:
+ class DiagIterator(object):
def __init__(self, tu):
self.tu = tu
@@ -2977,7 +3002,7 @@ class TranslationUnit(ClangObject):
print(value)
if not isinstance(value, str):
raise TypeError('Unexpected unsaved file contents.')
- unsaved_files_array[i].name = name
+ unsaved_files_array[i].name = fspath(name)
unsaved_files_array[i].contents = value
unsaved_files_array[i].length = len(value)
ptr = conf.lib.clang_reparseTranslationUnit(self, len(unsaved_files),
@@ -2996,10 +3021,10 @@ class TranslationUnit(ClangObject):
case, the reason(s) why should be available via
TranslationUnit.diagnostics().
- filename -- The path to save the translation unit to.
+ filename -- The path to save the translation unit to (str or PathLike).
"""
options = conf.lib.clang_defaultSaveOptions(self)
- result = int(conf.lib.clang_saveTranslationUnit(self, filename,
+ result = int(conf.lib.clang_saveTranslationUnit(self, fspath(filename),
options))
if result != 0:
raise TranslationUnitSaveError(result,
@@ -3041,10 +3066,10 @@ class TranslationUnit(ClangObject):
print(value)
if not isinstance(value, str):
raise TypeError('Unexpected unsaved file contents.')
- unsaved_files_array[i].name = b(name)
+ unsaved_files_array[i].name = b(fspath(name))
unsaved_files_array[i].contents = b(value)
unsaved_files_array[i].length = len(value)
- ptr = conf.lib.clang_codeCompleteAt(self, path, line, column,
+ ptr = conf.lib.clang_codeCompleteAt(self, fspath(path), line, column,
unsaved_files_array, len(unsaved_files), options)
if ptr:
return CodeCompletionResults(ptr)
@@ -3072,7 +3097,7 @@ class File(ClangObject):
@staticmethod
def from_name(translation_unit, file_name):
"""Retrieve a file handle within the given translation unit."""
- return File(conf.lib.clang_getFile(translation_unit, file_name))
+ return File(conf.lib.clang_getFile(translation_unit, fspath(file_name)))
@property
def name(self):
@@ -3171,7 +3196,7 @@ class CompileCommand(object):
Invariant : the first argument is the compiler executable
"""
length = conf.lib.clang_CompileCommand_getNumArgs(self.cmd)
- for i in xrange(length):
+ for i in range(length):
yield conf.lib.clang_CompileCommand_getArg(self.cmd, i)
class CompileCommands(object):
@@ -3223,7 +3248,7 @@ class CompilationDatabase(ClangObject):
"""Builds a CompilationDatabase from the database found in buildDir"""
errorCode = c_uint()
try:
- cdb = conf.lib.clang_CompilationDatabase_fromDirectory(buildDir,
+ cdb = conf.lib.clang_CompilationDatabase_fromDirectory(fspath(buildDir),
byref(errorCode))
except CompilationDatabaseError as e:
raise CompilationDatabaseError(int(errorCode.value),
@@ -3236,7 +3261,7 @@ class CompilationDatabase(ClangObject):
build filename. Returns None if filename is not found in the database.
"""
return conf.lib.clang_CompilationDatabase_getCompileCommands(self,
- filename)
+ fspath(filename))
def getAllCompileCommands(self):
"""
@@ -3999,6 +4024,15 @@ functionList = [
Type,
Type.from_result),
+ ("clang_Type_getNumTemplateArguments",
+ [Type],
+ c_int),
+
+ ("clang_Type_getTemplateArgumentAsType",
+ [Type, c_uint],
+ Type,
+ Type.from_result),
+
("clang_Type_getOffsetOf",
[Type, c_interop_string],
c_longlong),
@@ -4062,7 +4096,7 @@ def register_functions(lib, ignore_errors):
for f in functionList:
register(f)
-class Config:
+class Config(object):
library_path = None
library_file = None
compatibility_check = True
@@ -4075,7 +4109,7 @@ class Config:
raise Exception("library path must be set before before using " \
"any other functionalities in libclang.")
- Config.library_path = path
+ Config.library_path = fspath(path)
@staticmethod
def set_library_file(filename):
@@ -4084,7 +4118,7 @@ class Config:
raise Exception("library file must be set before before using " \
"any other functionalities in libclang.")
- Config.library_file = filename
+ Config.library_file = fspath(filename)
@staticmethod
def set_compatibility_check(check_status):
diff --git a/bindings/python/examples/cindex/cindex-dump.py b/bindings/python/examples/cindex/cindex-dump.py
index 5556ad121a3e..acec7e0e0054 100644
--- a/bindings/python/examples/cindex/cindex-dump.py
+++ b/bindings/python/examples/cindex/cindex-dump.py
@@ -79,7 +79,7 @@ def main():
if not tu:
parser.error("unable to load input")
- pprint(('diags', map(get_diag_info, tu.diagnostics)))
+ pprint(('diags', [get_diag_info(d) for d in tu.diagnostics]))
pprint(('nodes', get_info(tu.cursor)))
if __name__ == '__main__':
diff --git a/bindings/python/tests/CMakeLists.txt b/bindings/python/tests/CMakeLists.txt
new file mode 100644
index 000000000000..7af6503f1588
--- /dev/null
+++ b/bindings/python/tests/CMakeLists.txt
@@ -0,0 +1,46 @@
+# Test target to run Python test suite from main build.
+
+add_custom_target(check-clang-python
+ COMMAND ${CMAKE_COMMAND} -E env
+ CLANG_LIBRARY_PATH=$<TARGET_FILE_DIR:libclang>
+ ${PYTHON_EXECUTABLE} -m unittest discover
+ DEPENDS libclang
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+set(RUN_PYTHON_TESTS TRUE)
+set_target_properties(check-clang-python PROPERTIES FOLDER "Clang tests")
+
+# Tests require libclang.so which is only built with LLVM_ENABLE_PIC=ON
+if(NOT LLVM_ENABLE_PIC)
+ set(RUN_PYTHON_TESTS FALSE)
+endif()
+
+# Do not try to run if libclang was built with ASan because
+# the sanitizer library will likely be loaded too late to perform
+# interception and will then fail.
+# We could use LD_PRELOAD/DYLD_INSERT_LIBRARIES but this isn't
+# portable so its easier just to not run the tests when building
+# with ASan.
+list(FIND LLVM_USE_SANITIZER "Address" LLVM_USE_ASAN_INDEX)
+if(NOT LLVM_USE_ASAN_INDEX EQUAL -1)
+ set(RUN_PYTHON_TESTS FALSE)
+endif()
+
+# Tests fail on Windows, and need someone knowledgeable to fix.
+# It's not clear whether it's a test or a valid binding problem.
+if(WIN32)
+ set(RUN_PYTHON_TESTS FALSE)
+endif()
+
+# AArch64 and Hexagon have known test failures that need to be
+# addressed.
+# SystemZ has broken Python/FFI interface:
+# https://reviews.llvm.org/D52840#1265716
+if(${LLVM_NATIVE_ARCH} MATCHES "^(AArch64|Hexagon|SystemZ)$")
+ set(RUN_PYTHON_TESTS FALSE)
+endif()
+
+if(RUN_PYTHON_TESTS)
+ set_property(GLOBAL APPEND PROPERTY
+ LLVM_ADDITIONAL_TEST_TARGETS check-clang-python)
+endif()
diff --git a/bindings/python/tests/cindex/test_access_specifiers.py b/bindings/python/tests/cindex/test_access_specifiers.py
index 2f6144be082c..e36424f240aa 100644
--- a/bindings/python/tests/cindex/test_access_specifiers.py
+++ b/bindings/python/tests/cindex/test_access_specifiers.py
@@ -1,3 +1,7 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
from clang.cindex import AccessSpecifier
from clang.cindex import Cursor
diff --git a/bindings/python/tests/cindex/test_cdb.py b/bindings/python/tests/cindex/test_cdb.py
index 64651af31732..589fc72856bd 100644
--- a/bindings/python/tests/cindex/test_cdb.py
+++ b/bindings/python/tests/cindex/test_cdb.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import CompilationDatabase
from clang.cindex import CompilationDatabaseError
from clang.cindex import CompileCommands
@@ -6,6 +11,8 @@ import os
import gc
import unittest
import sys
+from .util import skip_if_no_fspath
+from .util import str_to_path
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
@@ -26,17 +33,19 @@ class TestCDB(unittest.TestCase):
"""Check we can load a compilation database"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
- def test_lookup_fail(self):
- """Check file lookup failure"""
- cdb = CompilationDatabase.fromDirectory(kInputsDir)
- self.assertIsNone(cdb.getCompileCommands('file_do_not_exist.cpp'))
-
def test_lookup_succeed(self):
"""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')
self.assertNotEqual(len(cmds), 0)
+ @skip_if_no_fspath
+ def test_lookup_succeed_pathlike(self):
+ """Same as test_lookup_succeed, but with PathLikes"""
+ cdb = CompilationDatabase.fromDirectory(str_to_path(kInputsDir))
+ cmds = cdb.getCompileCommands(str_to_path('/home/john.doe/MyProject/project.cpp'))
+ self.assertNotEqual(len(cmds), 0)
+
def test_all_compilecommand(self):
"""Check we get all results from the db"""
cdb = CompilationDatabase.fromDirectory(kInputsDir)
diff --git a/bindings/python/tests/cindex/test_code_completion.py b/bindings/python/tests/cindex/test_code_completion.py
index a56bb304cd71..e0b41577aeb3 100644
--- a/bindings/python/tests/cindex/test_code_completion.py
+++ b/bindings/python/tests/cindex/test_code_completion.py
@@ -1,6 +1,13 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import TranslationUnit
import unittest
+from .util import skip_if_no_fspath
+from .util import str_to_path
class TestCodeCompletion(unittest.TestCase):
@@ -38,6 +45,32 @@ void f() {
]
self.check_completion_results(cr, expected)
+ @skip_if_no_fspath
+ def test_code_complete_pathlike(self):
+ files = [(str_to_path('fake.c'), """
+/// Aaa.
+int test1;
+
+/// Bbb.
+void test2(void);
+
+void f() {
+
+}
+""")]
+
+ tu = TranslationUnit.from_source(str_to_path('fake.c'), ['-std=c99'], unsaved_files=files,
+ options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
+
+ cr = tu.codeComplete(str_to_path('fake.c'), 9, 1, unsaved_files=files, include_brief_comments=True)
+
+ expected = [
+ "{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
+ "{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
+ "{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
+ ]
+ self.check_completion_results(cr, expected)
+
def test_code_complete_availability(self):
files = [('fake.cpp', """
class P {
@@ -61,11 +94,11 @@ void f(P x, Q y) {
cr = tu.codeComplete('fake.cpp', 12, 5, unsaved_files=files)
expected = [
- "{'const', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
- "{'volatile', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
+ "{'const', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
+ "{'volatile', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
"{'operator', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
- "{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
- "{'Q', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None"
+ "{'P', TypedText} || Priority: 50 || Availability: Available || Brief comment: None",
+ "{'Q', TypedText} || Priority: 50 || Availability: Available || Brief comment: None"
]
self.check_completion_results(cr, expected)
diff --git a/bindings/python/tests/cindex/test_comment.py b/bindings/python/tests/cindex/test_comment.py
index d6c6d8e5c5b0..73fb617ae168 100644
--- a/bindings/python/tests/cindex/test_comment.py
+++ b/bindings/python/tests/cindex/test_comment.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import TranslationUnit
from tests.cindex.util import get_cursor
diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py
index f5733fd15879..ef875e972474 100644
--- a/bindings/python/tests/cindex/test_cursor.py
+++ b/bindings/python/tests/cindex/test_cursor.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
import ctypes
import gc
import unittest
diff --git a/bindings/python/tests/cindex/test_cursor_kind.py b/bindings/python/tests/cindex/test_cursor_kind.py
index f1ee753ef8b1..e6b9558b3cc1 100644
--- a/bindings/python/tests/cindex/test_cursor_kind.py
+++ b/bindings/python/tests/cindex/test_cursor_kind.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import CursorKind
import unittest
diff --git a/bindings/python/tests/cindex/test_diagnostics.py b/bindings/python/tests/cindex/test_diagnostics.py
index 78b327daa72c..c17d5b28efe9 100644
--- a/bindings/python/tests/cindex/test_diagnostics.py
+++ b/bindings/python/tests/cindex/test_diagnostics.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import *
from .util import get_tu
@@ -46,7 +51,7 @@ class TestDiagnostics(unittest.TestCase):
self.assertEqual(tu.diagnostics[0].fixits[0].value, '.f0 = ')
def test_diagnostic_range(self):
- tu = get_tu('void f() { int i = "a" + 1; }')
+ tu = get_tu('void f() { int i = "a"; }')
self.assertEqual(len(tu.diagnostics), 1)
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
self.assertEqual(tu.diagnostics[0].location.line, 1)
@@ -58,7 +63,7 @@ class TestDiagnostics(unittest.TestCase):
self.assertEqual(tu.diagnostics[0].ranges[0].start.line, 1)
self.assertEqual(tu.diagnostics[0].ranges[0].start.column, 20)
self.assertEqual(tu.diagnostics[0].ranges[0].end.line, 1)
- self.assertEqual(tu.diagnostics[0].ranges[0].end.column, 27)
+ self.assertEqual(tu.diagnostics[0].ranges[0].end.column, 23)
with self.assertRaises(IndexError):
tu.diagnostics[0].ranges[1].start.line
diff --git a/bindings/python/tests/cindex/test_exception_specification_kind.py b/bindings/python/tests/cindex/test_exception_specification_kind.py
index 80b3639a8ab3..6c13f70fb256 100644
--- a/bindings/python/tests/cindex/test_exception_specification_kind.py
+++ b/bindings/python/tests/cindex/test_exception_specification_kind.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
import clang.cindex
from clang.cindex import ExceptionSpecificationKind
from .util import get_tu
diff --git a/bindings/python/tests/cindex/test_file.py b/bindings/python/tests/cindex/test_file.py
index 98f6575262cc..a146fe5c9239 100644
--- a/bindings/python/tests/cindex/test_file.py
+++ b/bindings/python/tests/cindex/test_file.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import Index, File
import unittest
diff --git a/bindings/python/tests/cindex/test_index.py b/bindings/python/tests/cindex/test_index.py
index cfdf98e628ac..46aafcf222e7 100644
--- a/bindings/python/tests/cindex/test_index.py
+++ b/bindings/python/tests/cindex/test_index.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import *
import os
import unittest
diff --git a/bindings/python/tests/cindex/test_linkage.py b/bindings/python/tests/cindex/test_linkage.py
index 6b482f8081b3..cdd97fc2df0d 100644
--- a/bindings/python/tests/cindex/test_linkage.py
+++ b/bindings/python/tests/cindex/test_linkage.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import LinkageKind
from clang.cindex import Cursor
from clang.cindex import TranslationUnit
diff --git a/bindings/python/tests/cindex/test_location.py b/bindings/python/tests/cindex/test_location.py
index cbc32deb4bd5..fbe9770d7ebc 100644
--- a/bindings/python/tests/cindex/test_location.py
+++ b/bindings/python/tests/cindex/test_location.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import Cursor
from clang.cindex import File
from clang.cindex import SourceLocation
diff --git a/bindings/python/tests/cindex/test_tls_kind.py b/bindings/python/tests/cindex/test_tls_kind.py
index fbc3418a64ef..c828ac83a468 100644
--- a/bindings/python/tests/cindex/test_tls_kind.py
+++ b/bindings/python/tests/cindex/test_tls_kind.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import TLSKind
from clang.cindex import Cursor
from clang.cindex import TranslationUnit
diff --git a/bindings/python/tests/cindex/test_token_kind.py b/bindings/python/tests/cindex/test_token_kind.py
index 700f95a64624..904e007cafc5 100644
--- a/bindings/python/tests/cindex/test_token_kind.py
+++ b/bindings/python/tests/cindex/test_token_kind.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import TokenKind
import unittest
diff --git a/bindings/python/tests/cindex/test_tokens.py b/bindings/python/tests/cindex/test_tokens.py
index c93353dc9da2..dd6d3a3259ed 100644
--- a/bindings/python/tests/cindex/test_tokens.py
+++ b/bindings/python/tests/cindex/test_tokens.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from clang.cindex import CursorKind
from clang.cindex import Index
from clang.cindex import SourceLocation
diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py
index d3ee535f4d0d..f3e770a93611 100644
--- a/bindings/python/tests/cindex/test_translation_unit.py
+++ b/bindings/python/tests/cindex/test_translation_unit.py
@@ -1,6 +1,12 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
from contextlib import contextmanager
import gc
import os
+import sys
import tempfile
import unittest
@@ -15,6 +21,8 @@ from clang.cindex import TranslationUnitLoadError
from clang.cindex import TranslationUnit
from .util import get_cursor
from .util import get_tu
+from .util import skip_if_no_fspath
+from .util import str_to_path
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
@@ -31,6 +39,17 @@ def save_tu(tu):
yield t.name
+@contextmanager
+def save_tu_pathlike(tu):
+ """Convenience API to save a TranslationUnit to a file.
+
+ Returns the filename it was saved to.
+ """
+ with tempfile.NamedTemporaryFile() as t:
+ tu.save(str_to_path(t.name))
+ yield t.name
+
+
class TestTranslationUnit(unittest.TestCase):
def test_spelling(self):
path = os.path.join(kInputsDir, 'hello.cpp')
@@ -75,15 +94,31 @@ int SOME_DEFINE;
self.assertEqual(spellings[-1], 'y')
def test_unsaved_files_2(self):
- try:
- from StringIO import StringIO
- except:
+ if sys.version_info.major >= 3:
from io import StringIO
+ else:
+ from io import BytesIO as StringIO
tu = TranslationUnit.from_source('fake.c', unsaved_files = [
('fake.c', StringIO('int x;'))])
spellings = [c.spelling for c in tu.cursor.get_children()]
self.assertEqual(spellings[-1], 'x')
+ @skip_if_no_fspath
+ def test_from_source_accepts_pathlike(self):
+ tu = TranslationUnit.from_source(str_to_path('fake.c'), ['-Iincludes'], unsaved_files = [
+ (str_to_path('fake.c'), """
+#include "fake.h"
+ int x;
+ int SOME_DEFINE;
+ """),
+ (str_to_path('includes/fake.h'), """
+#define SOME_DEFINE y
+ """)
+ ])
+ spellings = [c.spelling for c in tu.cursor.get_children()]
+ self.assertEqual(spellings[-2], 'x')
+ self.assertEqual(spellings[-1], 'y')
+
def assert_normpaths_equal(self, path1, path2):
""" Compares two paths for equality after normalizing them with
os.path.normpath
@@ -130,6 +165,16 @@ int SOME_DEFINE;
self.assertTrue(os.path.exists(path))
self.assertGreater(os.path.getsize(path), 0)
+ @skip_if_no_fspath
+ def test_save_pathlike(self):
+ """Ensure TranslationUnit.save() works with PathLike filename."""
+
+ tu = get_tu('int foo();')
+
+ with save_tu_pathlike(tu) as path:
+ self.assertTrue(os.path.exists(path))
+ self.assertGreater(os.path.getsize(path), 0)
+
def test_save_translation_errors(self):
"""Ensure that saving to an invalid directory raises."""
@@ -162,6 +207,22 @@ int SOME_DEFINE;
# Just in case there is an open file descriptor somewhere.
del tu2
+ @skip_if_no_fspath
+ def test_load_pathlike(self):
+ """Ensure TranslationUnits can be constructed from saved files -
+ PathLike variant."""
+ tu = get_tu('int foo();')
+ self.assertEqual(len(tu.diagnostics), 0)
+ with save_tu(tu) as path:
+ tu2 = TranslationUnit.from_ast_file(filename=str_to_path(path))
+ self.assertEqual(len(tu2.diagnostics), 0)
+
+ foo = get_cursor(tu2, 'foo')
+ self.assertIsNotNone(foo)
+
+ # Just in case there is an open file descriptor somewhere.
+ del tu2
+
def test_index_parse(self):
path = os.path.join(kInputsDir, 'hello.cpp')
index = Index.create()
@@ -180,6 +241,19 @@ int SOME_DEFINE;
with self.assertRaises(Exception):
f = tu.get_file('foobar.cpp')
+ @skip_if_no_fspath
+ def test_get_file_pathlike(self):
+ """Ensure tu.get_file() works appropriately with PathLike filenames."""
+
+ tu = get_tu('int foo();')
+
+ f = tu.get_file(str_to_path('t.c'))
+ self.assertIsInstance(f, File)
+ self.assertEqual(f.name, 't.c')
+
+ with self.assertRaises(Exception):
+ f = tu.get_file(str_to_path('foobar.cpp'))
+
def test_get_source_location(self):
"""Ensure tu.get_source_location() works."""
diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py
index 4dec0583c7b0..bcdbeff9d99c 100644
--- a/bindings/python/tests/cindex/test_type.py
+++ b/bindings/python/tests/cindex/test_type.py
@@ -1,3 +1,8 @@
+import os
+from clang.cindex import Config
+if 'CLANG_LIBRARY_PATH' in os.environ:
+ Config.set_library_path(os.environ['CLANG_LIBRARY_PATH'])
+
import gc
import unittest
@@ -436,3 +441,28 @@ class TestType(unittest.TestCase):
self.assertIsNotNone(testInteger, "Could not find testInteger.")
self.assertEqual(testInteger.type.get_address_space(), 2)
+
+ def test_template_arguments(self):
+ source = """
+ class Foo {
+ };
+ template <typename T>
+ class Template {
+ };
+ Template<Foo> instance;
+ int bar;
+ """
+ tu = get_tu(source, lang='cpp')
+
+ # Varible with a template argument.
+ cursor = get_cursor(tu, 'instance')
+ cursor_type = cursor.type
+ self.assertEqual(cursor.kind, CursorKind.VAR_DECL)
+ self.assertEqual(cursor_type.spelling, 'Template<Foo>')
+ self.assertEqual(cursor_type.get_num_template_arguments(), 1)
+ template_type = cursor_type.get_template_argument_type(0)
+ self.assertEqual(template_type.spelling, 'Foo')
+
+ # Variable without a template argument.
+ cursor = get_cursor(tu, 'bar')
+ self.assertEqual(cursor.get_num_template_arguments(), -1)
diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py
index c53ba7c81bde..57e17941c558 100644
--- a/bindings/python/tests/cindex/util.py
+++ b/bindings/python/tests/cindex/util.py
@@ -1,5 +1,15 @@
# This file provides common utility functions for the test suite.
+import os
+HAS_FSPATH = hasattr(os, 'fspath')
+
+if HAS_FSPATH:
+ from pathlib import Path as str_to_path
+else:
+ str_to_path = None
+
+import unittest
+
from clang.cindex import Cursor
from clang.cindex import TranslationUnit
@@ -68,8 +78,13 @@ def get_cursors(source, spelling):
return cursors
+skip_if_no_fspath = unittest.skipUnless(HAS_FSPATH,
+ "Requires file system path protocol / Python 3.6+")
+
__all__ = [
'get_cursor',
'get_cursors',
'get_tu',
+ 'skip_if_no_fspath',
+ 'str_to_path',
]