cindex.py revision 1b945a7455e17fd792ef3bd3790dc88beea5faad
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_object_p(), c_uint(), c_uint()
98        SourceLocation_loc(self, byref(f), byref(l), byref(c))
99        f = File(f) if f else None
100        self.file, self.line, self.column = f, int(l.value), int(c.value)
101        return self
102
103    def __repr__(self):
104        return "<SourceLocation file %r, line %r, column %r>" % (
105            self.file.name if self.file else None, self.line, self.column)
106
107class SourceRange(Structure):
108    """
109    A SourceRange describes a range of source locations within the source
110    code.
111    """
112    _fields_ = [
113        ("ptr_data", c_void_p),
114        ("begin_int_data", c_uint),
115        ("end_int_data", c_uint)]
116
117    @property
118    def start(self):
119        """
120        Return a SourceLocation representing the first character within a
121        source range.
122        """
123        return SourceRange_start(self).init()
124
125    @property
126    def end(self):
127        """
128        Return a SourceLocation representing the last character within a
129        source range.
130        """
131        return SourceRange_end(self).init()
132
133class Cursor(Structure):
134    """
135    The Cursor class represents a reference to an element within the AST. It
136    acts as a kind of iterator.
137    """
138    _fields_ = [("kind", c_int), ("data", c_void_p * 3)]
139
140    def __eq__(self, other):
141        return Cursor_eq(self, other)
142
143    def __ne__(self, other):
144        return not Cursor_eq(self, other)
145
146    @staticmethod
147    def null():
148        """Return the null cursor object."""
149        return Cursor_null()
150
151    def is_declaration(self):
152        """Return True if the cursor points to a declaration."""
153        return Cursor_is_decl(self.kind)
154
155    def is_reference(self):
156        """Return True if the cursor points to a refernce."""
157        return Cursor_is_ref(self.kind)
158
159    def is_expression(self):
160        """Return True if the cursor points to an expression."""
161        return Cursor_is_expr(self.kind)
162
163    def is_statement(self):
164        """Return True if the cursor points to a statement."""
165        return Cursor_is_stmt(self.kind)
166
167    def is_translation_unit(self):
168        """Return True if the cursor points to a translation unit."""
169        return Cursor_is_tu(self.kind)
170
171    def is_invalid(self):
172        """Return  True if the cursor points to an invalid entity."""
173        return Cursor_is_inv(self.kind)
174
175    def is_definition(self):
176        """
177        Returns true if the declaration pointed at by the cursor is also a
178        definition of that entity.
179        """
180        return Cursor_is_def(self)
181
182    def get_declaration(self):
183        """
184        Return the underlying declaration for the cursor. If the cursor kind
185        is a declaration, then this simpy returns the declaration. If the
186        cursor is a reference, then this returns the referenced declaration.
187        """
188        if not self.is_declaration():
189            raise Exception("Cursor does not refer to a Declaration")
190        return Cursor_decl(self)
191
192    def get_definition(self):
193        """
194        If the cursor is a reference to a declaration or a declaration of
195        some entity, return a cursor that points to the definition of that
196        entity.
197        """
198        # TODO: Should probably check that this is either a reference or
199        # declaration prior to issuing the lookup.
200        return Cursor_def(self)
201
202    @property
203    def spelling(self):
204        """Return the spelling of the entity pointed at by the cursor."""
205        if not self.is_declaration():
206            # FIXME: This should be documented in Index.h
207            raise ValueError("Cursor does not refer to a Declaration")
208        return Cursor_spelling(self)
209
210    @property
211    def location(self):
212        """
213        Return the source location (the starting character) of the entity
214        pointed at by the cursor.
215        """
216        return Cursor_loc(self).init()
217
218    @property
219    def extent(self):
220        """
221        Return the source range (the range of text) occupied by the entity
222        pointed at by the cursor.
223        """
224        return Cursor_extent(self)
225
226    @property
227    def file(self):
228        """
229        Return the file containing the pointed-at entity. This is an alias for
230        location.file.
231        """
232        return self.location.file
233
234# FIXME: Implement this class.
235class Entity(Structure):
236    """
237    An Entity is a uniqe token for accessing "visible" declarations within
238    a translation unit.
239    """
240    # NOTE: Index is written here as a void*, but given in the API as CXIndex.
241    # Be careful to translate back to Index when returning this member.
242    # TODO: Rename as _index and write a property?
243    _fields_ = [("index", c_void_p), ("data", c_void_p)]
244
245## CIndex Objects ##
246
247# CIndex objects (derived from ClangObject) are essentially lightweight
248# wrappers attached to some underlying object, which is exposed via CIndex as
249# a void*.
250
251class ClangObject(object):
252    """
253    A helper for Clang objects. This class helps act as an intermediary for
254    the ctypes library and the Clang CIndex library.
255    """
256    def __init__(self, obj):
257        assert isinstance(obj, c_object_p) and obj
258        self.obj = self._as_parameter_ = obj
259
260    def from_param(self):
261        return self._as_parameter_
262
263class Index(ClangObject):
264    """
265    The Index type provides the primary interface to the Clang CIndex library,
266    primarily by providing an interface for reading and parsing translation
267    units.
268    """
269    def __init__(self, obj):
270        ClangObject.__init__(self, obj)
271
272    @staticmethod
273    def create(excludeDecls=False, displayDiags=False):
274        """
275        Create a new Index.
276        Parameters:
277        excludeDecls -- Exclude local declarations from translation units.
278        displayDiags -- Display diagnostics during translation unit creation.
279        """
280        return Index(Index_create(excludeDecls, displayDiags))
281
282    def __del__(self):
283        Index_dispose(self)
284
285    def read(self, path):
286        """Load the translation unit from the given AST file."""
287        return TranslationUnit.read(self, path)
288
289    def parse(self, path, args = []):
290        """
291        Load the translation unit from the given source code file by running
292        clang and generating the AST before loading. Additional command line
293        parameters can be passed to clang via the args parameter.
294        """
295        return TranslationUnit.parse(self, path, args)
296
297
298class TranslationUnit(ClangObject):
299    """
300    The TranslationUnit class represents a source code translation unit and
301    provides read-only access to its top-level declarations.
302    """
303    def __init__(self, obj, free=False):
304        ClangObject.__init__(self, obj)
305        self.free = free
306
307    def __del__(self):
308        if self.free and self.obj:
309            TranslationUnit_dispose(self)
310
311    @property
312    def cursor(self):
313        """Retrieve the cursor that represents the given translation unit."""
314        return TranslationUnit_cursor(self)
315
316    @property
317    def spelling(self):
318        """Get the original translation unit source file name."""
319        return TranslationUnit_spelling(self)
320
321    @staticmethod
322    def read(ix, path):
323        """Create a translation unit from the given AST file."""
324        ptr = TranslationUnit_read(ix, path)
325        return TranslationUnit(ptr, True) if ptr else None
326
327    @staticmethod
328    def parse(ix, path, args = []):
329        """
330        Construct a translation unit from the given source file, applying
331        the given command line argument.
332        """
333        # TODO: Support unsaved files.
334        argc, argv = len(args), create_string_vector(args)
335        ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0)
336        return TranslationUnit(ptr, True) if ptr else None
337
338class File(ClangObject):
339    """
340    The File class represents a particular source file that is part of a
341    translation unit.
342    """
343
344    @property
345    def name(self):
346        """Return the complete file and path name of the file, if valid."""
347        return File_name(self)
348
349    @property
350    def time(self):
351        """Return the last modification time of the file, if valid."""
352        return File_time(self)
353
354class Declaration(ClangObject):
355    """
356    The Declaration class represents a declaration with a translation unit.
357    """
358    def __init__(self, obj):
359        ClangObject.__init__(self, obj)
360
361        # Figure out the kind of cursor and inject a base class that provides
362        # some declaration-specific functionality.
363        self.cursor = Declaration_cursor(self)
364
365    @property
366    def kind(self):
367        """Retur the kind of cursor."""
368        return self.cursor.kind
369
370    @property
371    def entity(self):
372        """Return an entity that represents this declaration."""
373        return Entity(Declaration_entity(self))
374
375    @property
376    def spelling(self):
377        """Return the spelling (name) of the declaration."""
378        return Declaration_spelling(self)
379
380    def load(self, fun, data = None):
381        """
382        Recursively visit any elements declared or referenced within this
383        declaration.
384        """
385        f = lambda d, c, x: fun(Declaration(d), c, x)
386        Declaration_load(self, Callback(f), data)
387
388# Specific declaration kinds
389class ClassDeclaration:
390    pass
391
392class FunctionDeclaration:
393    pass
394
395class TypedefDeclaration:
396    pass
397
398# Additional Functions and Types
399
400# Wrap calls to TranslationUnit._load and Decl._load.
401Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p)
402
403# String Functions
404String_dispose = lib.clang_disposeString
405String_dispose.argtypes = [String]
406
407# Source Location Functions
408SourceLocation_loc = lib.clang_getInstantiationLocation
409SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p,
410                               c_uint_p]
411
412# Source Range Functions
413SourceRange_start = lib.clang_getRangeStart
414SourceRange_start.argtypes = [SourceRange]
415SourceRange_start.restype = SourceLocation
416
417SourceRange_end = lib.clang_getRangeEnd
418SourceRange_end.argtypes = [SourceRange]
419SourceRange_end.restype = SourceLocation
420
421# Cursor Functions
422# TODO: Implement this function
423Cursor_get = lib.clang_getCursor
424Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint]
425Cursor.restype = Cursor
426
427Cursor_null = lib.clang_getNullCursor
428Cursor_null.restype = Cursor
429
430Cursor_kind = lib.clang_getCursorKind
431Cursor_kind.argtypes = [Cursor]
432Cursor_kind.res = c_int
433
434# FIXME: Not really sure what a USR is or what this function actually does...
435Cursor_usr = lib.clang_getCursorUSR
436
437Cursor_is_decl = lib.clang_isDeclaration
438Cursor_is_decl.argtypes = [CursorKind]
439Cursor_is_decl.restype = c_bool
440
441Cursor_is_ref = lib.clang_isReference
442Cursor_is_ref.argtypes = [CursorKind]
443Cursor_is_ref.restype = c_bool
444
445Cursor_is_expr = lib.clang_isExpression
446Cursor_is_expr.argtypes = [CursorKind]
447Cursor_is_expr.restype = c_bool
448
449Cursor_is_stmt = lib.clang_isStatement
450Cursor_is_stmt.argtypes = [CursorKind]
451Cursor_is_stmt.restype = c_bool
452
453Cursor_is_inv = lib.clang_isInvalid
454Cursor_is_inv.argtypes = [CursorKind]
455Cursor_is_inv.restype = c_bool
456
457Cursor_is_tu = lib.clang_isTranslationUnit
458Cursor_is_tu.argtypes = [CursorKind]
459Cursor_is_tu.restype = c_bool
460
461Cursor_is_def = lib.clang_isCursorDefinition
462Cursor_is_def.argtypes = [Cursor]
463Cursor_is_def.restype = c_bool
464
465Cursor_def = lib.clang_getCursorDefinition
466Cursor_def.argtypes = [Cursor]
467Cursor_def.restype = Cursor
468
469Cursor_eq = lib.clang_equalCursors
470Cursor_eq.argtypes = [Cursor, Cursor]
471Cursor_eq.restype = c_uint
472
473Cursor_spelling = lib.clang_getCursorSpelling
474Cursor_spelling.argtypes = [Cursor]
475Cursor_spelling.restype = String
476
477Cursor_loc = lib.clang_getCursorLocation
478Cursor_loc.argtypes = [Cursor]
479Cursor_loc.restype = SourceLocation
480
481Cursor_extent = lib.clang_getCursorExtent
482Cursor_extent.argtypes = [Cursor]
483Cursor_extent.restype = SourceRange
484
485Cursor_ref = lib.clang_getCursorReferenced
486Cursor_ref.argtypes = [Cursor]
487Cursor_ref.restype = Cursor
488
489# Index Functions
490Index_create = lib.clang_createIndex
491Index_create.argtypes = [c_int, c_int]
492Index_create.restype = c_object_p
493
494Index_dispose = lib.clang_disposeIndex
495Index_dispose.argtypes = [Index]
496
497# Translation Unit Functions
498TranslationUnit_read = lib.clang_createTranslationUnit
499TranslationUnit_read.argtypes = [Index, c_char_p]
500TranslationUnit_read.restype = c_object_p
501
502TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile
503TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p,
504                                  c_int, c_void_p]
505TranslationUnit_parse.restype = c_object_p
506
507TranslationUnit_cursor = lib.clang_getTranslationUnitCursor
508TranslationUnit_cursor.argtypes = [TranslationUnit]
509TranslationUnit_cursor.restype = Cursor
510
511TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
512TranslationUnit_spelling.argtypes = [TranslationUnit]
513TranslationUnit_spelling.restype = String
514
515TranslationUnit_dispose = lib.clang_disposeTranslationUnit
516TranslationUnit_dispose.argtypes = [TranslationUnit]
517
518# File Functions
519File_name = lib.clang_getFileName
520File_name.argtypes = [File]
521File_name.restype = c_char_p
522
523File_time = lib.clang_getFileTime
524File_time.argtypes = [File]
525File_time.restype = c_uint
526