cindex.py revision f869083cbfea538d6b7baf4ece30066b11984e12
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# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper
52# object. This is a problem, because it means that from_parameter will see an
53# integer and pass the wrong value on platforms where int != void*. Work around
54# this by marshalling object arguments as void**.
55c_object_p = POINTER(c_void_p)
56
57lib = get_cindex_library()
58
59## Typedefs ##
60CursorKind = c_int
61
62### Structures and Utility Classes ###
63
64class _CXString(Structure):
65    """Helper for transforming CXString results."""
66
67    _fields_ = [("spelling", c_char_p), ("free", c_int)]
68
69    def __del__(self):
70        _CXString_dispose(self)
71
72    @staticmethod
73    def from_result(res, fn, args):
74        assert isinstance(res, _CXString)
75        return _CXString_getCString(res)
76
77class SourceLocation(Structure):
78    """
79    A SourceLocation represents a particular location within a source file.
80    """
81    _fields_ = [("ptr_data", c_void_p), ("int_data", c_uint)]
82    _data = None
83
84    def _get_instantiation(self):
85        if self._data is None:
86            f, l, c = c_object_p(), c_uint(), c_uint()
87            SourceLocation_loc(self, byref(f), byref(l), byref(c))
88            f = File(f) if f else None
89            self._data = (f, int(l.value), int(c.value))
90        return self._data
91
92    @property
93    def file(self):
94        """Get the file represented by this source location."""
95        return self._get_instantiation()[0]
96
97    @property
98    def line(self):
99        """Get the line represented by this source location."""
100        return self._get_instantiation()[1]
101
102    @property
103    def column(self):
104        """Get the column represented by this source location."""
105        return self._get_instantiation()[2]
106
107    def __repr__(self):
108        return "<SourceLocation file %r, line %r, column %r>" % (
109            self.file.name if self.file else None, self.line, self.column)
110
111class SourceRange(Structure):
112    """
113    A SourceRange describes a range of source locations within the source
114    code.
115    """
116    _fields_ = [
117        ("ptr_data", c_void_p),
118        ("begin_int_data", c_uint),
119        ("end_int_data", c_uint)]
120
121    @property
122    def start(self):
123        """
124        Return a SourceLocation representing the first character within a
125        source range.
126        """
127        return SourceRange_start(self)
128
129    @property
130    def end(self):
131        """
132        Return a SourceLocation representing the last character within a
133        source range.
134        """
135        return SourceRange_end(self)
136
137    def __repr__(self):
138        return "<SourceRange start %r, end %r>" % (self.start, self.end)
139
140class Cursor(Structure):
141    """
142    The Cursor class represents a reference to an element within the AST. It
143    acts as a kind of iterator.
144    """
145    _fields_ = [("kind", c_int), ("data", c_void_p * 3)]
146
147    def __eq__(self, other):
148        return Cursor_eq(self, other)
149
150    def __ne__(self, other):
151        return not Cursor_eq(self, other)
152
153    def is_declaration(self):
154        """Return True if the cursor points to a declaration."""
155        return Cursor_is_decl(self.kind)
156
157    def is_reference(self):
158        """Return True if the cursor points to a reference."""
159        return Cursor_is_ref(self.kind)
160
161    def is_expression(self):
162        """Return True if the cursor points to an expression."""
163        return Cursor_is_expr(self.kind)
164
165    def is_statement(self):
166        """Return True if the cursor points to a statement."""
167        return Cursor_is_stmt(self.kind)
168
169    def is_translation_unit(self):
170        """Return True if the cursor points to a translation unit."""
171        return Cursor_is_tu(self.kind)
172
173    def is_invalid(self):
174        """Return  True if the cursor points to an invalid entity."""
175        return Cursor_is_inv(self.kind)
176
177    def is_definition(self):
178        """
179        Returns true if the declaration pointed at by the cursor is also a
180        definition of that entity.
181        """
182        return Cursor_is_def(self)
183
184    def get_definition(self):
185        """
186        If the cursor is a reference to a declaration or a declaration of
187        some entity, return a cursor that points to the definition of that
188        entity.
189        """
190        # TODO: Should probably check that this is either a reference or
191        # declaration prior to issuing the lookup.
192        return Cursor_def(self)
193
194    def get_usr(self):
195        """Return the Unified Symbol Resultion (USR) for the entity referenced
196        by the given cursor (or None).
197
198        A Unified Symbol Resolution (USR) is a string that identifies a
199        particular entity (function, class, variable, etc.) within a
200        program. USRs can be compared across translation units to determine,
201        e.g., when references in one translation refer to an entity defined in
202        another translation unit."""
203        return Cursor_usr(self)
204
205    @property
206    def spelling(self):
207        """Return the spelling of the entity pointed at by the cursor."""
208        if not self.is_declaration():
209            # FIXME: clang_getCursorSpelling should be fixed to not assert on
210            # this, for consistency with clang_getCursorUSR.
211            return None
212        return Cursor_spelling(self)
213
214    @property
215    def location(self):
216        """
217        Return the source location (the starting character) of the entity
218        pointed at by the cursor.
219        """
220        return Cursor_loc(self)
221
222    @property
223    def extent(self):
224        """
225        Return the source range (the range of text) occupied by the entity
226        pointed at by the cursor.
227        """
228        return Cursor_extent(self)
229
230    def get_children(self):
231        """Return an iterator for the accessing the children of this cursor."""
232
233        # FIXME: Expose iteration from CIndex, PR6125.
234        def visitor(child, parent, children):
235            # FIXME: Document this assertion in API.
236            # FIXME: There should just be an isNull method.
237            assert child != Cursor_null()
238            children.append(child)
239            return 1 # continue
240        children = []
241        Cursor_visit(self, Callback(visitor), children)
242        return iter(children)
243
244    @staticmethod
245    def from_result(res, fn, args):
246        assert isinstance(res, Cursor)
247        # FIXME: There should just be an isNull method.
248        if res == Cursor_null():
249            return None
250        return res
251
252## CIndex Objects ##
253
254# CIndex objects (derived from ClangObject) are essentially lightweight
255# wrappers attached to some underlying object, which is exposed via CIndex as
256# a void*.
257
258class ClangObject(object):
259    """
260    A helper for Clang objects. This class helps act as an intermediary for
261    the ctypes library and the Clang CIndex library.
262    """
263    def __init__(self, obj):
264        assert isinstance(obj, c_object_p) and obj
265        self.obj = self._as_parameter_ = obj
266
267    def from_param(self):
268        return self._as_parameter_
269
270class Index(ClangObject):
271    """
272    The Index type provides the primary interface to the Clang CIndex library,
273    primarily by providing an interface for reading and parsing translation
274    units.
275    """
276
277    @staticmethod
278    def create(excludeDecls=False, displayDiags=False):
279        """
280        Create a new Index.
281        Parameters:
282        excludeDecls -- Exclude local declarations from translation units.
283        displayDiags -- Display diagnostics during translation unit creation.
284        """
285        return Index(Index_create(excludeDecls, displayDiags))
286
287    def __del__(self):
288        Index_dispose(self)
289
290    def read(self, path):
291        """Load the translation unit from the given AST file."""
292        return TranslationUnit.read(self, path)
293
294    def parse(self, path, args = []):
295        """
296        Load the translation unit from the given source code file by running
297        clang and generating the AST before loading. Additional command line
298        parameters can be passed to clang via the args parameter.
299        """
300        return TranslationUnit.parse(self, path, args)
301
302
303class TranslationUnit(ClangObject):
304    """
305    The TranslationUnit class represents a source code translation unit and
306    provides read-only access to its top-level declarations.
307    """
308
309    def __del__(self):
310        TranslationUnit_dispose(self)
311
312    @property
313    def cursor(self):
314        """Retrieve the cursor that represents the given translation unit."""
315        return TranslationUnit_cursor(self)
316
317    @property
318    def spelling(self):
319        """Get the original translation unit source file name."""
320        return TranslationUnit_spelling(self)
321
322    @staticmethod
323    def read(ix, path):
324        """Create a translation unit from the given AST file."""
325        ptr = TranslationUnit_read(ix, path)
326        return TranslationUnit(ptr) if ptr else None
327
328    @staticmethod
329    def parse(ix, path, args = []):
330        """
331        Construct a translation unit from the given source file, applying
332        the given command line argument.
333        """
334        # TODO: Support unsaved files.
335        argc, argv = len(args), create_string_vector(args)
336        ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0)
337        return TranslationUnit(ptr) if ptr else None
338
339class File(ClangObject):
340    """
341    The File class represents a particular source file that is part of a
342    translation unit.
343    """
344
345    @property
346    def name(self):
347        """Return the complete file and path name of the file, if valid."""
348        return File_name(self)
349
350    @property
351    def time(self):
352        """Return the last modification time of the file, if valid."""
353        return File_time(self)
354
355# Additional Functions and Types
356
357# Wrap calls to TranslationUnit._load and Decl._load.
358Callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object)
359
360# String Functions
361_CXString_dispose = lib.clang_disposeString
362_CXString_dispose.argtypes = [_CXString]
363
364_CXString_getCString = lib.clang_getCString
365_CXString_getCString.argtypes = [_CXString]
366_CXString_getCString.restype = c_char_p
367
368# Source Location Functions
369SourceLocation_loc = lib.clang_getInstantiationLocation
370SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p),
371                               POINTER(c_uint), POINTER(c_uint)]
372
373# Source Range Functions
374SourceRange_start = lib.clang_getRangeStart
375SourceRange_start.argtypes = [SourceRange]
376SourceRange_start.restype = SourceLocation
377
378SourceRange_end = lib.clang_getRangeEnd
379SourceRange_end.argtypes = [SourceRange]
380SourceRange_end.restype = SourceLocation
381
382# Cursor Functions
383# TODO: Implement this function
384Cursor_get = lib.clang_getCursor
385Cursor_get.argtypes = [TranslationUnit, SourceLocation]
386Cursor_get.restype = Cursor
387
388Cursor_null = lib.clang_getNullCursor
389Cursor_null.restype = Cursor
390
391Cursor_kind = lib.clang_getCursorKind
392Cursor_kind.argtypes = [Cursor]
393Cursor_kind.restype = c_int
394
395Cursor_usr = lib.clang_getCursorUSR
396Cursor_usr.argtypes = [Cursor]
397Cursor_usr.restype = _CXString
398Cursor_usr.errcheck = _CXString.from_result
399
400Cursor_is_decl = lib.clang_isDeclaration
401Cursor_is_decl.argtypes = [CursorKind]
402Cursor_is_decl.restype = bool
403
404Cursor_is_ref = lib.clang_isReference
405Cursor_is_ref.argtypes = [CursorKind]
406Cursor_is_ref.restype = bool
407
408Cursor_is_expr = lib.clang_isExpression
409Cursor_is_expr.argtypes = [CursorKind]
410Cursor_is_expr.restype = bool
411
412Cursor_is_stmt = lib.clang_isStatement
413Cursor_is_stmt.argtypes = [CursorKind]
414Cursor_is_stmt.restype = bool
415
416Cursor_is_inv = lib.clang_isInvalid
417Cursor_is_inv.argtypes = [CursorKind]
418Cursor_is_inv.restype = bool
419
420Cursor_is_tu = lib.clang_isTranslationUnit
421Cursor_is_tu.argtypes = [CursorKind]
422Cursor_is_tu.restype = bool
423
424Cursor_is_def = lib.clang_isCursorDefinition
425Cursor_is_def.argtypes = [Cursor]
426Cursor_is_def.restype = bool
427
428Cursor_def = lib.clang_getCursorDefinition
429Cursor_def.argtypes = [Cursor]
430Cursor_def.restype = Cursor
431Cursor_def.errcheck = Cursor.from_result
432
433Cursor_eq = lib.clang_equalCursors
434Cursor_eq.argtypes = [Cursor, Cursor]
435Cursor_eq.restype = c_uint
436
437Cursor_spelling = lib.clang_getCursorSpelling
438Cursor_spelling.argtypes = [Cursor]
439Cursor_spelling.restype = _CXString
440Cursor_spelling.errcheck = _CXString.from_result
441
442Cursor_loc = lib.clang_getCursorLocation
443Cursor_loc.argtypes = [Cursor]
444Cursor_loc.restype = SourceLocation
445
446Cursor_extent = lib.clang_getCursorExtent
447Cursor_extent.argtypes = [Cursor]
448Cursor_extent.restype = SourceRange
449
450Cursor_ref = lib.clang_getCursorReferenced
451Cursor_ref.argtypes = [Cursor]
452Cursor_ref.restype = Cursor
453Cursor_ref.errcheck = Cursor.from_result
454
455Cursor_visit = lib.clang_visitChildren
456Cursor_visit.argtypes = [Cursor, Callback, py_object]
457Cursor_visit.restype = c_uint
458
459# Index Functions
460Index_create = lib.clang_createIndex
461Index_create.argtypes = [c_int, c_int]
462Index_create.restype = c_object_p
463
464Index_dispose = lib.clang_disposeIndex
465Index_dispose.argtypes = [Index]
466
467# Translation Unit Functions
468TranslationUnit_read = lib.clang_createTranslationUnit
469TranslationUnit_read.argtypes = [Index, c_char_p]
470TranslationUnit_read.restype = c_object_p
471
472TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile
473TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p,
474                                  c_int, c_void_p]
475TranslationUnit_parse.restype = c_object_p
476
477TranslationUnit_cursor = lib.clang_getTranslationUnitCursor
478TranslationUnit_cursor.argtypes = [TranslationUnit]
479TranslationUnit_cursor.restype = Cursor
480TranslationUnit_cursor.errcheck = Cursor.from_result
481
482TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
483TranslationUnit_spelling.argtypes = [TranslationUnit]
484TranslationUnit_spelling.restype = _CXString
485TranslationUnit_spelling.errcheck = _CXString.from_result
486
487TranslationUnit_dispose = lib.clang_disposeTranslationUnit
488TranslationUnit_dispose.argtypes = [TranslationUnit]
489
490# File Functions
491File_name = lib.clang_getFileName
492File_name.argtypes = [File]
493File_name.restype = c_char_p
494
495File_time = lib.clang_getFileTime
496File_time.argtypes = [File]
497File_time.restype = c_uint
498