cindex.py revision aa22984a82121b55841715123f7314bb850cb664
1# -*- coding: utf-8 -*-
2
3from ctypes import *
4
5def get_cindex_library():
6    # FIXME: It's probably not the case that the library is actually found in
7    # this location. We need a better system of identifying and loading the
8    # CIndex library. It could be on path or elsewhere, or versioned, etc.
9    import platform
10    name = platform.system()
11    if name == 'Darwin':
12        return cdll.LoadLibrary('libCIndex.dylib')
13    elif name == 'Windows':
14        return cdll.LoadLibrary('libCIndex.dll')
15    else:
16        return cdll.LoadLibrary('libCIndex.so')
17
18## Utility Types and Functions ##
19def alloc_string_vector(strs):
20    """
21    Allocate a string buffer large enough to accommodate the given list of
22    python strings.
23    """
24    n = 0
25    for i in strs: n += len(i) + 1
26    return create_string_buffer(n)
27
28def copy_string_vector(vec, strs):
29    """
30    Copy the contents of each string into the vector, preserving null
31    terminated elements.
32    """
33    n = 0
34    for i in strs:
35        # This is terribly inefficient, but I can't figure out how to copy a
36        # chunk of characters into the resultant vector. t should be: something
37        # like this: vec[n:n + len(i)] = i[:]; n += len(i) + 1
38        for j in i:
39            vec[n] = j
40            n += 1
41        n += 1
42
43def create_string_vector(strs):
44    """
45    Create a string vector (char *[]) from the given list of strings.
46    """
47    vec = alloc_string_vector(strs)
48    copy_string_vector(vec, strs)
49    return vec
50
51# Aliases for convenience
52c_int_p = POINTER(c_int)
53c_uint_p = POINTER(c_uint)
54c_bool = c_uint
55
56# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper
57# object. This is a problem, because it means that from_parameter will see an
58# integer and pass the wrong value on platforms where int != void*. Work around
59# this by marshalling object arguments as void**.
60c_object_p = POINTER(c_void_p)
61
62lib = get_cindex_library()
63
64## Typedefs ##
65CursorKind = c_int
66
67### Structures and Utility Classes ###
68
69class String(Structure):
70    """
71    The String class is a simple wrapper around constant string data returned
72    from functions in the CIndex library.
73
74    String objects do not provide any of the operations that Python strings
75    support. However, these objects can be explicitly cast using the str()
76    function.
77    """
78    _fields_ = [("spelling", c_char_p), ("free", c_int)]
79
80    def __del__(self):
81        if self.free:
82            String_dispose(self)
83
84    def __str__(self):
85        return self.spelling
86
87class SourceLocation(Structure):
88    """
89    A SourceLocation Represents a particular location within a source file.
90    """
91    _fields_ = [("ptr_data", c_void_p), ("int_data", c_uint)]
92
93    def init(self):
94        """
95        Initialize the source location, setting its file, line and column.
96        """
97        f, l, c = c_void_p(), c_uint(), c_uint()
98        SourceLocation_loc(self, byref(f), byref(l), byref(c))
99        self.file, self.line, self.column = File(f), l, c
100        return self
101
102class SourceRange(Structure):
103    """
104    A SourceRange describes a range of source locations within the source
105    code.
106    """
107    _fields_ = [
108        ("ptr_data", c_void_p),
109        ("begin_int_data", c_uint),
110        ("end_int_data", c_uint)]
111
112    def start(self):
113        """
114        Return a SourceLocation representing the first character within a
115        source range.
116        """
117        return SourceRange_start(self).init()
118
119    def end(self):
120        """
121        Return a SourceLocation representing the last character within a
122        source range.
123        """
124        return SourceRange_end(self).init()
125
126class Cursor(Structure):
127    """
128    The Cursor class represents a reference to an element within the AST. It
129    acts as a kind of iterator.
130    """
131    _fields_ = [("kind", c_int), ("data", c_void_p * 3)]
132
133    def __eq__(self, other):
134        return Cursor_eq(self, other)
135
136    def __ne__(self, other):
137        return not Cursor_eq(self, other)
138
139    @staticmethod
140    def null():
141        """Return the null cursor object."""
142        return Cursor_null()
143
144    def is_declaration(self):
145        """Return True if the cursor points to a declaration."""
146        return Cursor_is_decl(self.kind)
147
148    def is_reference(self):
149        """Return True if the cursor points to a refernce."""
150        return Cursor_is_ref(self.kind)
151
152    def is_expression(self):
153        """Return True if the cursor points to an expression."""
154        return Cursor_is_expr(self.kind)
155
156    def is_statement(self):
157        """Return True if the cursor points to a statement."""
158        return Cursor_is_stmt(self.kind)
159
160    def is_translation_unit(self):
161        """Return True if the cursor points to a translation unit."""
162        return Cursor_is_tu(self.kind)
163
164    def is_invalid(self):
165        """Return  True if the cursor points to an invalid entity."""
166        return Cursor_is_inv(self.kind)
167
168    def is_definition(self):
169        """
170        Returns true if the declaration pointed at by the cursor is also a
171        definition of that entity.
172        """
173        return Cursor_is_def(self)
174
175    def get_declaration(self):
176        """
177        Return the underlying declaration for the cursor. If the cursor kind
178        is a declaration, then this simpy returns the declaration. If the
179        cursor is a reference, then this returns the referenced declaration.
180        """
181        if not self.is_declaration():
182            raise Exception("Cursor does not refer to a Declaration")
183        return Cursor_decl(self)
184
185    def get_definition(self):
186        """
187        If the cursor is a reference to a declaration or a declaration of
188        some entity, return a cursor that points to the definition of that
189        entity.
190        """
191        # TODO: Should probably check that this is either a reference or
192        # declaration prior to issuing the lookup.
193        return Cursor_def(self)
194
195    @property
196    def spelling(self):
197        """Return the spelling of the entity pointed at by the cursor."""
198        if not self.is_declaration():
199            # FIXME: This should be documented in Index.h
200            raise ValueError("Cursor does not refer to a Declaration")
201        return Cursor_spelling(self)
202
203    @property
204    def location(self):
205        """
206        Return the source location (the starting character) of the entity
207        pointed at by the cursor.
208        """
209        return Cursor_loc(self).init()
210
211    @property
212    def extent(self):
213        """
214        Return the source range (the range of text) occupied by the entity
215        pointed at by the cursor.
216        """
217        return Cursor_extent(self)
218
219    @property
220    def file(self):
221        """
222        Return the file containing the pointed-at entity. This is an alias for
223        location.file.
224        """
225        return self.location.file
226
227# FIXME: Implement this class.
228class Entity(Structure):
229    """
230    An Entity is a uniqe token for accessing "visible" declarations within
231    a translation unit.
232    """
233    # NOTE: Index is written here as a void*, but given in the API as CXIndex.
234    # Be careful to translate back to Index when returning this member.
235    # TODO: Rename as _index and write a property?
236    _fields_ = [("index", c_void_p), ("data", c_void_p)]
237
238## CIndex Objects ##
239
240# CIndex objects (derived from ClangObject) are essentially lightweight
241# wrappers attached to some underlying object, which is exposed via CIndex as
242# a void*.
243
244class ClangObject(object):
245    """
246    A helper for Clang objects. This class helps act as an intermediary for
247    the ctypes library and the Clang CIndex library.
248    """
249    def __init__(self, obj):
250        assert isinstance(obj, c_object_p) and obj
251        self.obj = self._as_parameter_ = obj
252
253    def from_param(self):
254        return self._as_parameter_
255
256class Index(ClangObject):
257    """
258    The Index type provides the primary interface to the Clang CIndex library,
259    primarily by providing an interface for reading and parsing translation
260    units.
261    """
262    def __init__(self, obj):
263        ClangObject.__init__(self, obj)
264
265    @staticmethod
266    def create(excludeDecls=False, displayDiags=False):
267        """
268        Create a new Index.
269        Parameters:
270        excludeDecls -- Exclude local declarations from translation units.
271        displayDiags -- Display diagnostics during translation unit creation.
272        """
273        return Index(Index_create(excludeDecls, displayDiags))
274
275    def __del__(self):
276        Index_dispose(self)
277
278    def read(self, path):
279        """Load the translation unit from the given AST file."""
280        return TranslationUnit.read(self, path)
281
282    def parse(self, path, args = []):
283        """
284        Load the translation unit from the given source code file by running
285        clang and generating the AST before loading. Additional command line
286        parameters can be passed to clang via the args parameter.
287        """
288        return TranslationUnit.parse(self, path, args)
289
290
291class TranslationUnit(ClangObject):
292    """
293    The TranslationUnit class represents a source code translation unit and
294    provides read-only access to its top-level declarations.
295    """
296    def __init__(self, obj, free=False):
297        ClangObject.__init__(self, obj)
298        self.free = free
299
300    def __del__(self):
301        if self.free and self.obj:
302            TranslationUnit_dispose(self)
303
304    def load(self, fun, data = None):
305        # Actually call this over a lambda that attaches an object the
306        # underlying void pointer.
307        f = lambda t, c, x: fun(TranslationUnit(t), c, x)
308        TranslationUnit_load(self.obj, Callback(f), data)
309
310    @property
311    def spelling(self):
312        return TranslationUnit_spelling(self)
313
314    @staticmethod
315    def read(ix, path):
316        """Create a translation unit from the given AST file."""
317        ptr = TranslationUnit_read(ix, path)
318        return TranslationUnit(ptr, True) if ptr else None
319
320    @staticmethod
321    def parse(ix, path, args = []):
322        """
323        Construct a translation unit from the given source file, applying
324        the given command line argument.
325        """
326        # TODO: Support unsaved files.
327        argc, argv = len(args), create_string_vector(args)
328        ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0)
329        return TranslationUnit(ptr, True) if ptr else None
330
331class File(ClangObject):
332    """
333    The File class...
334    """
335    def __init__(self, obj):
336        ClangObject.__init__(self, obj)
337
338    @property
339    def is_valid(self):
340        return self.obj is not None
341
342    @property
343    def name(self):
344        """Return the name of the file, if valid. Otherwise, an empty string."""
345        return File_name(self) if self.obj else ""
346
347    @property
348    def time(self):
349        """Return the time of the file, if valid. Otherwise, -1."""
350        return File_time(self) if self.obj else -1
351
352class Declaration(ClangObject):
353    """
354    The Declaration class represents a declaration with a translation unit.
355    """
356    def __init__(self, obj):
357        ClangObject.__init__(self, obj)
358
359        # Figure out the kind of cursor and inject a base class that provides
360        # some declaration-specific functionality.
361        self.cursor = Declaration_cursor(self)
362
363    @property
364    def kind(self):
365        """Retur the kind of cursor."""
366        return self.cursor.kind
367
368    @property
369    def entity(self):
370        """Return an entity that represents this declaration."""
371        return Entity(Declaration_entity(self))
372
373    @property
374    def spelling(self):
375        """Return the spelling (name) of the declaration."""
376        return Declaration_spelling(self)
377
378    def load(self, fun, data = None):
379        """
380        Recursively visit any elements declared or referenced within this
381        declaration.
382        """
383        f = lambda d, c, x: fun(Declaration(d), c, x)
384        Declaration_load(self, Callback(f), data)
385
386# Specific declaration kinds
387class ClassDeclaration:
388    pass
389
390class FunctionDeclaration:
391    pass
392
393class TypedefDeclaration:
394    pass
395
396# Additional Functions and Types
397
398# Wrap calls to TranslationUnit._load and Decl._load.
399Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p)
400
401# String Functions
402String_dispose = lib.clang_disposeString
403String_dispose.argtypes = [String]
404
405# Source Location Functions
406SourceLocation_loc = lib.clang_getInstantiationLocation
407SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_void_p), c_uint_p, c_uint_p]
408
409# Source Range Functions
410SourceRange_start = lib.clang_getRangeStart
411SourceRange_start.argtypes = [SourceLocation]
412SourceRange_start.restype = SourceRange
413
414SourceRange_end = lib.clang_getRangeEnd
415SourceRange_end.argtypes = [SourceLocation]
416SourceRange_end.restype = SourceRange
417
418# Cursor Functions
419# TODO: Implement this function
420Cursor_get = lib.clang_getCursor
421Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint]
422Cursor.restype = Cursor
423
424Cursor_null = lib.clang_getNullCursor
425Cursor_null.restype = Cursor
426
427Cursor_kind = lib.clang_getCursorKind
428Cursor_kind.argtypes = [Cursor]
429Cursor_kind.res = c_int
430
431# FIXME: Not really sure what a USR is or what this function actually does...
432Cursor_usr = lib.clang_getCursorUSR
433
434Cursor_is_decl = lib.clang_isDeclaration
435Cursor_is_decl.argtypes = [CursorKind]
436Cursor_is_decl.restype = c_bool
437
438Cursor_is_ref = lib.clang_isReference
439Cursor_is_ref.argtypes = [CursorKind]
440Cursor_is_ref.restype = c_bool
441
442Cursor_is_expr = lib.clang_isExpression
443Cursor_is_expr.argtypes = [CursorKind]
444Cursor_is_expr.restype = c_bool
445
446Cursor_is_stmt = lib.clang_isStatement
447Cursor_is_stmt.argtypes = [CursorKind]
448Cursor_is_stmt.restype = c_bool
449
450Cursor_is_inv = lib.clang_isInvalid
451Cursor_is_inv.argtypes = [CursorKind]
452Cursor_is_inv.restype = c_bool
453
454Cursor_is_tu = lib.clang_isTranslationUnit
455Cursor_is_tu.argtypes = [CursorKind]
456Cursor_is_tu.restype = c_bool
457
458Cursor_is_def = lib.clang_isCursorDefinition
459Cursor_is_def.argtypes = [Cursor]
460Cursor_is_def.restype = c_bool
461
462Cursor_def = lib.clang_getCursorDefinition
463Cursor_def.argtypes = [Cursor]
464Cursor_def.restype = Cursor
465
466Cursor_eq = lib.clang_equalCursors
467Cursor_eq.argtypes = [Cursor, Cursor]
468Cursor_eq.restype = c_uint
469
470Cursor_spelling = lib.clang_getCursorSpelling
471Cursor_spelling.argtypes = [Cursor]
472Cursor_spelling.restype = String
473
474Cursor_loc = lib.clang_getCursorLocation
475Cursor_loc.argtypes = [Cursor]
476Cursor_loc.restype = SourceLocation
477
478Cursor_extent = lib.clang_getCursorExtent
479Cursor_extent.argtypes = [Cursor]
480Cursor_extent.restype = SourceRange
481
482Cursor_ref = lib.clang_getCursorReferenced
483Cursor_ref.argtypes = [Cursor]
484Cursor_ref.restype = Cursor
485
486# Index Functions
487Index_create = lib.clang_createIndex
488Index_create.argtypes = [c_int, c_int]
489Index_create.restype = c_object_p
490
491Index_dispose = lib.clang_disposeIndex
492Index_dispose.argtypes = [Index]
493
494# Translation Unit Functions
495TranslationUnit_read = lib.clang_createTranslationUnit
496TranslationUnit_read.argtypes = [Index, c_char_p]
497TranslationUnit_read.restype = c_object_p
498
499TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile
500TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p,
501                                  c_int, c_void_p]
502TranslationUnit_parse.restype = c_object_p
503
504TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
505TranslationUnit_spelling.argtypes = [TranslationUnit]
506TranslationUnit_spelling.restype = String
507
508TranslationUnit_dispose = lib.clang_disposeTranslationUnit
509TranslationUnit_dispose.argtypes = [TranslationUnit]
510
511# File Functions
512File_name = lib.clang_getFileName
513File_name.argtypes = [File]
514File_name.restype = c_char_p
515
516File_time = lib.clang_getFileTime
517File_time.argtypes = [File]
518File_time.restype = c_uint
519