From 222e2a7620e6520ffaf4fc4e69d79c18da31542e Mon Sep 17 00:00:00 2001 From: "Zancanaro; Carlo" Date: Mon, 24 Sep 2012 09:58:17 +1000 Subject: Add the clang library to the repo (with some of my changes, too). --- clang/bindings/python/README.txt | 17 + clang/bindings/python/clang/__init__.py | 24 + clang/bindings/python/clang/cindex.py | 2126 ++++++++++++++++++++ .../bindings/python/examples/cindex/cindex-dump.py | 87 + .../python/examples/cindex/cindex-includes.py | 58 + clang/bindings/python/tests/__init__.py | 0 .../bindings/python/tests/cindex/INPUTS/header1.h | 6 + .../bindings/python/tests/cindex/INPUTS/header2.h | 6 + .../bindings/python/tests/cindex/INPUTS/header3.h | 3 + .../bindings/python/tests/cindex/INPUTS/hello.cpp | 6 + .../python/tests/cindex/INPUTS/include.cpp | 5 + .../python/tests/cindex/INPUTS/parse_arguments.c | 2 + clang/bindings/python/tests/cindex/__init__.py | 0 clang/bindings/python/tests/cindex/test_cursor.py | 92 + .../python/tests/cindex/test_cursor_kind.py | 40 + .../python/tests/cindex/test_diagnostics.py | 82 + clang/bindings/python/tests/cindex/test_file.py | 9 + clang/bindings/python/tests/cindex/test_index.py | 15 + .../bindings/python/tests/cindex/test_location.py | 86 + .../python/tests/cindex/test_translation_unit.py | 84 + clang/bindings/python/tests/cindex/test_type.py | 276 +++ clang/bindings/python/tests/cindex/util.py | 65 + 22 files changed, 3089 insertions(+) create mode 100644 clang/bindings/python/README.txt create mode 100644 clang/bindings/python/clang/__init__.py create mode 100644 clang/bindings/python/clang/cindex.py create mode 100644 clang/bindings/python/examples/cindex/cindex-dump.py create mode 100644 clang/bindings/python/examples/cindex/cindex-includes.py create mode 100644 clang/bindings/python/tests/__init__.py create mode 100644 clang/bindings/python/tests/cindex/INPUTS/header1.h create mode 100644 clang/bindings/python/tests/cindex/INPUTS/header2.h create mode 100644 clang/bindings/python/tests/cindex/INPUTS/header3.h create mode 100644 clang/bindings/python/tests/cindex/INPUTS/hello.cpp create mode 100644 clang/bindings/python/tests/cindex/INPUTS/include.cpp create mode 100644 clang/bindings/python/tests/cindex/INPUTS/parse_arguments.c create mode 100644 clang/bindings/python/tests/cindex/__init__.py create mode 100644 clang/bindings/python/tests/cindex/test_cursor.py create mode 100644 clang/bindings/python/tests/cindex/test_cursor_kind.py create mode 100644 clang/bindings/python/tests/cindex/test_diagnostics.py create mode 100644 clang/bindings/python/tests/cindex/test_file.py create mode 100644 clang/bindings/python/tests/cindex/test_index.py create mode 100644 clang/bindings/python/tests/cindex/test_location.py create mode 100644 clang/bindings/python/tests/cindex/test_translation_unit.py create mode 100644 clang/bindings/python/tests/cindex/test_type.py create mode 100644 clang/bindings/python/tests/cindex/util.py (limited to 'clang/bindings/python') diff --git a/clang/bindings/python/README.txt b/clang/bindings/python/README.txt new file mode 100644 index 0000000..742cf8f --- /dev/null +++ b/clang/bindings/python/README.txt @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// Clang Python Bindings +//===----------------------------------------------------------------------===// + +This directory implements Python bindings for Clang. + +You may need to alter LD_LIBRARY_PATH so that the Clang library can be +found. The unit tests are designed to be run with 'nosetests'. For example: +-- +$ env PYTHONPATH=$(echo ~/llvm/tools/clang/bindings/python/) \ + LD_LIBRARY_PATH=$(llvm-config --libdir) \ + nosetests -v +tests.cindex.test_index.test_create ... ok +... + +OK +-- diff --git a/clang/bindings/python/clang/__init__.py b/clang/bindings/python/clang/__init__.py new file mode 100644 index 0000000..88f3081 --- /dev/null +++ b/clang/bindings/python/clang/__init__.py @@ -0,0 +1,24 @@ +#===- __init__.py - Clang Python Bindings --------------------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +Clang Library Bindings +====================== + +This package provides access to the Clang compiler and libraries. + +The available modules are: + + cindex + + Bindings for the Clang indexing library. +""" + +__all__ = ['cindex'] + diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py new file mode 100644 index 0000000..6f0d25f --- /dev/null +++ b/clang/bindings/python/clang/cindex.py @@ -0,0 +1,2126 @@ +#===- cindex.py - Python Indexing Library Bindings -----------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +r""" +Clang Indexing Library Bindings +=============================== + +This module provides an interface to the Clang indexing library. It is a +low-level interface to the indexing library which attempts to match the Clang +API directly while also being "pythonic". Notable differences from the C API +are: + + * string results are returned as Python strings, not CXString objects. + + * null cursors are translated to None. + + * access to child cursors is done via iteration, not visitation. + +The major indexing objects are: + + Index + + The top-level object which manages some global library state. + + TranslationUnit + + High-level object encapsulating the AST for a single translation unit. These + can be loaded from .ast files or parsed on the fly. + + Cursor + + Generic object for representing a node in the AST. + + SourceRange, SourceLocation, and File + + Objects representing information about the input source. + +Most object information is exposed using properties, when the underlying API +call is efficient. +""" + +# TODO +# ==== +# +# o API support for invalid translation units. Currently we can't even get the +# diagnostics on failure because they refer to locations in an object that +# will have been invalidated. +# +# o fix memory management issues (currently client must hold on to index and +# translation unit, or risk crashes). +# +# o expose code completion APIs. +# +# o cleanup ctypes wrapping, would be nice to separate the ctypes details more +# clearly, and hide from the external interface (i.e., help(cindex)). +# +# 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 + # this location. We need a better system of identifying and loading the + # CIndex library. It could be on path or elsewhere, or versioned, etc. + import platform + name = platform.system() + if name == 'Darwin': + return cdll.LoadLibrary('libclang.dylib') + elif name == 'Windows': + return cdll.LoadLibrary('libclang.dll') + else: + return cdll.LoadLibrary('libclang.so') + +# 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 +# integer and pass the wrong value on platforms where int != void*. Work around +# this by marshalling object arguments as void**. +c_object_p = POINTER(c_void_p) + +lib = get_cindex_library() + +### Structures and Utility Classes ### + +class _CXString(Structure): + """Helper for transforming CXString results.""" + + _fields_ = [("spelling", c_char_p), ("free", c_int)] + + def __del__(self): + _CXString_dispose(self) + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, _CXString) + return _CXString_getCString(res) + +class SourceLocation(Structure): + """ + A SourceLocation represents a particular location within a source file. + """ + _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] + _data = None + + def _get_instantiation(self): + 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)) + 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.""" + return self._get_instantiation()[0] + + @property + def line(self): + """Get the line represented by this source location.""" + return self._get_instantiation()[1] + + @property + def column(self): + """Get the column represented by this source location.""" + return self._get_instantiation()[2] + + @property + def offset(self): + """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 "" % ( + filename, self.line, self.column) + +class SourceRange(Structure): + """ + A SourceRange describes a range of source locations within the source + code. + """ + _fields_ = [ + ("ptr_data", c_void_p * 2), + ("begin_int_data", c_uint), + ("end_int_data", c_uint)] + + # FIXME: Eliminate this and make normal constructor? Requires hiding ctypes + # object. + @staticmethod + def from_locations(start, end): + return SourceRange_getRange(start, end) + + @property + def start(self): + """ + Return a SourceLocation representing the first character within a + source range. + """ + return SourceRange_start(self) + + @property + def end(self): + """ + Return a SourceLocation representing the last character within a + source range. + """ + 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 "" % (self.start, self.end) + +class Diagnostic(object): + """ + A Diagnostic is a single instance of a Clang diagnostic. It includes the + diagnostic severity, the message, the location the diagnostic occurred, as + well as additional source ranges and associated fix-it hints. + """ + + Ignored = 0 + Note = 1 + Warning = 2 + Error = 3 + Fatal = 4 + + def __init__(self, ptr): + self.ptr = ptr + + def __del__(self): + _clang_disposeDiagnostic(self) + + @property + def severity(self): + return _clang_getDiagnosticSeverity(self) + + @property + def location(self): + return _clang_getDiagnosticLocation(self) + + @property + def spelling(self): + return _clang_getDiagnosticSpelling(self) + + @property + def ranges(self): + class RangeIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(_clang_getDiagnosticNumRanges(self.diag)) + + def __getitem__(self, key): + if (key >= len(self)): + raise IndexError + return _clang_getDiagnosticRange(self.diag, key) + + return RangeIterator(self) + + @property + def fixits(self): + class FixItIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(_clang_getDiagnosticNumFixIts(self.diag)) + + def __getitem__(self, key): + range = SourceRange() + value = _clang_getDiagnosticFixIt(self.diag, key, byref(range)) + if len(value) == 0: + raise IndexError + + return FixIt(range, value) + + 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 "" % ( + self.severity, self.location, self.spelling) + + def from_param(self): + return self.ptr + +class FixIt(object): + """ + A FixIt represents a transformation to be applied to the source to + "fix-it". The fix-it shouldbe applied by replacing the given source range + with the given value. + """ + + def __init__(self, range, value): + self.range = range + self.value = value + + def __repr__(self): + return "" % (self.range, self.value) + +### Cursor Kinds ### + +class CursorKind(object): + """ + A CursorKind describes the kind of entity that a cursor points to. + """ + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + + def __init__(self, value): + if value >= len(CursorKind._kinds): + CursorKind._kinds += [None] * (value - len(CursorKind._kinds) + 1) + if CursorKind._kinds[value] is not None: + raise ValueError,'CursorKind already loaded' + self.value = value + CursorKind._kinds[value] = self + CursorKind._name_map = None + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this cursor kind.""" + if self._name_map is None: + self._name_map = {} + for key,value in CursorKind.__dict__.items(): + if isinstance(value,CursorKind): + self._name_map[value] = key + return self._name_map[self] + + @staticmethod + def from_id(id): + if id >= len(CursorKind._kinds) or CursorKind._kinds[id] is None: + raise ValueError,'Unknown cursor kind' + return CursorKind._kinds[id] + + @staticmethod + def get_all_kinds(): + """Return all CursorKind enumeration instances.""" + return filter(None, CursorKind._kinds) + + def is_declaration(self): + """Test if this is a declaration kind.""" + return CursorKind_is_decl(self) + + def is_reference(self): + """Test if this is a reference kind.""" + return CursorKind_is_ref(self) + + def is_expression(self): + """Test if this is an expression kind.""" + return CursorKind_is_expr(self) + + def is_statement(self): + """Test if this is a statement kind.""" + return CursorKind_is_stmt(self) + + def is_attribute(self): + """Test if this is an attribute kind.""" + return CursorKind_is_attribute(self) + + def is_invalid(self): + """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,) + +# FIXME: Is there a nicer way to expose this enumeration? We could potentially +# represent the nested structure, or even build a class hierarchy. The main +# things we want for sure are (a) simple external access to kinds, (b) a place +# to hang a description and name, (c) easy to keep in sync with Index.h. + +### +# Declaration Kinds + +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find their +# definitions, etc. However, the specific kind of the declaration is not +# reported. +CursorKind.UNEXPOSED_DECL = CursorKind(1) + +# A C or C++ struct. +CursorKind.STRUCT_DECL = CursorKind(2) + +# A C or C++ union. +CursorKind.UNION_DECL = CursorKind(3) + +# A C++ class. +CursorKind.CLASS_DECL = CursorKind(4) + +# An enumeration. +CursorKind.ENUM_DECL = CursorKind(5) + +# A field (in C) or non-static data member (in C++) in a struct, union, or C++ +# class. +CursorKind.FIELD_DECL = CursorKind(6) + +# An enumerator constant. +CursorKind.ENUM_CONSTANT_DECL = CursorKind(7) + +# A function. +CursorKind.FUNCTION_DECL = CursorKind(8) + +# A variable. +CursorKind.VAR_DECL = CursorKind(9) + +# A function or method parameter. +CursorKind.PARM_DECL = CursorKind(10) + +# An Objective-C @interface. +CursorKind.OBJC_INTERFACE_DECL = CursorKind(11) + +# An Objective-C @interface for a category. +CursorKind.OBJC_CATEGORY_DECL = CursorKind(12) + +# An Objective-C @protocol declaration. +CursorKind.OBJC_PROTOCOL_DECL = CursorKind(13) + +# An Objective-C @property declaration. +CursorKind.OBJC_PROPERTY_DECL = CursorKind(14) + +# An Objective-C instance variable. +CursorKind.OBJC_IVAR_DECL = CursorKind(15) + +# An Objective-C instance method. +CursorKind.OBJC_INSTANCE_METHOD_DECL = CursorKind(16) + +# An Objective-C class method. +CursorKind.OBJC_CLASS_METHOD_DECL = CursorKind(17) + +# An Objective-C @implementation. +CursorKind.OBJC_IMPLEMENTATION_DECL = CursorKind(18) + +# An Objective-C @implementation for a category. +CursorKind.OBJC_CATEGORY_IMPL_DECL = CursorKind(19) + +# A typedef. +CursorKind.TYPEDEF_DECL = CursorKind(20) + +# A C++ class method. +CursorKind.CXX_METHOD = CursorKind(21) + +# A C++ namespace. +CursorKind.NAMESPACE = CursorKind(22) + +# A linkage specification, e.g. 'extern "C"'. +CursorKind.LINKAGE_SPEC = CursorKind(23) + +# A C++ constructor. +CursorKind.CONSTRUCTOR = CursorKind(24) + +# A C++ destructor. +CursorKind.DESTRUCTOR = CursorKind(25) + +# A C++ conversion function. +CursorKind.CONVERSION_FUNCTION = CursorKind(26) + +# A C++ template type parameter +CursorKind.TEMPLATE_TYPE_PARAMETER = CursorKind(27) + +# A C++ non-type template paramater. +CursorKind.TEMPLATE_NON_TYPE_PARAMETER = CursorKind(28) + +# A C++ template template parameter. +CursorKind.TEMPLATE_TEMPLATE_PARAMTER = CursorKind(29) + +# A C++ function template. +CursorKind.FUNCTION_TEMPLATE = CursorKind(30) + +# A C++ class template. +CursorKind.CLASS_TEMPLATE = CursorKind(31) + +# A C++ class template partial specialization. +CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION = CursorKind(32) + +# A C++ namespace alias declaration. +CursorKind.NAMESPACE_ALIAS = CursorKind(33) + +# A C++ using directive +CursorKind.USING_DIRECTIVE = CursorKind(34) + +# A C++ using declaration +CursorKind.USING_DECLARATION = CursorKind(35) + +# A Type alias decl. +CursorKind.TYPE_ALIAS_DECL = CursorKind(36) + +# A Objective-C synthesize decl +CursorKind.OBJC_SYNTHESIZE_DECL = CursorKind(37) + +# A Objective-C dynamic decl +CursorKind.OBJC_DYNAMIC_DECL = CursorKind(38) + +# A C++ access specifier decl. +CursorKind.CXX_ACCESS_SPEC_DECL = CursorKind(39) + + +### +# Reference Kinds + +CursorKind.OBJC_SUPER_CLASS_REF = CursorKind(40) +CursorKind.OBJC_PROTOCOL_REF = CursorKind(41) +CursorKind.OBJC_CLASS_REF = CursorKind(42) + +# A reference to a type declaration. +# +# A type reference occurs anywhere where a type is named but not +# declared. For example, given: +# typedef unsigned size_type; +# size_type size; +# +# The typedef is a declaration of size_type (CXCursor_TypedefDecl), +# while the type of the variable "size" is referenced. The cursor +# referenced by the type of size is the typedef for size_type. +CursorKind.TYPE_REF = CursorKind(43) +CursorKind.CXX_BASE_SPECIFIER = CursorKind(44) + +# A reference to a class template, function template, template +# template parameter, or class template partial specialization. +CursorKind.TEMPLATE_REF = CursorKind(45) + +# A reference to a namespace or namepsace alias. +CursorKind.NAMESPACE_REF = CursorKind(46) + +# A reference to a member of a struct, union, or class that occurs in +# some non-expression context, e.g., a designated initializer. +CursorKind.MEMBER_REF = CursorKind(47) + +# A reference to a labeled statement. +CursorKind.LABEL_REF = CursorKind(48) + +# A reference toa a set of overloaded functions or function templates +# that has not yet been resolved to a specific function or function template. +CursorKind.OVERLOADED_DECL_REF = CursorKind(49) + +### +# Invalid/Error Kinds + +CursorKind.INVALID_FILE = CursorKind(70) +CursorKind.NO_DECL_FOUND = CursorKind(71) +CursorKind.NOT_IMPLEMENTED = CursorKind(72) +CursorKind.INVALID_CODE = CursorKind(73) + +### +# Expression Kinds + +# An expression whose specific kind is not exposed via this interface. +# +# Unexposed expressions have the same operations as any other kind of +# expression; one can extract their location information, spelling, children, +# etc. However, the specific kind of the expression is not reported. +CursorKind.UNEXPOSED_EXPR = CursorKind(100) + +# An expression that refers to some value declaration, such as a function, +# varible, or enumerator. +CursorKind.DECL_REF_EXPR = CursorKind(101) + +# An expression that refers to a member of a struct, union, class, Objective-C +# class, etc. +CursorKind.MEMBER_REF_EXPR = CursorKind(102) + +# An expression that calls a function. +CursorKind.CALL_EXPR = CursorKind(103) + +# An expression that sends a message to an Objective-C object or class. +CursorKind.OBJC_MESSAGE_EXPR = CursorKind(104) + +# An expression that represents a block literal. +CursorKind.BLOCK_EXPR = CursorKind(105) + +# An integer literal. +CursorKind.INTEGER_LITERAL = CursorKind(106) + +# A floating point number literal. +CursorKind.FLOATING_LITERAL = CursorKind(107) + +# An imaginary number literal. +CursorKind.IMAGINARY_LITERAL = CursorKind(108) + +# A string literal. +CursorKind.STRING_LITERAL = CursorKind(109) + +# A character literal. +CursorKind.CHARACTER_LITERAL = CursorKind(110) + +# A parenthesized expression, e.g. "(1)". +# +# This AST node is only formed if full location information is requested. +CursorKind.PAREN_EXPR = CursorKind(111) + +# This represents the unary-expression's (except sizeof and +# alignof). +CursorKind.UNARY_OPERATOR = CursorKind(112) + +# [C99 6.5.2.1] Array Subscripting. +CursorKind.ARRAY_SUBSCRIPT_EXPR = CursorKind(113) + +# A builtin binary operation expression such as "x + y" or +# "x <= y". +CursorKind.BINARY_OPERATOR = CursorKind(114) + +# Compound assignment such as "+=". +CursorKind.COMPOUND_ASSIGNMENT_OPERATOR = CursorKind(115) + +# The ?: ternary operator. +CursorKind.CONDITONAL_OPERATOR = CursorKind(116) + +# An explicit cast in C (C99 6.5.4) or a C-style cast in C++ +# (C++ [expr.cast]), which uses the syntax (Type)expr. +# +# For example: (int)f. +CursorKind.CSTYLE_CAST_EXPR = CursorKind(117) + +# [C99 6.5.2.5] +CursorKind.COMPOUND_LITERAL_EXPR = CursorKind(118) + +# Describes an C or C++ initializer list. +CursorKind.INIT_LIST_EXPR = CursorKind(119) + +# The GNU address of label extension, representing &&label. +CursorKind.ADDR_LABEL_EXPR = CursorKind(120) + +# This is the GNU Statement Expression extension: ({int X=4; X;}) +CursorKind.StmtExpr = CursorKind(121) + +# Represents a C11 generic selection. +CursorKind.GENERIC_SELECTION_EXPR = CursorKind(122) + +# Implements the GNU __null extension, which is a name for a null +# pointer constant that has integral type (e.g., int or long) and is the same +# size and alignment as a pointer. +# +# The __null extension is typically only used by system headers, which define +# NULL as __null in C++ rather than using 0 (which is an integer that may not +# match the size of a pointer). +CursorKind.GNU_NULL_EXPR = CursorKind(123) + +# C++'s static_cast<> expression. +CursorKind.CXX_STATIC_CAST_EXPR = CursorKind(124) + +# C++'s dynamic_cast<> expression. +CursorKind.CXX_DYNAMIC_CAST_EXPR = CursorKind(125) + +# C++'s reinterpret_cast<> expression. +CursorKind.CXX_REINTERPRET_CAST_EXPR = CursorKind(126) + +# C++'s const_cast<> expression. +CursorKind.CXX_CONST_CAST_EXPR = CursorKind(127) + +# Represents an explicit C++ type conversion that uses "functional" +# notion (C++ [expr.type.conv]). +# +# Example: +# \code +# x = int(0.5); +# \endcode +CursorKind.CXX_FUNCTIONAL_CAST_EXPR = CursorKind(128) + +# A C++ typeid expression (C++ [expr.typeid]). +CursorKind.CXX_TYPEID_EXPR = CursorKind(129) + +# [C++ 2.13.5] C++ Boolean Literal. +CursorKind.CXX_BOOL_LITERAL_EXPR = CursorKind(130) + +# [C++0x 2.14.7] C++ Pointer Literal. +CursorKind.CXX_NULL_PTR_LITERAL_EXPR = CursorKind(131) + +# Represents the "this" expression in C++ +CursorKind.CXX_THIS_EXPR = CursorKind(132) + +# [C++ 15] C++ Throw Expression. +# +# This handles 'throw' and 'throw' assignment-expression. When +# assignment-expression isn't present, Op will be null. +CursorKind.CXX_THROW_EXPR = CursorKind(133) + +# A new expression for memory allocation and constructor calls, e.g: +# "new CXXNewExpr(foo)". +CursorKind.CXX_NEW_EXPR = CursorKind(134) + +# A delete expression for memory deallocation and destructor calls, +# e.g. "delete[] pArray". +CursorKind.CXX_DELETE_EXPR = CursorKind(135) + +# Represents a unary expression. +CursorKind.CXX_UNARY_EXPR = CursorKind(136) + +# ObjCStringLiteral, used for Objective-C string literals i.e. "foo". +CursorKind.OBJC_STRING_LITERAL = CursorKind(137) + +# ObjCEncodeExpr, used for in Objective-C. +CursorKind.OBJC_ENCODE_EXPR = CursorKind(138) + +# ObjCSelectorExpr used for in Objective-C. +CursorKind.OBJC_SELECTOR_EXPR = CursorKind(139) + +# Objective-C's protocol expression. +CursorKind.OBJC_PROTOCOL_EXPR = CursorKind(140) + +# An Objective-C "bridged" cast expression, which casts between +# Objective-C pointers and C pointers, transferring ownership in the process. +# +# \code +# NSString *str = (__bridge_transfer NSString *)CFCreateString(); +# \endcode +CursorKind.OBJC_BRIDGE_CAST_EXPR = CursorKind(141) + +# Represents a C++0x pack expansion that produces a sequence of +# expressions. +# +# A pack expansion expression contains a pattern (which itself is an +# expression) followed by an ellipsis. For example: +CursorKind.PACK_EXPANSION_EXPR = CursorKind(142) + +# Represents an expression that computes the length of a parameter +# pack. +CursorKind.SIZE_OF_PACK_EXPR = CursorKind(143) + +# A statement whose specific kind is not exposed via this interface. +# +# Unexposed statements have the same operations as any other kind of statement; +# one can extract their location information, spelling, children, etc. However, +# the specific kind of the statement is not reported. +CursorKind.UNEXPOSED_STMT = CursorKind(200) + +# A labelled statement in a function. +CursorKind.LABEL_STMT = CursorKind(201) + +# A compound statement +CursorKind.COMPOUND_STMT = CursorKind(202) + +# A case statement. +CursorKind.CASE_STMT = CursorKind(203) + +# A default statement. +CursorKind.DEFAULT_STMT = CursorKind(204) + +# An if statement. +CursorKind.IF_STMT = CursorKind(205) + +# A switch statement. +CursorKind.SWITCH_STMT = CursorKind(206) + +# A while statement. +CursorKind.WHILE_STMT = CursorKind(207) + +# A do statement. +CursorKind.DO_STMT = CursorKind(208) + +# A for statement. +CursorKind.FOR_STMT = CursorKind(209) + +# A goto statement. +CursorKind.GOTO_STMT = CursorKind(210) + +# An indirect goto statement. +CursorKind.INDIRECT_GOTO_STMT = CursorKind(211) + +# A continue statement. +CursorKind.CONTINUE_STMT = CursorKind(212) + +# A break statement. +CursorKind.BREAK_STMT = CursorKind(213) + +# A return statement. +CursorKind.RETURN_STMT = CursorKind(214) + +# A GNU-style inline assembler statement. +CursorKind.ASM_STMT = CursorKind(215) + +# Objective-C's overall @try-@catch-@finally statement. +CursorKind.OBJC_AT_TRY_STMT = CursorKind(216) + +# Objective-C's @catch statement. +CursorKind.OBJC_AT_CATCH_STMT = CursorKind(217) + +# Objective-C's @finally statement. +CursorKind.OBJC_AT_FINALLY_STMT = CursorKind(218) + +# Objective-C's @throw statement. +CursorKind.OBJC_AT_THROW_STMT = CursorKind(219) + +# Objective-C's @synchronized statement. +CursorKind.OBJC_AT_SYNCHRONIZED_STMT = CursorKind(220) + +# Objective-C's autorealease pool statement. +CursorKind.OBJC_AUTORELEASE_POOL_STMT = CursorKind(221) + +# Objective-C's for collection statement. +CursorKind.OBJC_FOR_COLLECTION_STMT = CursorKind(222) + +# C++'s catch statement. +CursorKind.CXX_CATCH_STMT = CursorKind(223) + +# C++'s try statement. +CursorKind.CXX_TRY_STMT = CursorKind(224) + +# C++'s for (* : *) statement. +CursorKind.CXX_FOR_RANGE_STMT = CursorKind(225) + +# Windows Structured Exception Handling's try statement. +CursorKind.SEH_TRY_STMT = CursorKind(226) + +# Windows Structured Exception Handling's except statement. +CursorKind.SEH_EXCEPT_STMT = CursorKind(227) + +# Windows Structured Exception Handling's finally statement. +CursorKind.SEH_FINALLY_STMT = CursorKind(228) + +# The null statement. +CursorKind.NULL_STMT = CursorKind(230) + +# Adaptor class for mixing declarations with statements and expressions. +CursorKind.DECL_STMT = CursorKind(231) + +### +# Other Kinds + +# Cursor that represents the translation unit itself. +# +# The translation unit cursor exists primarily to act as the root cursor for +# traversing the contents of a translation unit. +CursorKind.TRANSLATION_UNIT = CursorKind(300) + +### +# Attributes + +# An attribute whoe specific kind is note exposed via this interface +CursorKind.UNEXPOSED_ATTR = CursorKind(400) + +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) +CursorKind.MACRO_DEFINITION = CursorKind(501) +CursorKind.MACRO_INSTANTIATION = CursorKind(502) +CursorKind.INCLUSION_DIRECTIVE = CursorKind(503) + +### Cursors ### + +class Cursor(Structure): + """ + The Cursor class represents a reference to an element within the AST. It + acts as a kind of iterator. + """ + _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 self.__eq__(other) + + def is_definition(self): + """ + Returns true if the declaration pointed at by the cursor is also a + definition of that entity. + """ + return Cursor_is_def(self) + + def get_definition(self): + """ + If the cursor is a reference to a declaration or a declaration of + some entity, return a cursor that points to the definition of that + entity. + """ + # TODO: Should probably check that this is either a reference or + # declaration prior to issuing the lookup. + return Cursor_def(self) + + def get_usr(self): + """Return the Unified Symbol Resultion (USR) for the entity referenced + by the given cursor (or None). + + A Unified Symbol Resolution (USR) is a string that identifies a + particular entity (function, class, variable, etc.) within a + program. USRs can be compared across translation units to determine, + e.g., when references in one translation refer to an entity defined in + another translation unit.""" + return Cursor_usr(self) + + @property + def kind(self): + """Return the kind of this cursor.""" + return CursorKind.from_id(self._kind_id) + + @property + def spelling(self): + """Return the spelling of the entity pointed at by the cursor.""" + if not self.kind.is_declaration(): + # FIXME: clang_getCursorSpelling should be fixed to not assert on + # this, for consistency with clang_getCursorUSR. + return None + if not hasattr(self, '_spelling'): + self._spelling = Cursor_spelling(self) + return self._spelling + + @property + def displayname(self): + """ + Return the display name for the entity referenced by this cursor. + + The display name contains extra information that helps identify the cursor, + such as the parameters of a function or template or the arguments of a + class template specialization. + """ + if not hasattr(self, '_displayname'): + self._displayname = Cursor_displayname(self) + return self._displayname + + @property + def location(self): + """ + Return the source location (the starting character) of the entity + pointed at by the cursor. + """ + if not hasattr(self, '_loc'): + self._loc = Cursor_loc(self) + return self._loc + + @property + def extent(self): + """ + Return the source range (the range of text) occupied by the entity + pointed at by the cursor. + """ + if not hasattr(self, '_extent'): + self._extent = Cursor_extent(self) + return self._extent + + @property + def type(self): + """ + 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.""" + + # FIXME: Expose iteration from CIndex, PR6125. + def visitor(child, parent, children): + # FIXME: Document this assertion in API. + # FIXME: There should just be an isNull method. + assert child != Cursor_null() + children.append(child) + return 1 # continue + children = [] + Cursor_visit(self, Cursor_visit_callback(visitor), children) + return iter(children) + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, Cursor) + # FIXME: There should just be an isNull method. + if res == Cursor_null(): + return None + return res + + +### Type Kinds ### + +class TypeKind(object): + """ + Describes the kind of type. + """ + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + + def __init__(self, value): + if value >= len(TypeKind._kinds): + TypeKind._kinds += [None] * (value - len(TypeKind._kinds) + 1) + if TypeKind._kinds[value] is not None: + raise ValueError,'TypeKind already loaded' + self.value = value + TypeKind._kinds[value] = self + TypeKind._name_map = None + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this cursor kind.""" + if self._name_map is None: + self._name_map = {} + for key,value in TypeKind.__dict__.items(): + if isinstance(value,TypeKind): + self._name_map[value] = key + return self._name_map[self] + + @property + def spelling(self): + """Retrieve the spelling of this TypeKind.""" + return TypeKind_spelling(self.value) + + @staticmethod + def from_id(id): + if id >= len(TypeKind._kinds) or TypeKind._kinds[id] is None: + raise ValueError,'Unknown type kind %d' % id + return TypeKind._kinds[id] + + def __repr__(self): + return 'TypeKind.%s' % (self.name,) + +TypeKind_spelling = lib.clang_getTypeKindSpelling +TypeKind_spelling.argtypes = [c_uint] +TypeKind_spelling.restype = _CXString +TypeKind_spelling.errcheck = _CXString.from_result + + +TypeKind.INVALID = TypeKind(0) +TypeKind.UNEXPOSED = TypeKind(1) +TypeKind.VOID = TypeKind(2) +TypeKind.BOOL = TypeKind(3) +TypeKind.CHAR_U = TypeKind(4) +TypeKind.UCHAR = TypeKind(5) +TypeKind.CHAR16 = TypeKind(6) +TypeKind.CHAR32 = TypeKind(7) +TypeKind.USHORT = TypeKind(8) +TypeKind.UINT = TypeKind(9) +TypeKind.ULONG = TypeKind(10) +TypeKind.ULONGLONG = TypeKind(11) +TypeKind.UINT128 = TypeKind(12) +TypeKind.CHAR_S = TypeKind(13) +TypeKind.SCHAR = TypeKind(14) +TypeKind.WCHAR = TypeKind(15) +TypeKind.SHORT = TypeKind(16) +TypeKind.INT = TypeKind(17) +TypeKind.LONG = TypeKind(18) +TypeKind.LONGLONG = TypeKind(19) +TypeKind.INT128 = TypeKind(20) +TypeKind.FLOAT = TypeKind(21) +TypeKind.DOUBLE = TypeKind(22) +TypeKind.LONGDOUBLE = TypeKind(23) +TypeKind.NULLPTR = TypeKind(24) +TypeKind.OVERLOAD = TypeKind(25) +TypeKind.DEPENDENT = TypeKind(26) +TypeKind.OBJCID = TypeKind(27) +TypeKind.OBJCCLASS = TypeKind(28) +TypeKind.OBJCSEL = TypeKind(29) +TypeKind.COMPLEX = TypeKind(100) +TypeKind.POINTER = TypeKind(101) +TypeKind.BLOCKPOINTER = TypeKind(102) +TypeKind.LVALUEREFERENCE = TypeKind(103) +TypeKind.RVALUEREFERENCE = TypeKind(104) +TypeKind.RECORD = TypeKind(105) +TypeKind.ENUM = TypeKind(106) +TypeKind.TYPEDEF = TypeKind(107) +TypeKind.OBJCINTERFACE = TypeKind(108) +TypeKind.OBJCOBJECTPOINTER = TypeKind(109) +TypeKind.FUNCTIONNOPROTO = TypeKind(110) +TypeKind.FUNCTIONPROTO = TypeKind(111) +TypeKind.CONSTANTARRAY = TypeKind(112) +TypeKind.VECTOR = TypeKind(113) + +class Type(Structure): + """ + The type of an element in the abstract syntax tree. + """ + _fields_ = [("_kind_id", c_int), ("data", c_void_p * 2)] + + @property + def kind(self): + """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) + return res + + def get_canonical(self): + """ + Return the canonical type for a Type. + + Clang's type system explicitly models typedefs and all the + ways a specific type can be represented. The canonical type + is the underlying type with all the "sugar" removed. For + example, if 'T' is a typedef for 'int', the canonical type for + 'T' would be 'int'. + """ + return Type_get_canonical(self) + + def is_const_qualified(self): + """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. + + 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. + + 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. + """ + return Type_get_pointee(self) + + def get_declaration(self): + """ + Return the cursor for the declaration of the given type. + """ + return Type_get_declaration(self) + + def get_result(self): + """ + Retrieve the result type associated with a function type. + """ + 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 +# wrappers attached to some underlying object, which is exposed via CIndex as +# a void*. + +class ClangObject(object): + """ + A helper for Clang objects. This class helps act as an intermediary for + the ctypes library and the Clang CIndex library. + """ + def __init__(self, obj): + assert isinstance(obj, c_object_p) and obj + self.obj = self._as_parameter_ = obj + + def from_param(self): + return self._as_parameter_ + + +class _CXUnsavedFile(Structure): + """Helper for passing unsaved file arguments.""" + _fields_ = [("name", c_char_p), ("contents", c_char_p), ('length', c_ulong)] + +## Diagnostic Conversion ## + +_clang_getNumDiagnostics = lib.clang_getNumDiagnostics +_clang_getNumDiagnostics.argtypes = [c_object_p] +_clang_getNumDiagnostics.restype = c_uint + +_clang_getDiagnostic = lib.clang_getDiagnostic +_clang_getDiagnostic.argtypes = [c_object_p, c_uint] +_clang_getDiagnostic.restype = c_object_p + +_clang_disposeDiagnostic = lib.clang_disposeDiagnostic +_clang_disposeDiagnostic.argtypes = [Diagnostic] + +_clang_getDiagnosticSeverity = lib.clang_getDiagnosticSeverity +_clang_getDiagnosticSeverity.argtypes = [Diagnostic] +_clang_getDiagnosticSeverity.restype = c_int + +_clang_getDiagnosticLocation = lib.clang_getDiagnosticLocation +_clang_getDiagnosticLocation.argtypes = [Diagnostic] +_clang_getDiagnosticLocation.restype = SourceLocation + +_clang_getDiagnosticSpelling = lib.clang_getDiagnosticSpelling +_clang_getDiagnosticSpelling.argtypes = [Diagnostic] +_clang_getDiagnosticSpelling.restype = _CXString +_clang_getDiagnosticSpelling.errcheck = _CXString.from_result + +_clang_getDiagnosticNumRanges = lib.clang_getDiagnosticNumRanges +_clang_getDiagnosticNumRanges.argtypes = [Diagnostic] +_clang_getDiagnosticNumRanges.restype = c_uint + +_clang_getDiagnosticRange = lib.clang_getDiagnosticRange +_clang_getDiagnosticRange.argtypes = [Diagnostic, c_uint] +_clang_getDiagnosticRange.restype = SourceRange + +_clang_getDiagnosticNumFixIts = lib.clang_getDiagnosticNumFixIts +_clang_getDiagnosticNumFixIts.argtypes = [Diagnostic] +_clang_getDiagnosticNumFixIts.restype = c_uint + +_clang_getDiagnosticFixIt = lib.clang_getDiagnosticFixIt +_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: + class Kind: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self + + def __init__(self, completionString, key): + self.cs = completionString + self.key = key + + def __repr__(self): + return "{'" + self.spelling + "', " + str(self.kind) + "}" + + @property + def spelling(self): + return _clang_getCompletionChunkText(self.cs, self.key).spelling + + @property + def kind(self): + res = _clang_getCompletionChunkKind(self.cs, self.key) + return completionChunkKindMap[res] + + @property + def string(self): + res = _clang_getCompletionChunkCompletionString(self.cs, self.key) + + if (res): + return CompletionString(res) + else: + None + + def isKindOptional(self): + return self.kind == completionChunkKindMap[0] + + def isKindTypedText(self): + return self.kind == completionChunkKindMap[1] + + def isKindPlaceHolder(self): + return self.kind == completionChunkKindMap[3] + + def isKindInformative(self): + return self.kind == completionChunkKindMap[4] + + def isKindResultType(self): + return self.kind == completionChunkKindMap[15] + +completionChunkKindMap = { + 0: CompletionChunk.Kind("Optional"), + 1: CompletionChunk.Kind("TypedText"), + 2: CompletionChunk.Kind("Text"), + 3: CompletionChunk.Kind("Placeholder"), + 4: CompletionChunk.Kind("Informative"), + 5: CompletionChunk.Kind("CurrentParameter"), + 6: CompletionChunk.Kind("LeftParen"), + 7: CompletionChunk.Kind("RightParen"), + 8: CompletionChunk.Kind("LeftBracket"), + 9: CompletionChunk.Kind("RightBracket"), + 10: CompletionChunk.Kind("LeftBrace"), + 11: CompletionChunk.Kind("RightBrace"), + 12: CompletionChunk.Kind("LeftAngle"), + 13: CompletionChunk.Kind("RightAngle"), + 14: CompletionChunk.Kind("Comma"), + 15: CompletionChunk.Kind("ResultType"), + 16: CompletionChunk.Kind("Colon"), + 17: CompletionChunk.Kind("SemiColon"), + 18: CompletionChunk.Kind("Equal"), + 19: CompletionChunk.Kind("HorizontalSpace"), + 20: CompletionChunk.Kind("VerticalSpace")} + +class CompletionString(ClangObject): + class Availability: + def __init__(self, name): + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self + + def __len__(self): + return _clang_getNumCompletionChunks(self.obj) + + def __getitem__(self, key): + if len(self) <= key: + raise IndexError + return CompletionChunk(self.obj, key) + + @property + def priority(self): + return _clang_getCompletionPriority(self.obj) + + @property + def availability(self): + res = _clang_getCompletionAvailability(self.obj) + return availabilityKinds[res] + + def __repr__(self): + return " | ".join([str(a) for a in self]) \ + + " || Priority: " + str(self.priority) \ + + " || Availability: " + str(self.availability) + +availabilityKinds = { + 0: CompletionChunk.Kind("Available"), + 1: CompletionChunk.Kind("Deprecated"), + 2: CompletionChunk.Kind("NotAvailable")} + +class CodeCompletionResult(Structure): + _fields_ = [('cursorKind', c_int), ('completionString', c_object_p)] + + def __repr__(self): + return str(CompletionString(self.completionString)) + + @property + def kind(self): + return CursorKind.from_id(self.cursorKind) + + @property + def string(self): + return CompletionString(self.completionString) + +class CCRStructure(Structure): + _fields_ = [('results', POINTER(CodeCompletionResult)), + ('numResults', c_int)] + + def __len__(self): + return self.numResults + + def __getitem__(self, key): + if len(self) <= key: + raise IndexError + + return self.results[key] + +class CodeCompletionResults(ClangObject): + def __init__(self, ptr): + assert isinstance(ptr, POINTER(CCRStructure)) and ptr + self.ptr = self._as_parameter_ = ptr + + def from_param(self): + return self._as_parameter_ + + def __del__(self): + CodeCompletionResults_dispose(self) + + @property + def results(self): + return self.ptr.contents + + @property + def diagnostics(self): + class DiagnosticsItr: + def __init__(self, ccr): + self.ccr= ccr + + def __len__(self): + return int(_clang_codeCompleteGetNumDiagnostics(self.ccr)) + + def __getitem__(self, key): + return _clang_codeCompleteGetDiagnostic(self.ccr, key) + + return DiagnosticsItr(self) + + +class Index(ClangObject): + """ + The Index type provides the primary interface to the Clang CIndex library, + primarily by providing an interface for reading and parsing translation + units. + """ + + @staticmethod + def create(excludeDecls=False): + """ + Create a new Index. + Parameters: + excludeDecls -- Exclude local declarations from translation units. + """ + return Index(Index_create(excludeDecls, 0)) + + def __del__(self): + Index_dispose(self) + + def read(self, path): + """Load the translation unit from the given AST file.""" + ptr = TranslationUnit_read(self, path) + if ptr: + return TranslationUnit(ptr) + return None + + def parse(self, path, args = [], unsaved_files = [], options = 0): + """ + Load the translation unit from the given source code file by running + clang and generating the AST before loading. Additional command line + parameters can be passed to clang via the args parameter. + + In-memory contents for files can be provided by passing a list of pairs + to as unsaved_files, the first item should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + arg_array = 0 + if len(args): + arg_array = (c_char_p * len(args))(* args) + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print value + if not isinstance(value, str): + raise TypeError,'Unexpected unsaved file contents.' + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = TranslationUnit_parse(self, path, arg_array, len(args), + unsaved_files_array, len(unsaved_files), + options) + if ptr: + return TranslationUnit(ptr) + return None + + +class TranslationUnit(ClangObject): + """ + The TranslationUnit class represents a source code translation unit and + provides read-only access to its top-level declarations. + """ + + def __init__(self, ptr): + ClangObject.__init__(self, ptr) + + def __del__(self): + TranslationUnit_dispose(self) + + @property + def cursor(self): + """Retrieve the cursor that represents the given translation unit.""" + return TranslationUnit_cursor(self) + + @property + def spelling(self): + """Get the original translation unit source file name.""" + return TranslationUnit_spelling(self) + + def get_includes(self): + """ + Return an iterable sequence of FileInclusion objects that describe the + sequence of inclusions in a translation unit. The first object in + this sequence is always the input file. Note that this method will not + recursively iterate over header files included through precompiled + headers. + """ + def visitor(fobj, lptr, depth, includes): + if depth > 0: + loc = lptr.contents + includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) + + # Automatically adapt CIndex/ctype pointers to python objects + includes = [] + TranslationUnit_includes(self, + TranslationUnit_includes_callback(visitor), + includes) + return iter(includes) + + @property + def diagnostics(self): + """ + Return an iterable (and indexable) object containing the diagnostics. + """ + class DiagIterator: + def __init__(self, tu): + self.tu = tu + + def __len__(self): + return int(_clang_getNumDiagnostics(self.tu)) + + def __getitem__(self, key): + diag = _clang_getDiagnostic(self.tu, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return DiagIterator(self) + + def reparse(self, unsaved_files = [], options = 0): + """ + Reparse an already parsed translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print value + if not isinstance(value, str): + raise TypeError,'Unexpected unsaved file contents.' + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = TranslationUnit_reparse(self, len(unsaved_files), + unsaved_files_array, + options) + def codeComplete(self, path, line, column, unsaved_files = [], options = 0): + """ + Code complete in this translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print value + if not isinstance(value, str): + raise TypeError,'Unexpected unsaved file contents.' + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = TranslationUnit_codeComplete(self, path, + line, column, + unsaved_files_array, + len(unsaved_files), + options) + if ptr: + return CodeCompletionResults(ptr) + return None + +class File(ClangObject): + """ + The File class represents a particular source file that is part of a + 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.""" + return _CXString_getCString(File_name(self)) + + @property + def time(self): + """Return the last modification time of the file.""" + return File_time(self) + + def __str__(self): + return self.name + + def __repr__(self): + return "" % (self.name) + +class FileInclusion(object): + """ + The FileInclusion class represents the inclusion of one source file by + another via a '#include' directive or as the input file for the translation + unit. This class provides information about the included file, the including + file, the location of the '#include' directive and the depth of the included + file in the stack. Note that the input file has depth 0. + """ + + def __init__(self, src, tgt, loc, depth): + self.source = src + self.include = tgt + self.location = loc + self.depth = depth + + @property + def is_input_file(self): + """True if the included file is the input file.""" + return self.depth == 0 + +# Additional Functions and Types + +# String Functions +_CXString_dispose = lib.clang_disposeString +_CXString_dispose.argtypes = [_CXString] + +_CXString_getCString = lib.clang_getCString +_CXString_getCString.argtypes = [_CXString] +_CXString_getCString.restype = c_char_p + +# Source Location Functions +SourceLocation_loc = lib.clang_getInstantiationLocation +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] +SourceRange_getRange.restype = SourceRange + +SourceRange_start = lib.clang_getRangeStart +SourceRange_start.argtypes = [SourceRange] +SourceRange_start.restype = SourceLocation + +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] +CursorKind_is_decl.restype = bool + +CursorKind_is_ref = lib.clang_isReference +CursorKind_is_ref.argtypes = [CursorKind] +CursorKind_is_ref.restype = bool + +CursorKind_is_expr = lib.clang_isExpression +CursorKind_is_expr.argtypes = [CursorKind] +CursorKind_is_expr.restype = bool + +CursorKind_is_stmt = lib.clang_isStatement +CursorKind_is_stmt.argtypes = [CursorKind] +CursorKind_is_stmt.restype = bool + +CursorKind_is_attribute = lib.clang_isAttribute +CursorKind_is_attribute.argtypes = [CursorKind] +CursorKind_is_attribute.restype = bool + +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 +Cursor_get.argtypes = [TranslationUnit, SourceLocation] +Cursor_get.restype = Cursor + +Cursor_null = lib.clang_getNullCursor +Cursor_null.restype = Cursor + +Cursor_usr = lib.clang_getCursorUSR +Cursor_usr.argtypes = [Cursor] +Cursor_usr.restype = _CXString +Cursor_usr.errcheck = _CXString.from_result + +Cursor_is_def = lib.clang_isCursorDefinition +Cursor_is_def.argtypes = [Cursor] +Cursor_is_def.restype = bool + +Cursor_def = lib.clang_getCursorDefinition +Cursor_def.argtypes = [Cursor] +Cursor_def.restype = Cursor +Cursor_def.errcheck = Cursor.from_result + +Cursor_eq = lib.clang_equalCursors +Cursor_eq.argtypes = [Cursor, Cursor] +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] +Cursor_spelling.restype = _CXString +Cursor_spelling.errcheck = _CXString.from_result + +Cursor_displayname = lib.clang_getCursorDisplayName +Cursor_displayname.argtypes = [Cursor] +Cursor_displayname.restype = _CXString +Cursor_displayname.errcheck = _CXString.from_result + +Cursor_loc = lib.clang_getCursorLocation +Cursor_loc.argtypes = [Cursor] +Cursor_loc.restype = SourceLocation + +Cursor_extent = lib.clang_getCursorExtent +Cursor_extent.argtypes = [Cursor] +Cursor_extent.restype = SourceRange + +Cursor_ref = lib.clang_getCursorReferenced +Cursor_ref.argtypes = [Cursor] +Cursor_ref.restype = Cursor +Cursor_ref.errcheck = Cursor.from_result + +Cursor_type = lib.clang_getCursorType +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] +Cursor_visit.restype = c_uint + +# Type Functions +Type_get_canonical = lib.clang_getCanonicalType +Type_get_canonical.argtypes = [Type] +Type_get_canonical.restype = Type +Type_get_canonical.errcheck = Type.from_result + +Type_is_const_qualified = lib.clang_isConstQualifiedType +Type_is_const_qualified.argtypes = [Type] +Type_is_const_qualified.restype = bool + +Type_is_volatile_qualified = lib.clang_isVolatileQualifiedType +Type_is_volatile_qualified.argtypes = [Type] +Type_is_volatile_qualified.restype = bool + +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 +Type_get_pointee.errcheck = Type.from_result + +Type_get_declaration = lib.clang_getTypeDeclaration +Type_get_declaration.argtypes = [Type] +Type_get_declaration.restype = Cursor +Type_get_declaration.errcheck = Cursor.from_result + +Type_get_result = lib.clang_getResultType +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 +Index_create.argtypes = [c_int, c_int] +Index_create.restype = c_object_p + +Index_dispose = lib.clang_disposeIndex +Index_dispose.argtypes = [Index] + +# Translation Unit Functions +TranslationUnit_read = lib.clang_createTranslationUnit +TranslationUnit_read.argtypes = [Index, c_char_p] +TranslationUnit_read.restype = c_object_p + +TranslationUnit_parse = lib.clang_parseTranslationUnit +TranslationUnit_parse.argtypes = [Index, c_char_p, c_void_p, + c_int, c_void_p, c_int, c_int] +TranslationUnit_parse.restype = c_object_p + +TranslationUnit_reparse = lib.clang_reparseTranslationUnit +TranslationUnit_reparse.argtypes = [TranslationUnit, c_int, c_void_p, c_int] +TranslationUnit_reparse.restype = c_int + +TranslationUnit_codeComplete = lib.clang_codeCompleteAt +TranslationUnit_codeComplete.argtypes = [TranslationUnit, c_char_p, c_int, + c_int, c_void_p, c_int, c_int] +TranslationUnit_codeComplete.restype = POINTER(CCRStructure) + +TranslationUnit_cursor = lib.clang_getTranslationUnitCursor +TranslationUnit_cursor.argtypes = [TranslationUnit] +TranslationUnit_cursor.restype = Cursor +TranslationUnit_cursor.errcheck = Cursor.from_result + +TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling +TranslationUnit_spelling.argtypes = [TranslationUnit] +TranslationUnit_spelling.restype = _CXString +TranslationUnit_spelling.errcheck = _CXString.from_result + +TranslationUnit_dispose = lib.clang_disposeTranslationUnit +TranslationUnit_dispose.argtypes = [TranslationUnit] + +TranslationUnit_includes_callback = CFUNCTYPE(None, + c_object_p, + POINTER(SourceLocation), + c_uint, py_object) +TranslationUnit_includes = lib.clang_getInclusions +TranslationUnit_includes.argtypes = [TranslationUnit, + TranslationUnit_includes_callback, + 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 + +File_time = lib.clang_getFileTime +File_time.argtypes = [File] +File_time.restype = c_uint + +# Code completion + +CodeCompletionResults_dispose = lib.clang_disposeCodeCompleteResults +CodeCompletionResults_dispose.argtypes = [CodeCompletionResults] + +_clang_codeCompleteGetNumDiagnostics = lib.clang_codeCompleteGetNumDiagnostics +_clang_codeCompleteGetNumDiagnostics.argtypes = [CodeCompletionResults] +_clang_codeCompleteGetNumDiagnostics.restype = c_int + +_clang_codeCompleteGetDiagnostic = lib.clang_codeCompleteGetDiagnostic +_clang_codeCompleteGetDiagnostic.argtypes = [CodeCompletionResults, c_int] +_clang_codeCompleteGetDiagnostic.restype = Diagnostic + +_clang_getCompletionChunkText = lib.clang_getCompletionChunkText +_clang_getCompletionChunkText.argtypes = [c_void_p, c_int] +_clang_getCompletionChunkText.restype = _CXString + +_clang_getCompletionChunkKind = lib.clang_getCompletionChunkKind +_clang_getCompletionChunkKind.argtypes = [c_void_p, c_int] +_clang_getCompletionChunkKind.restype = c_int + +_clang_getCompletionChunkCompletionString = lib.clang_getCompletionChunkCompletionString +_clang_getCompletionChunkCompletionString.argtypes = [c_void_p, c_int] +_clang_getCompletionChunkCompletionString.restype = c_object_p + +_clang_getNumCompletionChunks = lib.clang_getNumCompletionChunks +_clang_getNumCompletionChunks.argtypes = [c_void_p] +_clang_getNumCompletionChunks.restype = c_int + +_clang_getCompletionAvailability = lib.clang_getCompletionAvailability +_clang_getCompletionAvailability.argtypes = [c_void_p] +_clang_getCompletionAvailability.restype = c_int + +_clang_getCompletionPriority = lib.clang_getCompletionPriority +_clang_getCompletionPriority.argtypes = [c_void_p] +_clang_getCompletionPriority.restype = c_int + + +### + +__all__ = ['Index', 'TranslationUnit', 'Cursor', 'CursorKind', 'Type', 'TypeKind', + 'Diagnostic', 'FixIt', 'CodeCompletionResults', 'SourceRange', + 'SourceLocation', 'File'] diff --git a/clang/bindings/python/examples/cindex/cindex-dump.py b/clang/bindings/python/examples/cindex/cindex-dump.py new file mode 100644 index 0000000..af7ddab --- /dev/null +++ b/clang/bindings/python/examples/cindex/cindex-dump.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +#===- cindex-dump.py - cindex/Python Source Dump -------------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +""" +A simple command line tool for dumping a source file using the Clang Index +Library. +""" + +def get_diag_info(diag): + return { 'severity' : diag.severity, + 'location' : diag.location, + 'spelling' : diag.spelling, + 'ranges' : diag.ranges, + 'fixits' : diag.fixits } + +def get_cursor_id(cursor, cursor_list = []): + if not opts.showIDs: + return None + + if cursor is None: + return None + + # FIXME: This is really slow. It would be nice if the index API exposed + # something that let us hash cursors. + for i,c in enumerate(cursor_list): + if cursor == c: + return i + cursor_list.append(cursor) + return len(cursor_list) - 1 + +def get_info(node, depth=0): + if opts.maxDepth is not None and depth >= opts.maxDepth: + children = None + else: + children = [get_info(c, depth+1) + for c in node.get_children()] + return { 'id' : get_cursor_id(node), + 'kind' : node.kind, + 'usr' : node.get_usr(), + 'spelling' : node.spelling, + 'location' : node.location, + 'extent.start' : node.extent.start, + 'extent.end' : node.extent.end, + 'is_definition' : node.is_definition(), + 'definition id' : get_cursor_id(node.get_definition()), + 'children' : children } + +def main(): + from clang.cindex import Index + from pprint import pprint + + from optparse import OptionParser, OptionGroup + + global opts + + parser = OptionParser("usage: %prog [options] {filename} [clang-args*]") + parser.add_option("", "--show-ids", dest="showIDs", + help="Don't compute cursor IDs (very slow)", + default=False) + parser.add_option("", "--max-depth", dest="maxDepth", + help="Limit cursor expansion to depth N", + metavar="N", type=int, default=None) + parser.disable_interspersed_args() + (opts, args) = parser.parse_args() + + if len(args) == 0: + parser.error('invalid number arguments') + + index = Index.create() + tu = index.parse(None, args) + if not tu: + parser.error("unable to load input") + + pprint(('diags', map(get_diag_info, tu.diagnostics))) + pprint(('nodes', get_info(tu.cursor))) + +if __name__ == '__main__': + main() + diff --git a/clang/bindings/python/examples/cindex/cindex-includes.py b/clang/bindings/python/examples/cindex/cindex-includes.py new file mode 100644 index 0000000..1750022 --- /dev/null +++ b/clang/bindings/python/examples/cindex/cindex-includes.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +#===- cindex-includes.py - cindex/Python Inclusion Graph -----*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +""" +A simple command line tool for dumping a Graphviz description (dot) that +describes include dependencies. +""" + +def main(): + import sys + from clang.cindex import Index + + from optparse import OptionParser, OptionGroup + + parser = OptionParser("usage: %prog [options] {filename} [clang-args*]") + parser.disable_interspersed_args() + (opts, args) = parser.parse_args() + if len(args) == 0: + parser.error('invalid number arguments') + + # FIXME: Add an output file option + out = sys.stdout + + index = Index.create() + tu = index.parse(None, args) + if not tu: + parser.error("unable to load input") + + # A helper function for generating the node name. + def name(f): + if f: + return "\"" + f.name + "\"" + + # Generate the include graph + out.write("digraph G {\n") + for i in tu.get_includes(): + line = " "; + if i.is_input_file: + # Always write the input file as a node just in case it doesn't + # actually include anything. This would generate a 1 node graph. + line += name(i.include) + else: + line += '%s->%s' % (name(i.source), name(i.include)) + line += "\n"; + out.write(line) + out.write("}\n") + +if __name__ == '__main__': + main() + diff --git a/clang/bindings/python/tests/__init__.py b/clang/bindings/python/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/clang/bindings/python/tests/cindex/INPUTS/header1.h b/clang/bindings/python/tests/cindex/INPUTS/header1.h new file mode 100644 index 0000000..b4eacbe --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/header1.h @@ -0,0 +1,6 @@ +#ifndef HEADER1 +#define HEADER1 + +#include "header3.h" + +#endif diff --git a/clang/bindings/python/tests/cindex/INPUTS/header2.h b/clang/bindings/python/tests/cindex/INPUTS/header2.h new file mode 100644 index 0000000..c4eddc0 --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/header2.h @@ -0,0 +1,6 @@ +#ifndef HEADER2 +#define HEADER2 + +#include "header3.h" + +#endif diff --git a/clang/bindings/python/tests/cindex/INPUTS/header3.h b/clang/bindings/python/tests/cindex/INPUTS/header3.h new file mode 100644 index 0000000..6dca764 --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/header3.h @@ -0,0 +1,3 @@ +// Not a guarded header! + +void f(); diff --git a/clang/bindings/python/tests/cindex/INPUTS/hello.cpp b/clang/bindings/python/tests/cindex/INPUTS/hello.cpp new file mode 100644 index 0000000..7ef086e --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/hello.cpp @@ -0,0 +1,6 @@ +#include "stdio.h" + +int main(int argc, char* argv[]) { + printf("hello world\n"); + return 0; +} diff --git a/clang/bindings/python/tests/cindex/INPUTS/include.cpp b/clang/bindings/python/tests/cindex/INPUTS/include.cpp new file mode 100644 index 0000000..60cfdaa --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/include.cpp @@ -0,0 +1,5 @@ +#include "header1.h" +#include "header2.h" +#include "header1.h" + +int main() { } diff --git a/clang/bindings/python/tests/cindex/INPUTS/parse_arguments.c b/clang/bindings/python/tests/cindex/INPUTS/parse_arguments.c new file mode 100644 index 0000000..7196486 --- /dev/null +++ b/clang/bindings/python/tests/cindex/INPUTS/parse_arguments.c @@ -0,0 +1,2 @@ +int DECL_ONE = 1; +int DECL_TWO = 2; diff --git a/clang/bindings/python/tests/cindex/__init__.py b/clang/bindings/python/tests/cindex/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py new file mode 100644 index 0000000..9f02bb2 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -0,0 +1,92 @@ +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. +int start_decl; + +struct s0 { + int a; + int b; +}; + +struct s1; + +void f0(int a0, int a1) { + int l0, l1; + + if (a0) + return; + + for (;;) { + break; + } +} +""" + +def test_get_children(): + tu = get_tu(kInput) + + # Skip until past start_decl. + it = tu.cursor.get_children() + while it.next().spelling != 'start_decl': + pass + + tu_nodes = list(it) + + 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 + assert s0_nodes[0].kind == CursorKind.FIELD_DECL + assert s0_nodes[0].spelling == 'a' + assert s0_nodes[0].type.kind == TypeKind.INT + assert s0_nodes[1].kind == CursorKind.FIELD_DECL + assert s0_nodes[1].spelling == 'b' + assert s0_nodes[1].type.kind == TypeKind.INT + + assert tu_nodes[1].kind == CursorKind.STRUCT_DECL + assert tu_nodes[1].spelling == 's1' + assert tu_nodes[1].displayname == 's1' + assert tu_nodes[1].is_definition() == False + + assert tu_nodes[2].kind == CursorKind.FUNCTION_DECL + 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/clang/bindings/python/tests/cindex/test_cursor_kind.py b/clang/bindings/python/tests/cindex/test_cursor_kind.py new file mode 100644 index 0000000..f8466e5 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_cursor_kind.py @@ -0,0 +1,40 @@ +from clang.cindex import CursorKind + +def test_name(): + assert CursorKind.UNEXPOSED_DECL.name is 'UNEXPOSED_DECL' + +def test_get_all_kinds(): + assert CursorKind.UNEXPOSED_DECL in CursorKind.get_all_kinds() + assert CursorKind.TRANSLATION_UNIT in CursorKind.get_all_kinds() + +def test_kind_groups(): + """Check that every kind classifies to exactly one group.""" + + assert CursorKind.UNEXPOSED_DECL.is_declaration() + assert CursorKind.TYPE_REF.is_reference() + assert CursorKind.DECL_REF_EXPR.is_expression() + 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') + if getattr(k, n)()] + + if k in ( CursorKind.TRANSLATION_UNIT, + CursorKind.MACRO_DEFINITION, + CursorKind.MACRO_INSTANTIATION, + CursorKind.INCLUSION_DIRECTIVE, + CursorKind.PREPROCESSING_DIRECTIVE): + assert len(group) == 0 + else: + assert len(group) == 1 diff --git a/clang/bindings/python/tests/cindex/test_diagnostics.py b/clang/bindings/python/tests/cindex/test_diagnostics.py new file mode 100644 index 0000000..48ab617 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_diagnostics.py @@ -0,0 +1,82 @@ +from clang.cindex import * +from .util import get_tu + +# FIXME: We need support for invalid translation units to test better. + +def test_diagnostic_warning(): + 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 + assert tu.diagnostics[0].location.column == 11 + assert (tu.diagnostics[0].spelling == + 'control reaches end of non-void function') + +def test_diagnostic_note(): + # FIXME: We aren't getting notes here for some reason. + 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 + assert tu.diagnostics[0].location.column == 7 + assert 'incompatible' in tu.diagnostics[0].spelling +# assert tu.diagnostics[1].severity == Diagnostic.Note +# assert tu.diagnostics[1].location.line == 1 +# assert tu.diagnostics[1].location.column == 11 +# assert tu.diagnostics[1].spelling == 'instantiated from' + +def test_diagnostic_fixit(): + 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 + assert tu.diagnostics[0].location.column == 26 + assert tu.diagnostics[0].spelling.startswith('use of GNU old-style') + assert len(tu.diagnostics[0].fixits) == 1 + assert tu.diagnostics[0].fixits[0].range.start.line == 1 + assert tu.diagnostics[0].fixits[0].range.start.column == 26 + assert tu.diagnostics[0].fixits[0].range.end.line == 1 + assert tu.diagnostics[0].fixits[0].range.end.column == 30 + assert tu.diagnostics[0].fixits[0].value == '.f0 = ' + +def test_diagnostic_range(): + 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 + assert tu.diagnostics[0].location.column == 16 + assert tu.diagnostics[0].spelling.startswith('incompatible pointer to') + assert len(tu.diagnostics[0].fixits) == 0 + assert len(tu.diagnostics[0].ranges) == 1 + assert tu.diagnostics[0].ranges[0].start.line == 1 + assert tu.diagnostics[0].ranges[0].start.column == 20 + assert tu.diagnostics[0].ranges[0].end.line == 1 + assert tu.diagnostics[0].ranges[0].end.column == 27 + try: + tu.diagnostics[0].ranges[1].start.line + except IndexError: + 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/clang/bindings/python/tests/cindex/test_file.py b/clang/bindings/python/tests/cindex/test_file.py new file mode 100644 index 0000000..146e8c5 --- /dev/null +++ b/clang/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) == "" diff --git a/clang/bindings/python/tests/cindex/test_index.py b/clang/bindings/python/tests/cindex/test_index.py new file mode 100644 index 0000000..dc173f0 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_index.py @@ -0,0 +1,15 @@ +from clang.cindex import * +import os + +kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') + +def test_create(): + index = Index.create() + +# FIXME: test Index.read + +def test_parse(): + index = Index.create() + assert isinstance(index, Index) + tu = index.parse(os.path.join(kInputsDir, 'hello.cpp')) + assert isinstance(tu, TranslationUnit) diff --git a/clang/bindings/python/tests/cindex/test_location.py b/clang/bindings/python/tests/cindex/test_location.py new file mode 100644 index 0000000..528676e --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_location.py @@ -0,0 +1,86 @@ +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" + +def assert_location(loc, line, column, offset): + assert loc.line == line + assert loc.column == column + assert loc.offset == offset + +def test_location(): + tu = get_tu(baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') + + 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 = get_tu('\n' + baseInput) + one = get_cursor(tu, 'one') + two = get_cursor(tu, 'two') + + assert one is not None + assert two is not None + + 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 = 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 + + # 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(): + 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/clang/bindings/python/tests/cindex/test_translation_unit.py b/clang/bindings/python/tests/cindex/test_translation_unit.py new file mode 100644 index 0000000..2e65d95 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_translation_unit.py @@ -0,0 +1,84 @@ +from clang.cindex import * +import os + +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) + assert tu.spelling == path + +def test_cursor(): + path = os.path.join(kInputsDir, 'hello.cpp') + index = Index.create() + tu = index.parse(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']) + 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.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 = [ + ('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 + index = Index.create() + tu = index.parse('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)] + + index = Index.create() + tu = index.parse(src) + for i in zip(inc, tu.get_includes()): + assert eq(i[0], i[1]) diff --git a/clang/bindings/python/tests/cindex/test_type.py b/clang/bindings/python/tests/cindex/test_type.py new file mode 100644 index 0000000..03621f3 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_type.py @@ -0,0 +1,276 @@ +from clang.cindex import CursorKind +from clang.cindex import TypeKind +from nose.tools import raises +from .util import get_cursor +from .util import get_tu + +kInput = """\ + +typedef int I; + +struct teststruct { + int a; + I b; + long c; + unsigned long d; + signed long e; + const int f; + int *g; + int ***h; +}; + +""" + +def test_a_struct(): + tu = get_tu(kInput) + + 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 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[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[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[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 { + void *A[2]; +}; +""" +def testConstantArray(): + 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_typekind_spelling(): + """Ensure TypeKind.spelling works.""" + tu = get_tu('int a;') + a = get_cursor(tu, 'a') + + assert a is not None + assert a.type.kind.spelling == 'Int' + +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 + +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/clang/bindings/python/tests/cindex/util.py b/clang/bindings/python/tests/cindex/util.py new file mode 100644 index 0000000..388b269 --- /dev/null +++ b/clang/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." + where 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', +] -- cgit v1.2.3