cindex.py revision 0263a1e517a5de518114d54d44298199ed962c4e
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 reference."""
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_definition(self):
183        """
184        If the cursor is a reference to a declaration or a declaration of
185        some entity, return a cursor that points to the definition of that
186        entity.
187        """
188        # TODO: Should probably check that this is either a reference or
189        # declaration prior to issuing the lookup.
190        return Cursor_def(self)
191
192    @property
193    def spelling(self):
194        """Return the spelling of the entity pointed at by the cursor."""
195        if not self.is_declaration():
196            # FIXME: This should be documented in Index.h
197            raise ValueError("Cursor does not refer to a Declaration")
198        return Cursor_spelling(self)
199
200    @property
201    def location(self):
202        """
203        Return the source location (the starting character) of the entity
204        pointed at by the cursor.
205        """
206        return Cursor_loc(self).init()
207
208    @property
209    def extent(self):
210        """
211        Return the source range (the range of text) occupied by the entity
212        pointed at by the cursor.
213        """
214        return Cursor_extent(self)
215
216    def get_children(self):
217        """Return an iterator for the accessing children of this cursor."""
218
219        # FIXME: Expose iteration from CIndex, PR6125.
220        def visitor(child, parent, children):
221            children.append(child)
222            return 1 # continue
223        children = []
224        Cursor_visit(self, Callback(visitor), children)
225        return iter(children)
226
227## CIndex Objects ##
228
229# CIndex objects (derived from ClangObject) are essentially lightweight
230# wrappers attached to some underlying object, which is exposed via CIndex as
231# a void*.
232
233class ClangObject(object):
234    """
235    A helper for Clang objects. This class helps act as an intermediary for
236    the ctypes library and the Clang CIndex library.
237    """
238    def __init__(self, obj):
239        assert isinstance(obj, c_object_p) and obj
240        self.obj = self._as_parameter_ = obj
241
242    def from_param(self):
243        return self._as_parameter_
244
245class Index(ClangObject):
246    """
247    The Index type provides the primary interface to the Clang CIndex library,
248    primarily by providing an interface for reading and parsing translation
249    units.
250    """
251
252    @staticmethod
253    def create(excludeDecls=False, displayDiags=False):
254        """
255        Create a new Index.
256        Parameters:
257        excludeDecls -- Exclude local declarations from translation units.
258        displayDiags -- Display diagnostics during translation unit creation.
259        """
260        return Index(Index_create(excludeDecls, displayDiags))
261
262    def __del__(self):
263        Index_dispose(self)
264
265    def read(self, path):
266        """Load the translation unit from the given AST file."""
267        return TranslationUnit.read(self, path)
268
269    def parse(self, path, args = []):
270        """
271        Load the translation unit from the given source code file by running
272        clang and generating the AST before loading. Additional command line
273        parameters can be passed to clang via the args parameter.
274        """
275        return TranslationUnit.parse(self, path, args)
276
277
278class TranslationUnit(ClangObject):
279    """
280    The TranslationUnit class represents a source code translation unit and
281    provides read-only access to its top-level declarations.
282    """
283
284    def __del__(self):
285        TranslationUnit_dispose(self)
286
287    @property
288    def cursor(self):
289        """Retrieve the cursor that represents the given translation unit."""
290        return TranslationUnit_cursor(self)
291
292    @property
293    def spelling(self):
294        """Get the original translation unit source file name."""
295        return TranslationUnit_spelling(self)
296
297    @staticmethod
298    def read(ix, path):
299        """Create a translation unit from the given AST file."""
300        ptr = TranslationUnit_read(ix, path)
301        return TranslationUnit(ptr) if ptr else None
302
303    @staticmethod
304    def parse(ix, path, args = []):
305        """
306        Construct a translation unit from the given source file, applying
307        the given command line argument.
308        """
309        # TODO: Support unsaved files.
310        argc, argv = len(args), create_string_vector(args)
311        ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0)
312        return TranslationUnit(ptr) if ptr else None
313
314class File(ClangObject):
315    """
316    The File class represents a particular source file that is part of a
317    translation unit.
318    """
319
320    @property
321    def name(self):
322        """Return the complete file and path name of the file, if valid."""
323        return File_name(self)
324
325    @property
326    def time(self):
327        """Return the last modification time of the file, if valid."""
328        return File_time(self)
329
330# Additional Functions and Types
331
332# Wrap calls to TranslationUnit._load and Decl._load.
333Callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object)
334
335# String Functions
336String_dispose = lib.clang_disposeString
337String_dispose.argtypes = [String]
338
339# Source Location Functions
340SourceLocation_loc = lib.clang_getInstantiationLocation
341SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p,
342                               c_uint_p]
343
344# Source Range Functions
345SourceRange_start = lib.clang_getRangeStart
346SourceRange_start.argtypes = [SourceRange]
347SourceRange_start.restype = SourceLocation
348
349SourceRange_end = lib.clang_getRangeEnd
350SourceRange_end.argtypes = [SourceRange]
351SourceRange_end.restype = SourceLocation
352
353# Cursor Functions
354# TODO: Implement this function
355Cursor_get = lib.clang_getCursor
356Cursor_get.argtypes = [TranslationUnit, SourceLocation]
357Cursor.restype = Cursor
358
359Cursor_null = lib.clang_getNullCursor
360Cursor_null.restype = Cursor
361
362Cursor_kind = lib.clang_getCursorKind
363Cursor_kind.argtypes = [Cursor]
364Cursor_kind.res = c_int
365
366# FIXME: Not really sure what a USR is or what this function actually does...
367Cursor_usr = lib.clang_getCursorUSR
368
369Cursor_is_decl = lib.clang_isDeclaration
370Cursor_is_decl.argtypes = [CursorKind]
371Cursor_is_decl.restype = c_bool
372
373Cursor_is_ref = lib.clang_isReference
374Cursor_is_ref.argtypes = [CursorKind]
375Cursor_is_ref.restype = c_bool
376
377Cursor_is_expr = lib.clang_isExpression
378Cursor_is_expr.argtypes = [CursorKind]
379Cursor_is_expr.restype = c_bool
380
381Cursor_is_stmt = lib.clang_isStatement
382Cursor_is_stmt.argtypes = [CursorKind]
383Cursor_is_stmt.restype = c_bool
384
385Cursor_is_inv = lib.clang_isInvalid
386Cursor_is_inv.argtypes = [CursorKind]
387Cursor_is_inv.restype = c_bool
388
389Cursor_is_tu = lib.clang_isTranslationUnit
390Cursor_is_tu.argtypes = [CursorKind]
391Cursor_is_tu.restype = c_bool
392
393Cursor_is_def = lib.clang_isCursorDefinition
394Cursor_is_def.argtypes = [Cursor]
395Cursor_is_def.restype = c_bool
396
397Cursor_def = lib.clang_getCursorDefinition
398Cursor_def.argtypes = [Cursor]
399Cursor_def.restype = Cursor
400
401Cursor_eq = lib.clang_equalCursors
402Cursor_eq.argtypes = [Cursor, Cursor]
403Cursor_eq.restype = c_uint
404
405Cursor_spelling = lib.clang_getCursorSpelling
406Cursor_spelling.argtypes = [Cursor]
407Cursor_spelling.restype = String
408
409Cursor_loc = lib.clang_getCursorLocation
410Cursor_loc.argtypes = [Cursor]
411Cursor_loc.restype = SourceLocation
412
413Cursor_extent = lib.clang_getCursorExtent
414Cursor_extent.argtypes = [Cursor]
415Cursor_extent.restype = SourceRange
416
417Cursor_ref = lib.clang_getCursorReferenced
418Cursor_ref.argtypes = [Cursor]
419Cursor_ref.restype = Cursor
420
421Cursor_visit = lib.clang_visitChildren
422Cursor_visit.argtypes = [Cursor, Callback, py_object]
423Cursor_visit.restype = c_uint
424
425# Index Functions
426Index_create = lib.clang_createIndex
427Index_create.argtypes = [c_int, c_int]
428Index_create.restype = c_object_p
429
430Index_dispose = lib.clang_disposeIndex
431Index_dispose.argtypes = [Index]
432
433# Translation Unit Functions
434TranslationUnit_read = lib.clang_createTranslationUnit
435TranslationUnit_read.argtypes = [Index, c_char_p]
436TranslationUnit_read.restype = c_object_p
437
438TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile
439TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p,
440                                  c_int, c_void_p]
441TranslationUnit_parse.restype = c_object_p
442
443TranslationUnit_cursor = lib.clang_getTranslationUnitCursor
444TranslationUnit_cursor.argtypes = [TranslationUnit]
445TranslationUnit_cursor.restype = Cursor
446
447TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
448TranslationUnit_spelling.argtypes = [TranslationUnit]
449TranslationUnit_spelling.restype = String
450
451TranslationUnit_dispose = lib.clang_disposeTranslationUnit
452TranslationUnit_dispose.argtypes = [TranslationUnit]
453
454# File Functions
455File_name = lib.clang_getFileName
456File_name.argtypes = [File]
457File_name.restype = c_char_p
458
459File_time = lib.clang_getFileTime
460File_time.argtypes = [File]
461File_time.restype = c_uint
462