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