blob: 2c7a04e3d946619ba29c39ea7a3a4050fbcb95fc [file] [log] [blame]
Daniel Dunbar30c0f262010-01-24 02:02:07 +00001# -*- 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 """
Daniel Dunbar149f38a2010-01-24 04:09:58 +000089 A SourceLocation represents a particular location within a source file.
Daniel Dunbar30c0f262010-01-24 02:02:07 +000090 """
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 """
Daniel Dunbar7b48b352010-01-24 04:09:34 +000097 f, l, c = c_object_p(), c_uint(), c_uint()
Daniel Dunbar30c0f262010-01-24 02:02:07 +000098 SourceLocation_loc(self, byref(f), byref(l), byref(c))
Daniel Dunbar7b48b352010-01-24 04:09:34 +000099 f = File(f) if f else None
100 self.file, self.line, self.column = f, int(l.value), int(c.value)
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000101 return self
102
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000103 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
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000107class 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
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000117 @property
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000118 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
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000125 @property
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000126 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
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000151 def is_declaration(self):
152 """Return True if the cursor points to a declaration."""
153 return Cursor_is_decl(self.kind)
154
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000155 def is_reference(self):
Daniel Dunbar149f38a2010-01-24 04:09:58 +0000156 """Return True if the cursor points to a reference."""
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000157 return Cursor_is_ref(self.kind)
158
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000159 def is_expression(self):
160 """Return True if the cursor points to an expression."""
161 return Cursor_is_expr(self.kind)
162
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000163 def is_statement(self):
164 """Return True if the cursor points to a statement."""
165 return Cursor_is_stmt(self.kind)
166
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000167 def is_translation_unit(self):
168 """Return True if the cursor points to a translation unit."""
169 return Cursor_is_tu(self.kind)
170
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000171 def is_invalid(self):
172 """Return True if the cursor points to an invalid entity."""
173 return Cursor_is_inv(self.kind)
174
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000175 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 """
Daniel Dunbaraa229842010-01-24 04:09:23 +0000188 if not self.is_declaration():
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000189 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."""
Daniel Dunbaraa229842010-01-24 04:09:23 +0000205 if not self.is_declaration():
206 # FIXME: This should be documented in Index.h
207 raise ValueError("Cursor does not refer to a Declaration")
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000208 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
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000234## CIndex Objects ##
235
236# CIndex objects (derived from ClangObject) are essentially lightweight
237# wrappers attached to some underlying object, which is exposed via CIndex as
238# a void*.
239
240class ClangObject(object):
241 """
242 A helper for Clang objects. This class helps act as an intermediary for
243 the ctypes library and the Clang CIndex library.
244 """
245 def __init__(self, obj):
246 assert isinstance(obj, c_object_p) and obj
247 self.obj = self._as_parameter_ = obj
248
249 def from_param(self):
250 return self._as_parameter_
251
252class Index(ClangObject):
253 """
254 The Index type provides the primary interface to the Clang CIndex library,
255 primarily by providing an interface for reading and parsing translation
256 units.
257 """
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000258
259 @staticmethod
260 def create(excludeDecls=False, displayDiags=False):
261 """
262 Create a new Index.
263 Parameters:
264 excludeDecls -- Exclude local declarations from translation units.
265 displayDiags -- Display diagnostics during translation unit creation.
266 """
267 return Index(Index_create(excludeDecls, displayDiags))
268
269 def __del__(self):
270 Index_dispose(self)
271
272 def read(self, path):
273 """Load the translation unit from the given AST file."""
274 return TranslationUnit.read(self, path)
275
276 def parse(self, path, args = []):
277 """
278 Load the translation unit from the given source code file by running
279 clang and generating the AST before loading. Additional command line
280 parameters can be passed to clang via the args parameter.
281 """
282 return TranslationUnit.parse(self, path, args)
283
284
285class TranslationUnit(ClangObject):
286 """
287 The TranslationUnit class represents a source code translation unit and
288 provides read-only access to its top-level declarations.
289 """
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000290
291 def __del__(self):
Daniel Dunbar99d593e2010-01-24 04:09:51 +0000292 TranslationUnit_dispose(self)
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000293
Daniel Dunbar1b945a72010-01-24 04:09:43 +0000294 @property
295 def cursor(self):
296 """Retrieve the cursor that represents the given translation unit."""
297 return TranslationUnit_cursor(self)
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000298
299 @property
300 def spelling(self):
Daniel Dunbar1b945a72010-01-24 04:09:43 +0000301 """Get the original translation unit source file name."""
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000302 return TranslationUnit_spelling(self)
303
304 @staticmethod
305 def read(ix, path):
306 """Create a translation unit from the given AST file."""
307 ptr = TranslationUnit_read(ix, path)
Daniel Dunbar99d593e2010-01-24 04:09:51 +0000308 return TranslationUnit(ptr) if ptr else None
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000309
310 @staticmethod
311 def parse(ix, path, args = []):
312 """
313 Construct a translation unit from the given source file, applying
314 the given command line argument.
315 """
316 # TODO: Support unsaved files.
317 argc, argv = len(args), create_string_vector(args)
318 ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0)
Daniel Dunbar99d593e2010-01-24 04:09:51 +0000319 return TranslationUnit(ptr) if ptr else None
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000320
321class File(ClangObject):
322 """
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000323 The File class represents a particular source file that is part of a
324 translation unit.
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000325 """
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000326
327 @property
328 def name(self):
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000329 """Return the complete file and path name of the file, if valid."""
330 return File_name(self)
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000331
332 @property
333 def time(self):
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000334 """Return the last modification time of the file, if valid."""
335 return File_time(self)
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000336
337class Declaration(ClangObject):
338 """
339 The Declaration class represents a declaration with a translation unit.
340 """
341 def __init__(self, obj):
342 ClangObject.__init__(self, obj)
343
344 # Figure out the kind of cursor and inject a base class that provides
345 # some declaration-specific functionality.
346 self.cursor = Declaration_cursor(self)
347
348 @property
349 def kind(self):
350 """Retur the kind of cursor."""
351 return self.cursor.kind
352
353 @property
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000354 def spelling(self):
355 """Return the spelling (name) of the declaration."""
356 return Declaration_spelling(self)
357
358 def load(self, fun, data = None):
359 """
360 Recursively visit any elements declared or referenced within this
361 declaration.
362 """
363 f = lambda d, c, x: fun(Declaration(d), c, x)
364 Declaration_load(self, Callback(f), data)
365
366# Specific declaration kinds
367class ClassDeclaration:
368 pass
369
370class FunctionDeclaration:
371 pass
372
373class TypedefDeclaration:
374 pass
375
376# Additional Functions and Types
377
378# Wrap calls to TranslationUnit._load and Decl._load.
379Callback = CFUNCTYPE(None, c_void_p, Cursor, c_void_p)
380
381# String Functions
382String_dispose = lib.clang_disposeString
383String_dispose.argtypes = [String]
384
385# Source Location Functions
386SourceLocation_loc = lib.clang_getInstantiationLocation
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000387SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p,
388 c_uint_p]
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000389
390# Source Range Functions
391SourceRange_start = lib.clang_getRangeStart
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000392SourceRange_start.argtypes = [SourceRange]
393SourceRange_start.restype = SourceLocation
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000394
395SourceRange_end = lib.clang_getRangeEnd
Daniel Dunbar7b48b352010-01-24 04:09:34 +0000396SourceRange_end.argtypes = [SourceRange]
397SourceRange_end.restype = SourceLocation
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000398
399# Cursor Functions
400# TODO: Implement this function
401Cursor_get = lib.clang_getCursor
402Cursor_get.argtypes = [TranslationUnit, c_char_p, c_uint, c_uint]
403Cursor.restype = Cursor
404
405Cursor_null = lib.clang_getNullCursor
406Cursor_null.restype = Cursor
407
408Cursor_kind = lib.clang_getCursorKind
409Cursor_kind.argtypes = [Cursor]
410Cursor_kind.res = c_int
411
412# FIXME: Not really sure what a USR is or what this function actually does...
413Cursor_usr = lib.clang_getCursorUSR
414
415Cursor_is_decl = lib.clang_isDeclaration
416Cursor_is_decl.argtypes = [CursorKind]
417Cursor_is_decl.restype = c_bool
418
419Cursor_is_ref = lib.clang_isReference
420Cursor_is_ref.argtypes = [CursorKind]
421Cursor_is_ref.restype = c_bool
422
423Cursor_is_expr = lib.clang_isExpression
424Cursor_is_expr.argtypes = [CursorKind]
425Cursor_is_expr.restype = c_bool
426
427Cursor_is_stmt = lib.clang_isStatement
428Cursor_is_stmt.argtypes = [CursorKind]
429Cursor_is_stmt.restype = c_bool
430
431Cursor_is_inv = lib.clang_isInvalid
432Cursor_is_inv.argtypes = [CursorKind]
433Cursor_is_inv.restype = c_bool
434
435Cursor_is_tu = lib.clang_isTranslationUnit
436Cursor_is_tu.argtypes = [CursorKind]
437Cursor_is_tu.restype = c_bool
438
439Cursor_is_def = lib.clang_isCursorDefinition
440Cursor_is_def.argtypes = [Cursor]
441Cursor_is_def.restype = c_bool
442
443Cursor_def = lib.clang_getCursorDefinition
444Cursor_def.argtypes = [Cursor]
445Cursor_def.restype = Cursor
446
447Cursor_eq = lib.clang_equalCursors
448Cursor_eq.argtypes = [Cursor, Cursor]
449Cursor_eq.restype = c_uint
450
451Cursor_spelling = lib.clang_getCursorSpelling
452Cursor_spelling.argtypes = [Cursor]
453Cursor_spelling.restype = String
454
455Cursor_loc = lib.clang_getCursorLocation
456Cursor_loc.argtypes = [Cursor]
457Cursor_loc.restype = SourceLocation
458
459Cursor_extent = lib.clang_getCursorExtent
460Cursor_extent.argtypes = [Cursor]
461Cursor_extent.restype = SourceRange
462
463Cursor_ref = lib.clang_getCursorReferenced
464Cursor_ref.argtypes = [Cursor]
465Cursor_ref.restype = Cursor
466
467# Index Functions
468Index_create = lib.clang_createIndex
469Index_create.argtypes = [c_int, c_int]
470Index_create.restype = c_object_p
471
472Index_dispose = lib.clang_disposeIndex
473Index_dispose.argtypes = [Index]
474
475# Translation Unit Functions
476TranslationUnit_read = lib.clang_createTranslationUnit
477TranslationUnit_read.argtypes = [Index, c_char_p]
478TranslationUnit_read.restype = c_object_p
479
480TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile
481TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p,
482 c_int, c_void_p]
483TranslationUnit_parse.restype = c_object_p
484
Daniel Dunbar1b945a72010-01-24 04:09:43 +0000485TranslationUnit_cursor = lib.clang_getTranslationUnitCursor
486TranslationUnit_cursor.argtypes = [TranslationUnit]
487TranslationUnit_cursor.restype = Cursor
488
Daniel Dunbar30c0f262010-01-24 02:02:07 +0000489TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling
490TranslationUnit_spelling.argtypes = [TranslationUnit]
491TranslationUnit_spelling.restype = String
492
493TranslationUnit_dispose = lib.clang_disposeTranslationUnit
494TranslationUnit_dispose.argtypes = [TranslationUnit]
495
496# File Functions
497File_name = lib.clang_getFileName
498File_name.argtypes = [File]
499File_name.restype = c_char_p
500
501File_time = lib.clang_getFileTime
502File_time.argtypes = [File]
503File_time.restype = c_uint