diff options
Diffstat (limited to 'bindings/python')
-rw-r--r-- | bindings/python/clang/cindex.py | 375 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_cursor.py | 37 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_cursor_kind.py | 9 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_diagnostics.py | 39 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_file.py | 9 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_location.py | 100 | ||||
-rw-r--r-- | bindings/python/tests/cindex/test_type.py | 294 | ||||
-rw-r--r-- | bindings/python/tests/cindex/util.py | 65 |
8 files changed, 794 insertions, 134 deletions
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index 7cc9ddaf76467..b4563eb0c6240 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -63,6 +63,7 @@ call is efficient. # o implement additional SourceLocation, SourceRange, and File methods. from ctypes import * +import collections def get_cindex_library(): # FIXME: It's probably not the case that the library is actually found in @@ -111,10 +112,21 @@ class SourceLocation(Structure): if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() SourceLocation_loc(self, byref(f), byref(l), byref(c), byref(o)) - f = File(f) if f else None + if f: + f = File(f) + else: + f = None self._data = (f, int(l.value), int(c.value), int(o.value)) return self._data + @staticmethod + def from_position(tu, file, line, column): + """ + Retrieve the source location associated with a given file/line/column in + a particular translation unit. + """ + return SourceLocation_getLocation(tu, file, line, column) + @property def file(self): """Get the file represented by this source location.""" @@ -135,9 +147,19 @@ class SourceLocation(Structure): """Get the file offset represented by this source location.""" return self._get_instantiation()[3] + def __eq__(self, other): + return SourceLocation_equalLocations(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + def __repr__(self): + if self.file: + filename = self.file.name + else: + filename = None return "<SourceLocation file %r, line %r, column %r>" % ( - self.file.name if self.file else None, self.line, self.column) + filename, self.line, self.column) class SourceRange(Structure): """ @@ -171,6 +193,12 @@ class SourceRange(Structure): """ return SourceRange_end(self) + def __eq__(self, other): + return SourceRange_equalRanges(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + def __repr__(self): return "<SourceRange start %r, end %r>" % (self.start, self.end) @@ -215,8 +243,8 @@ class Diagnostic(object): return int(_clang_getDiagnosticNumRanges(self.diag)) def __getitem__(self, key): - if (key >= len(self)): - raise IndexError + if (key >= len(self)): + raise IndexError return _clang_getDiagnosticRange(self.diag, key) return RangeIterator(self) @@ -240,6 +268,29 @@ class Diagnostic(object): return FixItIterator(self) + @property + def category_number(self): + """The category number for this diagnostic.""" + return _clang_getDiagnosticCategory(self) + + @property + def category_name(self): + """The string name of the category for this diagnostic.""" + return _clang_getDiagnosticCategoryName(self.category_number) + + @property + def option(self): + """The command-line option that enables this diagnostic.""" + return _clang_getDiagnosticOption(self, None) + + @property + def disable_option(self): + """The command-line option that disables this diagnostic.""" + disable = _CXString() + _clang_getDiagnosticOption(self, byref(disable)) + + return _CXString_getCString(disable) + def __repr__(self): return "<Diagnostic severity %r, location %r, spelling %r>" % ( self.severity, self.location, self.spelling) @@ -329,6 +380,18 @@ class CursorKind(object): """Test if this is an invalid kind.""" return CursorKind_is_inv(self) + def is_translation_unit(self): + """Test if this is a translation unit kind.""" + return CursorKind_is_translation_unit(self) + + def is_preprocessing(self): + """Test if this is a preprocessing kind.""" + return CursorKind_is_preprocessing(self) + + def is_unexposed(self): + """Test if this is an unexposed kind.""" + return CursorKind_is_unexposed(self) + def __repr__(self): return 'CursorKind.%s' % (self.name,) @@ -592,7 +655,7 @@ CursorKind.ADDR_LABEL_EXPR = CursorKind(120) # This is the GNU Statement Expression extension: ({int X=4; X;}) CursorKind.StmtExpr = CursorKind(121) -# Represents a C1X generic selection. +# Represents a C11 generic selection. CursorKind.GENERIC_SELECTION_EXPR = CursorKind(122) # Implements the GNU __null extension, which is a name for a null @@ -801,6 +864,11 @@ CursorKind.IB_ACTION_ATTR = CursorKind(401) CursorKind.IB_OUTLET_ATTR = CursorKind(402) CursorKind.IB_OUTLET_COLLECTION_ATTR = CursorKind(403) +CursorKind.CXX_FINAL_ATTR = CursorKind(404) +CursorKind.CXX_OVERRIDE_ATTR = CursorKind(405) +CursorKind.ANNOTATE_ATTR = CursorKind(406) +CursorKind.ASM_LABEL_ATTR = CursorKind(407) + ### # Preprocessing CursorKind.PREPROCESSING_DIRECTIVE = CursorKind(500) @@ -817,11 +885,15 @@ class Cursor(Structure): """ _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)] + @staticmethod + def from_location(tu, location): + return Cursor_get(tu, location) + def __eq__(self, other): return Cursor_eq(self, other) def __ne__(self, other): - return not Cursor_eq(self, other) + return not self.__eq__(other) def is_definition(self): """ @@ -903,13 +975,54 @@ class Cursor(Structure): @property def type(self): """ - Retrieve the type (if any) of of the entity pointed at by the - cursor. + Retrieve the Type (if any) of the entity pointed at by the cursor. """ if not hasattr(self, '_type'): self._type = Cursor_type(self) return self._type + @property + def underlying_typedef_type(self): + """Return the underlying type of a typedef declaration. + + Returns a Type for the typedef this cursor is a declaration for. If + the current cursor is not a typedef, this raises. + """ + if not hasattr(self, '_underlying_type'): + assert self.kind.is_declaration() + self._underlying_type = Cursor_underlying_type(self) + + return self._underlying_type + + @property + def enum_type(self): + """Return the integer type of an enum declaration. + + Returns a Type corresponding to an integer. If the cursor is not for an + enum, this raises. + """ + if not hasattr(self, '_enum_type'): + assert self.kind == CursorKind.ENUM_DECL + self._enum_type = Cursor_enum_type(self) + + return self._enum_type + + @property + def objc_type_encoding(self): + """Return the Objective-C type encoding as a str.""" + if not hasattr(self, '_objc_type_encoding'): + self._objc_type_encoding = Cursor_objc_type_encoding(self) + + return self._objc_type_encoding + + @property + def hash(self): + """Returns a hash of the cursor as an int.""" + if not hasattr(self, '_hash'): + self._hash = Cursor_hash(self) + + return self._hash + def get_children(self): """Return an iterator for accessing the children of this cursor.""" @@ -969,7 +1082,7 @@ class TypeKind(object): @staticmethod def from_id(id): if id >= len(TypeKind._kinds) or TypeKind._kinds[id] is None: - raise ValueError,'Unknown cursor kind' + raise ValueError,'Unknown type kind %d' % id return TypeKind._kinds[id] def __repr__(self): @@ -1020,6 +1133,7 @@ TypeKind.OBJCOBJECTPOINTER = TypeKind(109) TypeKind.FUNCTIONNOPROTO = TypeKind(110) TypeKind.FUNCTIONPROTO = TypeKind(111) TypeKind.CONSTANTARRAY = TypeKind(112) +TypeKind.VECTOR = TypeKind(113) class Type(Structure): """ @@ -1032,6 +1146,71 @@ class Type(Structure): """Return the kind of this type.""" return TypeKind.from_id(self._kind_id) + def argument_types(self): + """Retrieve a container for the non-variadic arguments for this type. + + The returned object is iterable and indexable. Each item in the + container is a Type instance. + """ + class ArgumentsIterator(collections.Sequence): + def __init__(self, parent): + self.parent = parent + self.length = None + + def __len__(self): + if self.length is None: + self.length = Type_get_num_arg_types(self.parent) + + return self.length + + def __getitem__(self, key): + # FIXME Support slice objects. + if not isinstance(key, int): + raise TypeError("Must supply a non-negative int.") + + if key < 0: + raise IndexError("Only non-negative indexes are accepted.") + + if key >= len(self): + raise IndexError("Index greater than container length: " + "%d > %d" % ( key, len(self) )) + + result = Type_get_arg_type(self.parent, key) + if result.kind == TypeKind.INVALID: + raise IndexError("Argument could not be retrieved.") + + return result + + assert self.kind == TypeKind.FUNCTIONPROTO + return ArgumentsIterator(self) + + @property + def element_type(self): + """Retrieve the Type of elements within this Type. + + If accessed on a type that is not an array, complex, or vector type, an + exception will be raised. + """ + result = Type_get_element_type(self) + if result.kind == TypeKind.INVALID: + raise Exception('Element type not available on this type.') + + return result + + @property + def element_count(self): + """Retrieve the number of elements in this type. + + Returns an int. + + If the Type is not an array or vector, this raises. + """ + result = Type_get_num_elements(self) + if result < 0: + raise Exception('Type does not have elements.') + + return result + @staticmethod def from_result(res, fn, args): assert isinstance(res, Type) @@ -1050,29 +1229,39 @@ class Type(Structure): return Type_get_canonical(self) def is_const_qualified(self): - """ - Determine whether a Type has the "const" qualifier set, - without looking through typedefs that may have added "const" + """Determine whether a Type has the "const" qualifier set. + + This does not look through typedefs that may have added "const" at a different level. """ return Type_is_const_qualified(self) def is_volatile_qualified(self): - """ - Determine whether a Type has the "volatile" qualifier set, - without looking through typedefs that may have added - "volatile" at a different level. + """Determine whether a Type has the "volatile" qualifier set. + + This does not look through typedefs that may have added "volatile" + at a different level. """ return Type_is_volatile_qualified(self) def is_restrict_qualified(self): - """ - Determine whether a Type has the "restrict" qualifier set, - without looking through typedefs that may have added - "restrict" at a different level. + """Determine whether a Type has the "restrict" qualifier set. + + This does not look through typedefs that may have added "restrict" at + a different level. """ return Type_is_restrict_qualified(self) + def is_function_variadic(self): + """Determine whether this function Type is a variadic function type.""" + assert self.kind == TypeKind.FUNCTIONPROTO + + return Type_is_variadic(self) + + def is_pod(self): + """Determine whether this Type represents plain old data (POD).""" + return Type_is_pod(self) + def get_pointee(self): """ For pointer types, returns the type of the pointee. @@ -1091,6 +1280,27 @@ class Type(Structure): """ return Type_get_result(self) + def get_array_element_type(self): + """ + Retrieve the type of the elements of the array type. + """ + return Type_get_array_element(self) + + def get_array_size(self): + """ + Retrieve the size of the constant array. + """ + return Type_get_array_size(self) + + def __eq__(self, other): + if type(other) != type(self): + return False + + return Type_equal(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + ## CIndex Objects ## # CIndex objects (derived from ClangObject) are essentially lightweight @@ -1157,6 +1367,20 @@ _clang_getDiagnosticFixIt.argtypes = [Diagnostic, c_uint, POINTER(SourceRange)] _clang_getDiagnosticFixIt.restype = _CXString _clang_getDiagnosticFixIt.errcheck = _CXString.from_result +_clang_getDiagnosticCategory = lib.clang_getDiagnosticCategory +_clang_getDiagnosticCategory.argtypes = [Diagnostic] +_clang_getDiagnosticCategory.restype = c_uint + +_clang_getDiagnosticCategoryName = lib.clang_getDiagnosticCategoryName +_clang_getDiagnosticCategoryName.argtypes = [c_uint] +_clang_getDiagnosticCategoryName.restype = _CXString +_clang_getDiagnosticCategoryName.errcheck = _CXString.from_result + +_clang_getDiagnosticOption = lib.clang_getDiagnosticOption +_clang_getDiagnosticOption.argtypes = [Diagnostic, POINTER(_CXString)] +_clang_getDiagnosticOption.restype = _CXString +_clang_getDiagnosticOption.errcheck = _CXString.from_result + ### class CompletionChunk: @@ -1350,7 +1574,9 @@ class Index(ClangObject): def read(self, path): """Load the translation unit from the given AST file.""" ptr = TranslationUnit_read(self, path) - return TranslationUnit(ptr) if ptr else None + if ptr: + return TranslationUnit(ptr) + return None def parse(self, path, args = [], unsaved_files = [], options = 0): """ @@ -1383,7 +1609,9 @@ class Index(ClangObject): ptr = TranslationUnit_parse(self, path, arg_array, len(args), unsaved_files_array, len(unsaved_files), options) - return TranslationUnit(ptr) if ptr else None + if ptr: + return TranslationUnit(ptr) + return None class TranslationUnit(ClangObject): @@ -1502,8 +1730,9 @@ class TranslationUnit(ClangObject): unsaved_files_array, len(unsaved_files), options) - return CodeCompletionResults(ptr) if ptr else None - + if ptr: + return CodeCompletionResults(ptr) + return None class File(ClangObject): """ @@ -1511,6 +1740,11 @@ class File(ClangObject): translation unit. """ + @staticmethod + def from_name(translation_unit, file_name): + """Retrieve a file handle within the given translation unit.""" + return File(File_getFile(translation_unit, file_name)) + @property def name(self): """Return the complete file and path name of the file.""" @@ -1521,6 +1755,12 @@ class File(ClangObject): """Return the last modification time of the file.""" return File_time(self) + def __str__(self): + return self.name + + def __repr__(self): + return "<File: %s>" % (self.name) + class FileInclusion(object): """ The FileInclusion class represents the inclusion of one source file by @@ -1557,6 +1797,14 @@ SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint)] +SourceLocation_getLocation = lib.clang_getLocation +SourceLocation_getLocation.argtypes = [TranslationUnit, File, c_uint, c_uint] +SourceLocation_getLocation.restype = SourceLocation + +SourceLocation_equalLocations = lib.clang_equalLocations +SourceLocation_equalLocations.argtypes = [SourceLocation, SourceLocation] +SourceLocation_equalLocations.restype = bool + # Source Range Functions SourceRange_getRange = lib.clang_getRange SourceRange_getRange.argtypes = [SourceLocation, SourceLocation] @@ -1570,6 +1818,10 @@ SourceRange_end = lib.clang_getRangeEnd SourceRange_end.argtypes = [SourceRange] SourceRange_end.restype = SourceLocation +SourceRange_equalRanges = lib.clang_equalRanges +SourceRange_equalRanges.argtypes = [SourceRange, SourceRange] +SourceRange_equalRanges.restype = bool + # CursorKind Functions CursorKind_is_decl = lib.clang_isDeclaration CursorKind_is_decl.argtypes = [CursorKind] @@ -1595,6 +1847,18 @@ CursorKind_is_inv = lib.clang_isInvalid CursorKind_is_inv.argtypes = [CursorKind] CursorKind_is_inv.restype = bool +CursorKind_is_translation_unit = lib.clang_isTranslationUnit +CursorKind_is_translation_unit.argtypes = [CursorKind] +CursorKind_is_translation_unit.restype = bool + +CursorKind_is_preprocessing = lib.clang_isPreprocessing +CursorKind_is_preprocessing.argtypes = [CursorKind] +CursorKind_is_preprocessing.restype = bool + +CursorKind_is_unexposed = lib.clang_isUnexposed +CursorKind_is_unexposed.argtypes = [CursorKind] +CursorKind_is_unexposed.restype = bool + # Cursor Functions # TODO: Implement this function Cursor_get = lib.clang_getCursor @@ -1620,7 +1884,11 @@ Cursor_def.errcheck = Cursor.from_result Cursor_eq = lib.clang_equalCursors Cursor_eq.argtypes = [Cursor, Cursor] -Cursor_eq.restype = c_uint +Cursor_eq.restype = bool + +Cursor_hash = lib.clang_hashCursor +Cursor_hash.argtypes = [Cursor] +Cursor_hash.restype = c_uint Cursor_spelling = lib.clang_getCursorSpelling Cursor_spelling.argtypes = [Cursor] @@ -1650,6 +1918,21 @@ Cursor_type.argtypes = [Cursor] Cursor_type.restype = Type Cursor_type.errcheck = Type.from_result +Cursor_underlying_type = lib.clang_getTypedefDeclUnderlyingType +Cursor_underlying_type.argtypes = [Cursor] +Cursor_underlying_type.restype = Type +Cursor_underlying_type.errcheck = Type.from_result + +Cursor_enum_type = lib.clang_getEnumDeclIntegerType +Cursor_enum_type.argtypes = [Cursor] +Cursor_enum_type.restype = Type +Cursor_enum_type.errcheck = Type.from_result + +Cursor_objc_type_encoding = lib.clang_getDeclObjCTypeEncoding +Cursor_objc_type_encoding.argtypes = [Cursor] +Cursor_objc_type_encoding.restype = _CXString +Cursor_objc_type_encoding.errcheck = _CXString.from_result + Cursor_visit_callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object) Cursor_visit = lib.clang_visitChildren Cursor_visit.argtypes = [Cursor, Cursor_visit_callback, py_object] @@ -1673,6 +1956,14 @@ Type_is_restrict_qualified = lib.clang_isRestrictQualifiedType Type_is_restrict_qualified.argtypes = [Type] Type_is_restrict_qualified.restype = bool +Type_is_pod = lib.clang_isPODType +Type_is_pod.argtypes = [Type] +Type_is_pod.restype = bool + +Type_is_variadic = lib.clang_isFunctionTypeVariadic +Type_is_variadic.argtypes = [Type] +Type_is_variadic.restype = bool + Type_get_pointee = lib.clang_getPointeeType Type_get_pointee.argtypes = [Type] Type_get_pointee.restype = Type @@ -1688,6 +1979,36 @@ Type_get_result.argtypes = [Type] Type_get_result.restype = Type Type_get_result.errcheck = Type.from_result +Type_get_num_arg_types = lib.clang_getNumArgTypes +Type_get_num_arg_types.argtypes = [Type] +Type_get_num_arg_types.restype = c_uint + +Type_get_arg_type = lib.clang_getArgType +Type_get_arg_type.argtypes = [Type, c_uint] +Type_get_arg_type.restype = Type +Type_get_arg_type.errcheck = Type.from_result +Type_get_element_type = lib.clang_getElementType + +Type_get_element_type.argtypes = [Type] +Type_get_element_type.restype = Type +Type_get_element_type.errcheck = Type.from_result + +Type_get_num_elements = lib.clang_getNumElements +Type_get_num_elements.argtypes = [Type] +Type_get_num_elements.restype = c_longlong + +Type_get_array_element = lib.clang_getArrayElementType +Type_get_array_element.argtypes = [Type] +Type_get_array_element.restype = Type +Type_get_array_element.errcheck = Type.from_result + +Type_get_array_size = lib.clang_getArraySize +Type_get_array_size.argtype = [Type] +Type_get_array_size.restype = c_longlong + +Type_equal = lib.clang_equalTypes +Type_equal.argtypes = [Type, Type] +Type_equal.restype = bool # Index Functions Index_create = lib.clang_createIndex @@ -1739,6 +2060,10 @@ TranslationUnit_includes.argtypes = [TranslationUnit, py_object] # File Functions +File_getFile = lib.clang_getFile +File_getFile.argtypes = [TranslationUnit, c_char_p] +File_getFile.restype = c_object_p + File_name = lib.clang_getFileName File_name.argtypes = [File] File_name.restype = _CXString diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py index 3dde891a9c264..9f02bb2a76863 100644 --- a/bindings/python/tests/cindex/test_cursor.py +++ b/bindings/python/tests/cindex/test_cursor.py @@ -1,4 +1,7 @@ -from clang.cindex import Index, CursorKind, TypeKind +from clang.cindex import CursorKind +from clang.cindex import TypeKind +from .util import get_cursor +from .util import get_tu kInput = """\ // FIXME: Find nicer way to drop builtins and other cruft. @@ -24,9 +27,8 @@ void f0(int a0, int a1) { """ def test_get_children(): - index = Index.create() - tu = index.parse('t.c', unsaved_files = [('t.c',kInput)]) - + tu = get_tu(kInput) + # Skip until past start_decl. it = tu.cursor.get_children() while it.next().spelling != 'start_decl': @@ -36,12 +38,14 @@ def test_get_children(): assert len(tu_nodes) == 3 + assert tu_nodes[0] != tu_nodes[1] assert tu_nodes[0].kind == CursorKind.STRUCT_DECL assert tu_nodes[0].spelling == 's0' assert tu_nodes[0].is_definition() == True assert tu_nodes[0].location.file.name == 't.c' assert tu_nodes[0].location.line == 4 assert tu_nodes[0].location.column == 8 + assert tu_nodes[0].hash > 0 s0_nodes = list(tu_nodes[0].get_children()) assert len(s0_nodes) == 2 @@ -61,3 +65,28 @@ def test_get_children(): assert tu_nodes[2].spelling == 'f0' assert tu_nodes[2].displayname == 'f0(int, int)' assert tu_nodes[2].is_definition() == True + +def test_underlying_type(): + tu = get_tu('typedef int foo;') + typedef = get_cursor(tu, 'foo') + assert typedef is not None + + assert typedef.kind.is_declaration() + underlying = typedef.underlying_typedef_type + assert underlying.kind == TypeKind.INT + +def test_enum_type(): + tu = get_tu('enum TEST { FOO=1, BAR=2 };') + enum = get_cursor(tu, 'TEST') + assert enum is not None + + assert enum.kind == CursorKind.ENUM_DECL + enum_type = enum.enum_type + assert enum_type.kind == TypeKind.UINT + +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' diff --git a/bindings/python/tests/cindex/test_cursor_kind.py b/bindings/python/tests/cindex/test_cursor_kind.py index d7a1cfad8f946..f8466e5e0d1d6 100644 --- a/bindings/python/tests/cindex/test_cursor_kind.py +++ b/bindings/python/tests/cindex/test_cursor_kind.py @@ -16,6 +16,15 @@ def test_kind_groups(): assert CursorKind.UNEXPOSED_STMT.is_statement() assert CursorKind.INVALID_FILE.is_invalid() + assert CursorKind.TRANSLATION_UNIT.is_translation_unit() + assert not CursorKind.TYPE_REF.is_translation_unit() + + assert CursorKind.PREPROCESSING_DIRECTIVE.is_preprocessing() + assert not CursorKind.TYPE_REF.is_preprocessing() + + assert CursorKind.UNEXPOSED_DECL.is_unexposed() + assert not CursorKind.TYPE_REF.is_unexposed() + for k in CursorKind.get_all_kinds(): group = [n for n in ('is_declaration', 'is_reference', 'is_expression', 'is_statement', 'is_invalid', 'is_attribute') diff --git a/bindings/python/tests/cindex/test_diagnostics.py b/bindings/python/tests/cindex/test_diagnostics.py index 98f97d3bd3b13..48ab6176fd1df 100644 --- a/bindings/python/tests/cindex/test_diagnostics.py +++ b/bindings/python/tests/cindex/test_diagnostics.py @@ -1,14 +1,10 @@ from clang.cindex import * - -def tu_from_source(source): - index = Index.create() - tu = index.parse('INPUT.c', unsaved_files = [('INPUT.c', source)]) - return tu +from .util import get_tu # FIXME: We need support for invalid translation units to test better. def test_diagnostic_warning(): - tu = tu_from_source("""int f0() {}\n""") + tu = get_tu('int f0() {}\n') assert len(tu.diagnostics) == 1 assert tu.diagnostics[0].severity == Diagnostic.Warning assert tu.diagnostics[0].location.line == 1 @@ -18,8 +14,7 @@ def test_diagnostic_warning(): def test_diagnostic_note(): # FIXME: We aren't getting notes here for some reason. - index = Index.create() - tu = tu_from_source("""#define A x\nvoid *A = 1;\n""") + tu = get_tu('#define A x\nvoid *A = 1;\n') assert len(tu.diagnostics) == 1 assert tu.diagnostics[0].severity == Diagnostic.Warning assert tu.diagnostics[0].location.line == 2 @@ -31,8 +26,7 @@ def test_diagnostic_note(): # assert tu.diagnostics[1].spelling == 'instantiated from' def test_diagnostic_fixit(): - index = Index.create() - tu = tu_from_source("""struct { int f0; } x = { f0 : 1 };""") + tu = get_tu('struct { int f0; } x = { f0 : 1 };') assert len(tu.diagnostics) == 1 assert tu.diagnostics[0].severity == Diagnostic.Warning assert tu.diagnostics[0].location.line == 1 @@ -46,8 +40,7 @@ def test_diagnostic_fixit(): assert tu.diagnostics[0].fixits[0].value == '.f0 = ' def test_diagnostic_range(): - index = Index.create() - tu = tu_from_source("""void f() { int i = "a" + 1; }""") + tu = get_tu('void f() { int i = "a" + 1; }') assert len(tu.diagnostics) == 1 assert tu.diagnostics[0].severity == Diagnostic.Warning assert tu.diagnostics[0].location.line == 1 @@ -65,5 +58,25 @@ def test_diagnostic_range(): assert True else: assert False - +def test_diagnostic_category(): + """Ensure that category properties work.""" + tu = get_tu('int f(int i) { return 7; }', all_warnings=True) + assert len(tu.diagnostics) == 1 + d = tu.diagnostics[0] + + assert d.severity == Diagnostic.Warning + assert d.location.line == 1 + assert d.location.column == 11 + + assert d.category_number == 2 + assert d.category_name == 'Semantic Issue' + +def test_diagnostic_option(): + """Ensure that category option properties work.""" + tu = get_tu('int f(int i) { return 7; }', all_warnings=True) + assert len(tu.diagnostics) == 1 + d = tu.diagnostics[0] + + assert d.option == '-Wunused-parameter' + assert d.disable_option == '-Wno-unused-parameter' diff --git a/bindings/python/tests/cindex/test_file.py b/bindings/python/tests/cindex/test_file.py new file mode 100644 index 0000000000000..146e8c5705287 --- /dev/null +++ b/bindings/python/tests/cindex/test_file.py @@ -0,0 +1,9 @@ +from clang.cindex import Index, File + +def test_file(): + index = Index.create() + tu = index.parse('t.c', unsaved_files = [('t.c', "")]) + file = File.from_name(tu, "t.c") + assert str(file) == "t.c" + assert file.name == "t.c" + assert repr(file) == "<File: t.c>" diff --git a/bindings/python/tests/cindex/test_location.py b/bindings/python/tests/cindex/test_location.py index 47c1c6021f554..528676ef14b57 100644 --- a/bindings/python/tests/cindex/test_location.py +++ b/bindings/python/tests/cindex/test_location.py @@ -1,4 +1,9 @@ -from clang.cindex import Index +from clang.cindex import Cursor +from clang.cindex import File +from clang.cindex import SourceLocation +from clang.cindex import SourceRange +from .util import get_cursor +from .util import get_tu baseInput="int one;\nint two;\n" @@ -8,43 +13,74 @@ def assert_location(loc, line, column, offset): assert loc.offset == offset def test_location(): - index = Index.create() - tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) + tu = get_tu(baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') - for n in tu.cursor.get_children(): - if n.spelling == 'one': - assert_location(n.location,line=1,column=5,offset=4) - if n.spelling == 'two': - assert_location(n.location,line=2,column=5,offset=13) + assert one is not None + assert two is not None + + assert_location(one.location,line=1,column=5,offset=4) + assert_location(two.location,line=2,column=5,offset=13) # adding a linebreak at top should keep columns same - tu = index.parse('t.c', unsaved_files = [('t.c',"\n"+baseInput)]) + tu = get_tu('\n' + baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') + + assert one is not None + assert two is not None - for n in tu.cursor.get_children(): - if n.spelling == 'one': - assert_location(n.location,line=2,column=5,offset=5) - if n.spelling == 'two': - assert_location(n.location,line=3,column=5,offset=14) + assert_location(one.location,line=2,column=5,offset=5) + assert_location(two.location,line=3,column=5,offset=14) # adding a space should affect column on first line only - tu = index.parse('t.c', unsaved_files = [('t.c'," "+baseInput)]) + tu = get_tu(' ' + baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') + + assert_location(one.location,line=1,column=6,offset=5) + assert_location(two.location,line=2,column=5,offset=14) + + # define the expected location ourselves and see if it matches + # the returned location + tu = get_tu(baseInput) + + file = File.from_name(tu, 't.c') + location = SourceLocation.from_position(tu, file, 1, 5) + cursor = Cursor.from_location(tu, location) + + one = get_cursor(tu, 'one') + assert one is not None + assert one == cursor - for n in tu.cursor.get_children(): - if n.spelling == 'one': - assert_location(n.location,line=1,column=6,offset=5) - if n.spelling == 'two': - assert_location(n.location,line=2,column=5,offset=14) + # Ensure locations referring to the same entity are equivalent. + location2 = SourceLocation.from_position(tu, file, 1, 5) + assert location == location2 + location3 = SourceLocation.from_position(tu, file, 1, 4) + assert location2 != location3 def test_extent(): - index = Index.create() - tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) - - for n in tu.cursor.get_children(): - if n.spelling == 'one': - assert_location(n.extent.start,line=1,column=1,offset=0) - assert_location(n.extent.end,line=1,column=8,offset=7) - assert baseInput[n.extent.start.offset:n.extent.end.offset] == "int one" - if n.spelling == 'two': - assert_location(n.extent.start,line=2,column=1,offset=9) - assert_location(n.extent.end,line=2,column=8,offset=16) - assert baseInput[n.extent.start.offset:n.extent.end.offset] == "int two" + tu = get_tu(baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') + + assert_location(one.extent.start,line=1,column=1,offset=0) + assert_location(one.extent.end,line=1,column=8,offset=7) + assert baseInput[one.extent.start.offset:one.extent.end.offset] == "int one" + + assert_location(two.extent.start,line=2,column=1,offset=9) + assert_location(two.extent.end,line=2,column=8,offset=16) + assert baseInput[two.extent.start.offset:two.extent.end.offset] == "int two" + + file = File.from_name(tu, 't.c') + location1 = SourceLocation.from_position(tu, file, 1, 1) + location2 = SourceLocation.from_position(tu, file, 1, 8) + + range1 = SourceRange.from_locations(location1, location2) + range2 = SourceRange.from_locations(location1, location2) + assert range1 == range2 + + location3 = SourceLocation.from_position(tu, file, 1, 6) + range3 = SourceRange.from_locations(location1, location3) + assert range1 != range3 diff --git a/bindings/python/tests/cindex/test_type.py b/bindings/python/tests/cindex/test_type.py index 35c7bbbfa2fa7..ed852103b94ae 100644 --- a/bindings/python/tests/cindex/test_type.py +++ b/bindings/python/tests/cindex/test_type.py @@ -1,4 +1,9 @@ -from clang.cindex import Index, CursorKind, TypeKind +from clang.cindex import CursorKind +from clang.cindex import Index +from clang.cindex import TypeKind +from nose.tools import raises +from .util import get_cursor +from .util import get_tu kInput = """\ @@ -18,63 +23,55 @@ struct teststruct { """ def test_a_struct(): - index = Index.create() - tu = index.parse('t.c', unsaved_files = [('t.c',kInput)]) + tu = get_tu(kInput) - for n in tu.cursor.get_children(): - if n.spelling == 'teststruct': - fields = list(n.get_children()) + teststruct = get_cursor(tu, 'teststruct') + 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.kind == CursorKind.FIELD_DECL for x in fields) + assert fields[0].spelling == 'a' + assert not fields[0].type.is_const_qualified() + assert fields[0].type.kind == TypeKind.INT + assert fields[0].type.get_canonical().kind == TypeKind.INT - assert fields[0].spelling == 'a' - assert not fields[0].type.is_const_qualified() - assert fields[0].type.kind == TypeKind.INT - assert fields[0].type.get_canonical().kind == TypeKind.INT + assert fields[1].spelling == 'b' + assert not fields[1].type.is_const_qualified() + assert fields[1].type.kind == TypeKind.TYPEDEF + assert fields[1].type.get_canonical().kind == TypeKind.INT + assert fields[1].type.get_declaration().spelling == 'I' - assert fields[1].spelling == 'b' - assert not fields[1].type.is_const_qualified() - assert fields[1].type.kind == TypeKind.TYPEDEF - assert fields[1].type.get_canonical().kind == TypeKind.INT - assert fields[1].type.get_declaration().spelling == 'I' + assert fields[2].spelling == 'c' + assert not fields[2].type.is_const_qualified() + assert fields[2].type.kind == TypeKind.LONG + assert fields[2].type.get_canonical().kind == TypeKind.LONG - assert fields[2].spelling == 'c' - assert not fields[2].type.is_const_qualified() - assert fields[2].type.kind == TypeKind.LONG - assert fields[2].type.get_canonical().kind == TypeKind.LONG + assert fields[3].spelling == 'd' + assert not fields[3].type.is_const_qualified() + assert fields[3].type.kind == TypeKind.ULONG + assert fields[3].type.get_canonical().kind == TypeKind.ULONG - assert fields[3].spelling == 'd' - assert not fields[3].type.is_const_qualified() - assert fields[3].type.kind == TypeKind.ULONG - assert fields[3].type.get_canonical().kind == TypeKind.ULONG + assert fields[4].spelling == 'e' + assert not fields[4].type.is_const_qualified() + assert fields[4].type.kind == TypeKind.LONG + assert fields[4].type.get_canonical().kind == TypeKind.LONG - assert fields[4].spelling == 'e' - assert not fields[4].type.is_const_qualified() - assert fields[4].type.kind == TypeKind.LONG - assert fields[4].type.get_canonical().kind == TypeKind.LONG + assert fields[5].spelling == 'f' + assert fields[5].type.is_const_qualified() + assert fields[5].type.kind == TypeKind.INT + assert fields[5].type.get_canonical().kind == TypeKind.INT - assert fields[5].spelling == 'f' - assert fields[5].type.is_const_qualified() - assert fields[5].type.kind == TypeKind.INT - assert fields[5].type.get_canonical().kind == TypeKind.INT - - assert fields[6].spelling == 'g' - assert not fields[6].type.is_const_qualified() - assert fields[6].type.kind == TypeKind.POINTER - assert fields[6].type.get_pointee().kind == TypeKind.INT - - assert fields[7].spelling == 'h' - assert not fields[7].type.is_const_qualified() - assert fields[7].type.kind == TypeKind.POINTER - assert fields[7].type.get_pointee().kind == TypeKind.POINTER - assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER - assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT - - break - - else: - assert False, "Didn't find teststruct??" + assert fields[6].spelling == 'g' + assert not fields[6].type.is_const_qualified() + assert fields[6].type.kind == TypeKind.POINTER + assert fields[6].type.get_pointee().kind == TypeKind.INT + assert fields[7].spelling == 'h' + assert not fields[7].type.is_const_qualified() + assert fields[7].type.kind == TypeKind.POINTER + assert fields[7].type.get_pointee().kind == TypeKind.POINTER + assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER + assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT constarrayInput=""" struct teststruct { @@ -82,14 +79,191 @@ struct teststruct { }; """ def testConstantArray(): - index = Index.create() - tu = index.parse('t.c', unsaved_files = [('t.c',constarrayInput)]) - - for n in tu.cursor.get_children(): - if n.spelling == 'teststruct': - fields = list(n.get_children()) - assert fields[0].spelling == 'A' - assert fields[0].type.kind == TypeKind.CONSTANTARRAY - break - else: - assert False, "Didn't find teststruct??" + tu = get_tu(constarrayInput) + + teststruct = get_cursor(tu, 'teststruct') + assert teststruct is not None, "Didn't find teststruct??" + fields = list(teststruct.get_children()) + assert fields[0].spelling == 'A' + assert fields[0].type.kind == TypeKind.CONSTANTARRAY + assert fields[0].type.get_array_element_type() is not None + assert fields[0].type.get_array_element_type().kind == TypeKind.POINTER + assert fields[0].type.get_array_size() == 2 + +def test_equal(): + """Ensure equivalence operators work on Type.""" + source = 'int a; int b; void *v;' + tu = get_tu(source) + + a = get_cursor(tu, 'a') + b = get_cursor(tu, 'b') + v = get_cursor(tu, 'v') + + assert a is not None + assert b is not None + assert v is not None + + assert a.type == b.type + assert a.type != v.type + + assert a.type != None + assert a.type != 'foo' + +def test_function_argument_types(): + """Ensure that Type.argument_types() works as expected.""" + tu = get_tu('void f(int, int);') + f = get_cursor(tu, 'f') + assert f is not None + + args = f.type.argument_types() + assert args is not None + assert len(args) == 2 + + t0 = args[0] + assert t0 is not None + assert t0.kind == TypeKind.INT + + t1 = args[1] + assert t1 is not None + assert t1.kind == TypeKind.INT + + args2 = list(args) + assert len(args2) == 2 + assert t0 == args2[0] + assert t1 == args2[1] + +@raises(TypeError) +def test_argument_types_string_key(): + """Ensure that non-int keys raise a TypeError.""" + tu = get_tu('void f(int, int);') + f = get_cursor(tu, 'f') + assert f is not None + + args = f.type.argument_types() + assert len(args) == 2 + + args['foo'] + +@raises(IndexError) +def test_argument_types_negative_index(): + """Ensure that negative indexes on argument_types Raises an IndexError.""" + tu = get_tu('void f(int, int);') + f = get_cursor(tu, 'f') + args = f.type.argument_types() + + args[-1] + +@raises(IndexError) +def test_argument_types_overflow_index(): + """Ensure that indexes beyond the length of Type.argument_types() raise.""" + tu = get_tu('void f(int, int);') + f = get_cursor(tu, 'f') + args = f.type.argument_types() + + args[2] + +@raises(Exception) +def test_argument_types_invalid_type(): + """Ensure that obtaining argument_types on a Type without them raises.""" + tu = get_tu('int i;') + i = get_cursor(tu, 'i') + assert i is not None + + i.type.argument_types() + +def test_is_pod(): + """Ensure Type.is_pod() works.""" + tu = get_tu('int i; void f();') + i = get_cursor(tu, 'i') + f = get_cursor(tu, 'f') + + assert i is not None + assert f is not None + + assert i.type.is_pod() + assert not f.type.is_pod() + +def test_function_variadic(): + """Ensure Type.is_function_variadic works.""" + + source =""" +#include <stdarg.h> + +void foo(int a, ...); +void bar(int a, int b); +""" + + tu = get_tu(source) + foo = get_cursor(tu, 'foo') + bar = get_cursor(tu, 'bar') + + assert foo is not None + assert bar is not None + + assert isinstance(foo.type.is_function_variadic(), bool) + assert foo.type.is_function_variadic() + assert not bar.type.is_function_variadic() + +def test_element_type(): + """Ensure Type.element_type works.""" + tu = get_tu('int i[5];') + i = get_cursor(tu, 'i') + assert i is not None + + assert i.type.kind == TypeKind.CONSTANTARRAY + assert i.type.element_type.kind == TypeKind.INT + +@raises(Exception) +def test_invalid_element_type(): + """Ensure Type.element_type raises if type doesn't have elements.""" + tu = get_tu('int i;') + i = get_cursor(tu, 'i') + assert i is not None + i.element_type + +def test_element_count(): + """Ensure Type.element_count works.""" + tu = get_tu('int i[5]; int j;') + i = get_cursor(tu, 'i') + j = get_cursor(tu, 'j') + + assert i is not None + assert j is not None + + assert i.type.element_count == 5 + + try: + j.type.element_count + assert False + except: + assert True + +def test_is_volatile_qualified(): + """Ensure Type.is_volatile_qualified works.""" + + tu = get_tu('volatile int i = 4; int j = 2;') + + i = get_cursor(tu, 'i') + j = get_cursor(tu, 'j') + + assert i is not None + assert j is not None + + assert isinstance(i.type.is_volatile_qualified(), bool) + assert i.type.is_volatile_qualified() + assert not j.type.is_volatile_qualified() + +def test_is_restrict_qualified(): + """Ensure Type.is_restrict_qualified works.""" + + tu = get_tu('struct s { void * restrict i; void * j };') + + i = get_cursor(tu, 'i') + j = get_cursor(tu, 'j') + + assert i is not None + assert j is not None + + assert isinstance(i.type.is_restrict_qualified(), bool) + assert i.type.is_restrict_qualified() + assert not j.type.is_restrict_qualified() diff --git a/bindings/python/tests/cindex/util.py b/bindings/python/tests/cindex/util.py new file mode 100644 index 0000000000000..388b269489982 --- /dev/null +++ b/bindings/python/tests/cindex/util.py @@ -0,0 +1,65 @@ +# This file provides common utility functions for the test suite. + +from clang.cindex import Cursor +from clang.cindex import Index + +def get_tu(source, lang='c', all_warnings=False): + """Obtain a translation unit from source and language. + + By default, the translation unit is created from source file "t.<ext>" + where <ext> is the default file extension for the specified language. By + default it is C, so "t.c" is the default file name. + + Supported languages are {c, cpp, objc}. + + all_warnings is a convenience argument to enable all compiler warnings. + """ + name = 't.c' + if lang == 'cpp': + name = 't.cpp' + elif lang == 'objc': + name = 't.m' + elif lang != 'c': + raise Exception('Unknown language: %s' % lang) + + args = [] + if all_warnings: + args = ['-Wall', '-Wextra'] + + index = Index.create() + tu = index.parse(name, args=args, unsaved_files=[(name, source)]) + assert tu is not None + return tu + +def get_cursor(source, spelling): + """Obtain a cursor from a source object. + + This provides a convenient search mechanism to find a cursor with specific + spelling within a source. The first argument can be either a + TranslationUnit or Cursor instance. + + If the cursor is not found, None is returned. + """ + 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: + return cursor + + # Recurse into children. + result = get_cursor(cursor, spelling) + if result is not None: + return result + + return None + + +__all__ = [ + 'get_cursor', + 'get_tu', +] |