cindex.py revision 149f38abf5e122941090bfb9d1d78dde0859024a
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_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## 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 """ 258 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 """ 290 291 def __del__(self): 292 TranslationUnit_dispose(self) 293 294 @property 295 def cursor(self): 296 """Retrieve the cursor that represents the given translation unit.""" 297 return TranslationUnit_cursor(self) 298 299 @property 300 def spelling(self): 301 """Get the original translation unit source file name.""" 302 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) 308 return TranslationUnit(ptr) if ptr else None 309 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) 319 return TranslationUnit(ptr) if ptr else None 320 321class File(ClangObject): 322 """ 323 The File class represents a particular source file that is part of a 324 translation unit. 325 """ 326 327 @property 328 def name(self): 329 """Return the complete file and path name of the file, if valid.""" 330 return File_name(self) 331 332 @property 333 def time(self): 334 """Return the last modification time of the file, if valid.""" 335 return File_time(self) 336 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 354 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 387SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p, 388 c_uint_p] 389 390# Source Range Functions 391SourceRange_start = lib.clang_getRangeStart 392SourceRange_start.argtypes = [SourceRange] 393SourceRange_start.restype = SourceLocation 394 395SourceRange_end = lib.clang_getRangeEnd 396SourceRange_end.argtypes = [SourceRange] 397SourceRange_end.restype = SourceLocation 398 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 485TranslationUnit_cursor = lib.clang_getTranslationUnitCursor 486TranslationUnit_cursor.argtypes = [TranslationUnit] 487TranslationUnit_cursor.restype = Cursor 488 489TranslationUnit_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 504